GraphQL connections Not Authorized to access in Mutation - amazon-web-services

I'm not sure if I'm doing this correctly with the connections in AppSync GraphQL.
This is what my graphql models look like:
type User #model #auth(rules: [{ allow: owner, ownerField: "username" }]) {
id: ID!
username: String!
email: String!
userType: UserType
}
enum UserType {
TEACHER
CREATOR
}
type Teacher #model {
id: ID!
userId: ID
name: String!
activations: [Activation]
#connection(keyName: "activationsByTeacherId", fields: ["id"])
creators: [TeacherCreatorPartnership]
#connection(name: "TeacherCreatorPartnership")
}
type Creator #model {
id: ID!
userId: ID
name: String!
email: String!
username: String
teachers: [TeacherCreatorPartnership] #connection(name: "CreatorTeacherPartnership")
posts: [Post] #connection(name: "CreatorPosts")
activations: [CreatorActivations] #connection(name: "CreatorActivations")
}
type TeacherCreatorPartnership #model(queries: null) {
id: ID!
teacher: Teacher! #connection(name: "TeacherCreatorPartnership")
creator: Creator! #connection(name: "CreatorTeacherPartnership")
}
type CreatorActivations #model(queries: null) {
id: ID!
creator: Creator! #connection(name: "CreatorActivations")
activation: Activation! #connection(name: "ActivationCreators")
}
type Activation
#model
#key(
name: "activationsByTeacherId"
fields: ["teacherId"]
queryField: "activationsByTeacherId"
)
#auth(
rules: [
{ allow: groups, groups: ["Admin"] }
{
allow: owner
ownerField: "teacherId"
operations: [create, update, delete]
}
{ allow: private, operations: [read] }
{ allow: public, operations: [read] }
]
) {
id: ID!
teacherId: ID!
title: String!
teacher: Teacher #connection(fields: ["teacherId"])
creators: [CreatorActivations] #connection(name: "ActivationCreators")
}
The idea is that when user signs in with Amplify, they'll go through an onboarding process and choose whether they're a creator or a teacher.
This works fine, but the problem is if a signed-in user wants to create a new Activation.
I'm not sure if the graphql on the Activation model is set correctly, perhaps the auth key is wrong?
This is how I'm processing create
const createNewActivation = async () => {
if (!title || !content || !location) return;
const newId = uuid();
activation.id = newId;
const user = await Auth.currentAuthenticatedUser();
try {
await API.graphql({
query: createActivation,
variables: {
input: {
...activation,
teacherId: user.attributes.sub,
},
},
authMode: "AMAZON_COGNITO_USER_POOLS",
});
} catch (error) {
console.log("Error: problem creating activation: ", error);
}
};
I've also set up a lambda function so that when user confirms their account from sign up, it will save the user information in DynamoDB with an ID, the username, and email.
Edit: Got it working but...
So, I was able to save the data, but I had to change the schema in my Activation model from:
{
allow: owner
ownerField: "teacherId"
operations: [create, update, delete]
}
to just
{
allow: owner
}
Not sure why I can't set the owner to teacherId? Wouldn't I need this so I can make proper connections to Teacher's model with teachId field?

Related

Amplify push GraphQL schema error #connection and #key

I have been following a YouTube tutorial to create and sell books online. In the tutorial, there has been a case of designing a GraphQL scheme for the orders. I have seen that the migrate details have been updated and have tried to adhere to the new regulations,I have faced a lot of errors. I tried solving most of them but some remain persistent. As this is my first time working with Graphql and AWS, I cannot progress any further. Youtube Link is provided below:
https://youtu.be/cWDJoK8zw58
type Book
#model(subscriptions: null)
#auth(
rules: [
#allow admins to create, update and delete books
{ allow: groups, groups: ["Admin"] }
# allow all authenticated users to view books
{ allow: private, operations: [read] }
# allow all guest users (not authenticated) to view books
{ allow: public, operations: [read] }
]
)
{
id: ID!
title: String!
description: String
image: String
author: String
featured: Boolean
price: Float
orders: [BookOrder] #hasOne(keyName: "byBook", fields: ["id"])
}
type BookOrder
#model(queries: null, subscriptions: null)
##key(name: "byBook", fields: ["book_id", "order_id"])
##key(name: "byOrder", fields: ["order_id", "book_id"])
#auth(
rules: [
# allow admins to create bookorders for customers by using customer email in lambda
{ allow: owner, identityClaim: "email", ownerField: "customer" }
{ allow: groups, groups: ["Admin"] }
]
)
{
id: ID!
book_id: ID! #index(name: "byBook", sortKeyFields: ["book_id, order_id"])
order_id: ID! #index(name: "byOrder", sortKeyFields: ["order_id, book_id"])
book: Book #hasOne(fields: ["book_id"])
order: Order #hasOne(fields: ["order_id"])
}
type Order
#model(subscriptions: null)
#auth(
rules: [
# only owner can see his orders
{ allow: owner, identityClaim: "email", ownerField: "customer" }
#allow admins to view orders
{ allow: groups, groups: ["Admin"] }
]
)
##key(name: "byUser", fields: ["user"])
{
id: ID!
user: String! #index(name: "byUser", sortKeyfields: ["user"])
date: String
total: Float
books: [BookOrder] #manyToMany(keyName: "byOrder", fields: ["id"])
}
enum OrderStatus {
SUCCESS
FAILED
}
input CartItem {
id: ID!
title: String
image: String
price: Float
amount: Int
}
input ProcessOrderInput {
id: ID!
cart: [CartItem]
total: Float!
token: String!
address: String
}
type Mutation {
processOrder(input: ProcessOrderInput!): OrderStatus
#function(name: "processPayment-${env}")
#function(name: "createOrder-${env}")
}
The error message being shown is as follows:
n error occurred when pushing the resources to the cloud
🛑 An error occurred during the push operation: /
Schema validation failed.
Unknown argument "keyName" on directive "#hasOne".
GraphQL request:21:31
20 | price: Float
21 | orders: [BookOrder] #hasOne(keyName: "byBook", fields: ["id"])
| ^
22 | }
Unknown argument "sortKeyfields" on directive "#index". Did you mean "sortKeyFields"?
GraphQL request:56:40
55 | id: ID!
56 | user: String! #index(name: "byUser", sortKeyfields: ["user"])
| ^
57 | date: String
Unknown argument "keyName" on directive "#manyToMany".
GraphQL request:59:34
58 | total: Float
59 | books: [BookOrder] #manyToMany(keyName: "byOrder", fields: ["id"])
| ^
60 | }
Unknown argument "fields" on directive "#manyToMany".
GraphQL request:59:54
58 | total: Float
59 | books: [BookOrder] #manyToMany(keyName: "byOrder", fields: ["id"])
| ^
60 | }
Directive "#manyToMany" argument "relationName" of type "String!" is required, but it was not provided.
GraphQL request:59:22
58 | total: Float
59 | books: [BookOrder] #manyToMany(keyName: "byOrder", fields: ["id"])
| ^
60 | }
⚠️ Review the Amplify CLI troubleshooting guide for potential next steps: https://docs.amplify.aws/cli/project/troubleshooting/
How do I resolve this error? Following the newly updated was-amplify docs, it seems right. But it is not working
It seems like your schema is missing a few logic statements, there is no need of putting up #manyTomany statements there. The updated Schema can be done as shown below
type Book
#model(subscriptions: null)
#auth(
rules: [
#allow admins to create, update and delete books
{ allow: groups, groups: ["Admin"] }
# allow all authenticated users to view books
{ allow: private, operations: [read] }
# allow all guest users (not authenticated) to view books
{ allow: public, operations: [read] }
]
)
{
id: ID!
title: String!
description: String
image: String
author: String
featured: Boolean
price: Float
orders: [BookOrder] #hasMany(fields: ["id"])
}
type BookOrder
#model(queries: null, subscriptions: null)
#auth(
rules: [
# allow admins to create bookorders for customers by using customer email in lambda
{ allow: owner, identityClaim: "email", ownerField: "customer" }
{ allow: groups, groups: ["Admin"] }
]
)
{
id: ID!
book_id: ID! #index(name: "byBook", sortKeyFields: ["order_id"])
order_id: ID! #index(name: "byOrder", sortKeyFields: ["book_id"])
book: Book #belongsTo(fields: ["book_id"])
order: Order #belongsTo(fields: ["order_id"])
}
type Order
#model(subscriptions: null)
#auth(
rules: [
# only owner can see his orders
{ allow: owner, identityClaim: "email", ownerField: "customer" }
#allow admins to view orders
{ allow: groups, groups: ["Admin"] }
]
)
{
id: ID!
user: String! #index(name: "byUser")
date: String
total: Float
books: [BookOrder] #hasMany(fields: ["id"])
}
enum OrderStatus {
SUCCESS
FAILED
}
input CartItem {
id: ID!
title: String
image: String
price: Float
amount: Int
}
input ProcessOrderInput {
id: ID!
cart: [CartItem]
total: Float!
token: String!
address: String
}
type Mutation {
processOrder(input: ProcessOrderInput!): OrderStatus
#function(name: "processPayment-${env}")
#function(name: "createOrder-${env}")
}
You should not be facing the issue now.

Filter Expression can only contain non-primary key attributes

I'm new to AWS Amplify, and have created a GraphQL API for a project that I'm working on in order to learn it & AppSync. Essentially I have a schema that looks like the following:
type User #model {
id: ID! #primaryKey
boards: [Board] #hasMany
createdAt: String!
updatedAt: String!
}
type Board #model {
id: ID! #primaryKey
createdBy: User!
title: String!
}
and I'm trying to run the following query in the AppSync console:
query MyQuery {
listUsers {
items {
boards {
items {
title
}
}
}
}
}
But for some reason I keep seeing this error:
Filter Expression can only contain non-primary key attributes: Primary key attribute: userBoardsId
I've specified the primary key in both models, and I'm aware that AppSync generates the 'userBoardsId' foreign key, but I'm not sure why it's causing an issue.
Have you tried like this? Since boards is array, you need to add items
query MyQuery {
listUsers {
items {
boards {
items {
title
}
}
}
}
}
EDIT:
type User #model {
id: ID! #primaryKey
boards: [Board] #hasMany (indexName: "byUser")
createdAt: String!
updatedAt: String!
}
type Board #model {
id: ID! #primaryKey
userID: ID! #index(name: "byUser")
createdBy: User
title: String!
}

AWS Appsync Graphql query to get list of items returns empty array even though the dynamodb table has items in it

I'm running an Angular 11 application that is integrated with AWS Amplify and Appsync using GraphQL and dynamoDB for the backend.
This is my Graphql schema:-
type School
#model
#auth(
rules: [{ allow: owner, ownerField: "admins", operations: [update, read] }]
) {
id: ID!
name: String!
admins: [Member]
classes: [Class] #connection(name: "SchoolClasses")
members: [Member] #connection(name: "SchoolMembers")
}
type Class
#model
#auth(
rules: [{ allow: owner, ownerField: "admins", operations: [update, read] }]
) {
id: ID!
name: String!
school: School #connection(name: "SchoolClasses")
admins: [Member]
members: [Member] #connection(name: "ClassMembers")
}
type Member #model #auth(rules: [{ allow: owner }]) {
id: ID!
name: String!
school: School #connection(name: "SchoolMembers")
class: Class #connection(name: "ClassMembers")
}
This is my client definition:-
const client = new AWSAppSyncClient({
url: awsconfig.aws_appsync_graphqlEndpoint,
region: awsconfig.aws_appsync_region,
auth: {
type: awsconfig.aws_appsync_authenticationType,
jwtToken: async () =>
(await Auth.currentSession()).getAccessToken().getJwtToken(),
},
complexObjectsCredentials: () => Auth.currentCredentials(),
cacheOptions: {
dataIdFromObject: (obj: any) => `${obj.__typename}:${obj.myKey}`,
},
});
This is my query method:-
client
.query({
query: ListSchools,
})
.then((data: any) => {
console.log('data from listSchools ', data);
console.log(data.data.listSchools.items);
});
};
This is my query definition:-
import gql from 'graphql-tag';
export default gql`
query ListSchools(
$filter: ModelSchoolFilterInput
$limit: Int
$nextToken: String
) {
listSchools(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
admins {
id
name
createdAt
updatedAt
owner
}
classes {
nextToken
}
members {
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
The output for data in the console looks like this:-
{
"data":{
"listSchools":{
"items":[],
"nextToken":null,
"__typename":"ModelSchoolConnection"
}
},
"loading":false,
"networkStatus":7,
"stale":false
}
As you can see, the items is an empty array. But currently I have 3 items in my dynamoDB table:-
What am I doing wrong?
I have checked the regions to see if it is querying a different region, but it is checking the correct region, so I should be seeing the results. Also, wouldn't it throw an error if we're querying the wrong table?
I figured it out. The issue was in the GraphQL Schema definition where I had set the #auth paramter to only allow a certain admin to access the list, that's why I was getting back an empty array. I removed the #auth parameter and it now gives back the proper list of items.

Field 'owner' suddenly undefined in AWS Amplify GraphQL

Today I tried to add a 'name' field to the 'User' type of my AWS GraphQL schema. After running "amplify push" and "code gen", I received an error from AWS saying that 'owner' was null. After comparing my git history side by side, I found that codegen deleted the autogenerated owner field in my queries while simultaneously adding two new fields, 'createdAt' and 'updatedAt'. Unsure how/why this was happening, I reverted to an earlier git commit where everything was working fine and pushed to amplify. All my files were reverted back to a commit where everything was working perfectly. However, I am still receiving errors that 'owner' is null. GraphQL schema and screenshots of terminal are below. Happy to provide other files (queries, mutations, etc.) if helpful.
type Image {
bucket: String!
region: String!
key: String!
}
type Circle
#model
#auth(rules: [
{ allow: owner, operations: [create, delete] }
]) {
id: ID!
name: String!
username: String!
password: String
bio: String
avatar: Image
favors: [CircleFavor] #connection(name: "CirclesFavors")
users: [CircleUser] #connection(name: "CirclesUsers")
pendingUsers: [User] #connection
tags: [String]
admin: User #connection
isPrivate: Boolean!
poster: ID!
}
type Favor
#model
#auth(rules: [
{ allow: owner, operations: [create, delete] }
]) {
id: ID!
name: String
description: String
location: String
deadline: AWSDateTime
circles: [CircleFavor] #connection(name: "FavorsCircles")
price: Float!
}
type CircleFavor
#model(queries: { get: "getCircleFavor" })
#auth(rules: [
{ allow: owner, operations: [create, delete] }
]) {
id: ID!
circle: Circle! #connection(name: "CirclesFavors")
favor: Favor! #connection(name: "FavorsCircles")
poster: User! #connection(name: "UsersFavors")
}
type User
#model(
queries: { get: "getUser" }
mutations: { create: "registerUser", update: "updateUser" }
subscriptions: null
)
#auth(rules: [
{ allow: owner, operations: [create, delete] }
]) {
id: ID!
username: String!
bio: String
email: AWSEmail
isRegistered: Boolean
orders: [Order] #connection(name: "UsersOrders")
circles: [CircleUser] #connection(name: "UsersCircles")
favors: [CircleFavor] #connection(name: "UsersFavors")
}
type CircleUser
#model(queries: { get: "getCircleUser" })
#auth(rules: [
{ allow: owner, operations: [create, delete] }
]) {
id: ID!
circle: Circle! #connection(name: "CirclesUsers")
user: User! #connection(name: "UsersCircles")
}
type Order
#model(
queries: null
mutations: { create: "createOrder" }
subscriptions: null
)
#auth(rules: [
{ allow: owner, operations: [create, delete] }
]) {
id: ID!
favor: Favor #connection
claimer: User #connection(name: "UsersOrders")
additional_costs: Float
isCompletedByClaimer: Boolean!
isApprovedByPoster: ApprovalStatus!
}
enum ApprovalStatus {
PENDING
APPROVED
DISPUTED
}
Bad news: You dropped those fields from the database. The owner data is gone, which is why it's coming back NULL now that you re-added it to the schema.

One to Many Relationship not showing in the object Amplify Schema definition

I am new to using amplify with GraphQL. I was setting up my DB schema and auto-generating the functions after running amplify push.
Goals I want to achieve but do not know how to are
I would like to be able to get user with all connected information (with one to one and one to many relationships) in the returned object from getUser
I would like to still be able to get userByUserName and see all connected one-to-many relationships as well
When calling the API generated function to get the user,
let user = await API.graphql(graphqlOperation(getUser,{id:userId}))
I am getting a user object back, however, it looks like this - even though I am definitely sure that data is set up correctly in the database.
buttons: {nextToken: null} -- WANT THIS TO INCLUDE ACTUAL INFORMATION ABOUT BUTTONS CONNECTED TO THIS USER
createdAt: "2020-09-02T23:41:12.278Z"
customStyles: {id: "e3d1bbef-ec6f-4a6d-9b5d-e693e890d4e0", bgColor: "F9FF9F", bgBtnColor: "FFFFFF", bgBtnHoverColor: "000000", textColor: "000000", …}
defaultStyles: null
email: "nata#email.edu"
firstName: "Nata"
id: "d683a6bb-383e-4cf1-943a-05b3da4e5cc3"
lastName: "Vache"
socialNetwork: {nextToken: null} -- WANT THIS TO INCLUDE ACTUAL INFORMATION ABOUT SOCIAL NETWORKS CONNECTED TO THIS USER, THE SAME WAY AS FOR EXAMPLE customStyles IS SHOWN.
updatedAt: "2020-09-02T23:41:12.278Z"
userName: "Nata568"
type User #model #key(name: "byUserName", fields: ["userName"], queryField: "userByUserName"){
id: ID!
firstName: String!
lastName: String!
userName: String!
email: String!
socialNetwork: [UserSocialNetwork] #connection(keyName: "UserSocialNetworkUser", fields: ["id"])
buttons: [Button] #connection(keyName: "ButtonUser", fields: ["id"])
defaultStyles: DefaultStyle #connection
customStyles: CustomStyle #connection
}
type UserSocialNetwork #model #key(name: "UserSocialNetworkUser", fields: ["userID", "id"], queryField:"userSocialNetworkByUserID") {
id: ID!
socialNetworkUsername: String!
userID: ID!
supportedSocialNetwork: SupportedSocialNetwork! #connection
}
type SupportedSocialNetwork #model {
id: ID!
name: String!
address: String!
}
type Button #model #key(name: "ButtonUser", fields: ["userID", "id"], queryField: "buttonByUserID") {
id: ID!
name: String!
address: String!
image: String
userID: ID!
}
This schema does not include all my model definitions - customStyles, defaultStyles, and the rest but they are one to one relationship, which is working perfectly fine. I am having issues with one-to-many relationships, such as User to UserSocialNetwork and User to Buttons.
I have read lots of resources about this on AWS Amplify Docs, have gone through examples but still have not found anything that I could work with that would allow me to get the information from connections on getUser call and also give me the ability to get the user by username.
Any input would be appreciated!!!
This is a problem with query Depth. It is 2 by default and need to increase it (to 4 in your case).
You can resolve this by following below steps.
In your cmd in your project root, (where you normally run amplify commands).
Run amplify codegen configure - This will prompt configs again.
Enter 4 for this option Enter maximum statement depth [increase from default if your schema is deeply nested]
Run amplify codegen - this will create your queries and mutations according to the new depth.
You can verify it by checking grapgql/queries.js.
Earlier you could see below fragment in getUser query in grapgql/queries.js,
buttons {
nextToken
}
But after following above resolution steps, it should be something like below.
buttons {
items {
id
image
address
}
}
Finally query for your user again.
General comment: If username is unique you could use that as the id instead of creating the extra index. If it isn't there will be problems with this schema since it can't do a getOperation but instead will do a query which might return multiple answers.
(The resolver in Appsync wants to use a dynamoDB.get by default (& design) but using an index would be a dynamoDB.query which results in a lot issues)
Anyway using your schema I can get it to work just fine when using the id
"data": {
"getUser": {
"createdAt": "2020-09-07T13:54:23.440Z",
"email": "meax",
"firstName": "Max",
"id": "19a752ec-5050-4e02-8ff8-05d9523e7ea5",
"socialNetwork": {
"items": [
{
"socialNetworkUsername": "What",
"id": "280ec8ea-5b25-46d3-8f22-f170e3210146",
"userID": "19a752ec-5050-4e02-8ff8-05d9523e7ea5"
}
]
},
"lastName": "Sc",
"userName": "zanndo",
"updatedAt": "2020-09-07T13:54:23.440Z",
"buttons": {
"items": [
{
"id": "65971568-b388-40a3-b99e-1bff0a730161",
"image": null,
"address": "ButonAdre"
}
]
}
}
}
}
This being my query
getUser(id: "19a752ec-5050-4e02-8ff8-05d9523e7ea5") {
createdAt
email
firstName
id
socialNetwork {
items {
socialNetworkUsername
id
userID
}
}
lastName
userName
updatedAt
buttons {
items {
id
image
address
}
}
}
}
Here is one where I made email the id.
query MyQuery {
getUser(id: "sw#gmail.com") {
id
firstName
lastName
socialNetwork {
items {
socialNetworkUsername
supportedSocialNetwork {
name
id
address
}
}
}
buttons {
items {
id
address
name
}
}
}
}
Also works
{
"data": {
"getUser": {
"id": "sw#gmail.com",
"firstName": "S",
"lastName": "W",
"socialNetwork": {
"items": [
{
"socialNetworkUsername": "SomeUserNameOrSomething",
"supportedSocialNetwork": {
"name": "Supported1",
"id": "daf52246-4b25-402c-9fdd-46f8f35e1b89",
"address": "SupportedAddr"
}
}
]
},
"buttons": {
"items": [
{
"id": "9883bd91-a2f1-479d-ab65-7a4bbe7b2dc4",
"address": "ButtonAddr",
"name": "Button1"
}
]
}
}
}
}
Bonus using your index
userByUserName(userName: "SW") {
items {
buttons {
items {
name
id
}
}
socialNetwork {
items {
socialNetworkUsername
supportedSocialNetwork {
name
}
}
}
}
}
"userByUserName": {
"items": [
{
"buttons": {
"items": [
{
"name": "Button1",
"id": "9883bd91-a2f1-479d-ab65-7a4bbe7b2dc4"
}
]
},
"socialNetwork": {
"items": [
{
"socialNetworkUsername": "SomeUserNameOrSomething",
"supportedSocialNetwork": {
"name": "Supported1"
}
}
]
}
}
]
}
This was the schema I used
type User #model #key(name: "byUserName", fields: ["userName"], queryField: "userByUserName"){
firstName: String!
lastName: String!
userName: String!
id: String!
socialNetwork: [UserSocialNetwork] #connection(keyName: "UserSocialNetworkUser", fields: ["id"])
buttons: [Button] #connection(keyName: "ButtonUser", fields: ["id"])
}
type UserSocialNetwork #model #key(name: "UserSocialNetworkUser", fields: ["userID", "id"], queryField:"userSocialNetworkByUserID") {
id: ID!
socialNetworkUsername: String!
userID: String!
supportedSocialNetwork: SupportedSocialNetwork! #connection
}
type SupportedSocialNetwork #model {
id: ID!
name: String!
address: String!
}
type Button #model #key(name: "ButtonUser", fields: ["userID", "id"], queryField: "buttonByUserID") {
id: ID!
name: String!
address: String!
image: String
userID: String!
}
Maybe I have misunderstood something?