I'm not keen on giving my schema exactly, so I'm going to paste it and edit some names to obfuscate my project a little- it may introduce typos that aren't really part of my "bug". FYI/YMMV/FWIW
Much of this was through hard earned documentation scrubbing and googling/duckducking (gooseing? "duck duck go(ose)?".. I may have the auth stuff totally wrong- but I was trying to get it close enough.. I originally had the cognito stuff attached to a different model..
I think this is the gist of what I was trying to do in terms of links of things. Some of the original intent is purposefully lost, and it may not make sense why I thought I needed a particular type.. but nevertheless, here's what's left. I did remove a couple types and associated connections, hopefully leaving everything syntactically correct(to where I had it).. but I haven't specifically tried this particular config. Forgive some casing issues introduced by search/replace to change concept names.
schema {
query: QueryRoot
}
type Badge
(fields: ["id"])
(name: "badgesByCreatorID", fields: ["creatorID", "id"])
(name: "badgesByTitle", fields: ["title", "id"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
title: String!
description: String!
enabled: Boolean!
visible: Boolean!
creatorID: ID!
creator: Entity! (fields: ["creatorID"])
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
type EarnedBadges
(fields: ["id"])
(name: "earnedBadgesByBadgeID", fields: ["badgeID"])
(name: "earnedBadgesByEntityID", fields: ["entityID"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
badgeID: ID!
badge: Badge! (fields: ["badgeID"])
entityID: ID!
entity: Entity! (fields: ["entityID"])
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
enum EmailType {
WORK
HOME
PERSONAL
BUSINESS
}
enum PrimaryType {
PRIMARY
OTHER
}
type AssociatedEntityEmailAddress
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
emailID: ID!
emailAddress: EmailAddress! (fields: ["emailID"])
entityID: ID!
entity: Entity! (fields: ["entityID"])
# timestamps
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
type EmailAddress
(fields: ["id"])
(name: "emailsByEmail", fields: ["email", "id"])
(name: "emailsByEntityID", fields: ["entityID", "id"])
(name: "primaryEmailsByEntityID", fields: ["entityID", "id", "primary"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
entityID: ID # ownerID # set once associated
associatedEntityEmailAddressID: ID # set once associated
type: EmailType!
lock: LockType
lockDate: Int
email: String!
emailVerified: Boolean!
primary: PrimaryType!
notifications: Boolean!
visible: Boolean!
secret: Boolean!
creatorID: ID!
creator: Entity! (fields: ["creatorID"]) # no rights once created unless entityID matches owner
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
type AssociatedEntityPhoneNumber
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
phoneID: ID!
phoneNumber: PhoneNumber! (fields: ["phoneID"])
entityID: ID!
entity: Entity! (fields: ["entityID"])
# timestamps
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
type PhoneNumber
(fields: ["id"])
(name: "phonesByEntityID", fields: ["entityID", "id"])
(name: "primaryPhonesByEntityID", fields: ["entityID", "id", "primary"])
(name: "phonesByPhoneNumber", fields: ["phone", "id"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
entityID: ID # set once associated
associatedPhoneNumberID: ID # set once associated
type: PhoneType!
lock: LockType
lockDate: Int
phone: String!
phoneVerified: Boolean!
tollFree: Boolean!
primary: PrimaryType!
notifications: Boolean!
visible: Boolean!
secret: Boolean!
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
enum PhoneType {
VOIP
MOBILE
LANDLINE
}
enum EntityType {
UNKNOWN
PERSON
BUSINESS
ORGANIZATION
AGENCY
API
}
enum EntitySecurity {
USER
ADMIN
}
enum LockType {
CONFIRMATION
ADMIN
PASSWORD
ABUSE
BLACKLIST
}
type RandomAssociatedData
(fields: ["id"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
entityID: ID!
entity: Entity! (fields: ["entityID"])
someData: String!
createdAt: AWSTimestamp! # never update- just replace
}
# semipivot table- 1:M allows multiple entities per cognito entity(?)
type AssociatedCognitoEntity
# https://aws.amazon.com/blogs/mobile/graphql-security-appsync-amplify/
(rules: [
# Defaults to use the "owner" field.
{ allow: owner },
# Authorize the update mutation and both queries. Use `queries: null` to disable auth for queries.
{ allow: owner, ownerField: "owner", operations: [update, read] }
# Admin users can access any operation.
{ allow: groups, groups: ["Admin"] }
])
{
id: ID!
type: EntityType!
cognitoID: String!
owner: String # may be same as primaryEntityID?
primaryEntityID: ID!
primaryEntity: Entity! (fields: ["primaryEntityID"])
primaryRandomAssociatedDataID: ID!
primaryRandomAssociatedData: RandomAssociatedData! (fields: ["primaryRandomAssociatedDataID"])
# all known entities associated with this cognito user
entities: [Entity!]! (fields: ["id"])
# plus main props of base Cognito profile
name: String
email: String
phone: String
picture: String
profile: String
username: String
gender: String
# timestamps
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
# originally what I was trying to use as auth class
type Entity
(fields: ["id"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
associatedCognitoEntityID: ID # set once associated
RandomAssociatedDataID: ID! # ALL entities get a key
RandomAssociatedData: RandomAssociatedData! (fields: ["RandomAssociatedDataID"])
type: EntityType!
entitySecurity: EntitySecurity!
lock: LockType
lockDate: Int
username: String
usernameDate: Int
password: String
passwordDate: Int
name: String
nameDate: Int
gender: String
genderDate: Int
picture: String
pictureVisible: Boolean!
pictureDate: Int
link: String
linkConfirmed: Boolean!
linkVisible: Boolean!
linkDate: Int
# proven claimed for this identity - AUTHORITY
# TODO: NOTE: multiple emails/phones/entities/cognitos with conflicting attributes (phone/email) will/should be in conflict/prevented
# users ideally shouldn't have multiple entities per cognito
# and even more ideally shouldn't any phones or emails on non primary entities
# hopefully uniqueness of phone number and email will prevent one cognito having
# two+ entities with the same phones or emails. They may be forced to move a phone/email
# over if they log in with a cognito that matches a phone from one entity and an email
# from another.
# Similarly, if a user logs in with another provider, creating a new account with the same
# email as an existing account, they should be blocked/the accounts should be merged.
associatedPhones: [AssociatedEntityPhoneNumber!]! (fields: ["id"]) # all phones, including one for primary
associatedEmails: [AssociatedEntityEmailAddress!]! (fields: ["id"]) # all emails, including one for primary
# Votes ABOUT this entity - NO AUTHORITY
CountedVotes: [CountedVote!]! (name: "CountedVoteVoteID", fields: ["id"])
# created BY this entity - AUTHORITY
myVotes: [Vote!]! (name: "CreatorEntityVotes", fields: ["id"])
# groups this entity is participating in (created?)
mygroups: [Group!]! (fields: ["id"])
# badged earned by this entity
myEarnedBadges: [EarnedBadges!]! (fields: ["id"])
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
type Group
(fields: ["id"])
(name: "groupsByCreatorID", fields: ["creatorID", "id"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
lock: LockType
lockDate: Int
memo: String
memoDate: Int
description: String
descriptionDate: Int
link: String
linkVisible: Boolean!
linkDate: Int
hidden: Boolean!
entities: [Entity!]! (fields: ["id"]) # entities involved, NO authority
Votes: [Vote!]! (fields: ["id"]) # associated Votes
creatorID: ID!
creator: Entity! (fields: ["creatorID"]) # AUTHORITY
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
# pivot table for Votes
type CountedVote
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
VoteID: ID!
Vote: Vote! (name: "CountedVoteVoteID", fields: ["VoteID"])
entityID: ID!
entity: Entity! (name: "CountedVoteEntityID", fields: ["entityID"])
someData: String!
# timestamps
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
type Vote
(fields: ["id"])
(name: "VotesByCreatorID", fields: ["creatorID"])
(rules: [
{ allow: public, operations: [read] }
])
{
id: ID!
CountedVoteID: ID # set once applied
Vote: Int!
VoteDate: Int!
memo: String
memoVisible: Boolean!
link: String
linkVisible: Boolean!
linkDate: Int
emailID: ID
phoneID: ID
groupID: ID
visible: Boolean!
hidden: Boolean!
creatorID: ID!
creator: Entity! (name: "CreatorEntityVotes", fields: ["creatorID"])
createdAt: AWSTimestamp!
updatedAt: AWSTimestamp!
}
type QueryRoot {
badge(id: ID!): Badge (cognito_groups: ["Users", "Admin"])
getVoteByID(id: ID!): Vote (cognito_groups: ["Users", "Admin"])
getgroupByID(id: ID!): Group (cognito_groups: ["Users", "Admin"])
getEntityByID(id: ID!): Entity (cognito_groups: ["Users", "Admin"])
getEmailByID(id: ID!): EmailAddress (cognito_groups: ["Users", "Admin"])
getEmailByEmail(email: String!): EmailAddress (cognito_groups: ["Users", "Admin"])
getPhoneByID(id: ID!): PhoneNumber (cognito_groups: ["Users", "Admin"])
getPhoneByPhoneNumber(phone: String!): PhoneNumber (cognito_groups: ["Users", "Admin"])
}