How Can I Troubleshoot An Unauthorised Error With AWS Amplify? - amazon-web-services

Use Case: I desire to delete a DynamoDB record with a mutation as an owner of the record using AWS Amplify, GraphQL and Typescript.
Note:
All changes have successfully deployed and built in the AWS Amplify Pipeline as a full-stack build with CI/CD
I am logged in as the owner of the record.
I have used console.log to ensure the values are obtained before the await function is ran.
Front-End Query:
await API.graphql({
query: deleteImage,
variables: {
input: {
id : fileName,
employerID : Auth.user.attributes["custom:id"],
},
},
authMode: "AMAZON_COGNITO_USER_POOLS",
});
GraphQL Schema:
type Image
#model
#auth(
rules: [
{ allow: owner, operations: [create, update, delete, read] }
]
GraphQL Mutation:
export const deleteImage = /* GraphQL */ `
mutation DeleteImage(
$input: DeleteImageInput!
$condition: ModelImageConditionInput
) {
deleteImage(input: $input, condition: $condition) {
id
owner
name
contentType
createdAt
updatedAt
employerID
}
}
`;
Error Message:
"Not Authorized to access deleteImage on type Mutation"

The unauthorized error may occur misleadingly when the record in the DynamoDB table does not exist.
Check your table to see whether the record exists, as this may be the cause.

Related

AWS Amplify graphql Subscription Filter Returns All

When using AWS Amplify graphql API, does a subscription filter require any further config beyond adding the filter object to the subscription options?
The documentation here suggests simply adding the filter to the subscription.
Example of the subscription:
const subscribeDeviceState = () => {
const sub = API.graphql(
graphqlOperation(onUpdateDevice, {
filter: {
id: { eq: "abc-123" },
},
})
).subscribe({
next: ({ provider, value }) => {
console.log({ provider, value });
processDeviceData(value.data.onUpdateDevice);
},
error: (error) => console.warn(error),
});
return sub;
};
I have tried multiple parameters in the filter object but each time, whenever any items is updated the subscription returns values for that item, even if the filter does not match.
I have only generated the schema manually and I'm using the Amplify options to generate the graphql code and operations on push.
schema.graphql:
type Device #model #auth(rules: [{ allow: private, provider: userPools }]) {
id: ID!
name: String
reported: AWSJSON
features: AWSJSON
}
Amplify generated subscription:
export const onUpdateDevice = `
subscription OnUpdateDevice {
onUpdateDevice {
id
name
reported
features
createdAt
updatedAt
}
}
`;

Updating data stored in DynamoDB using AWS Amplify graphqlOperation

I am trying to update the user data using the API from AWS Amplify. I can add new record to the connected table with the createUser function imported from graphql mutation but the updateUser doesn't work and returning error. The function I used is defined as below:
async function UpdateUsers() {
try {
const user = {
role: 'student',
fname: formState.fname,
lname: formState.lname,
age: formState.age,
school: formState.school,
class_name: formState.class_name,
sid: formState.sid,
test1: formState.test1,
test2: formState.test2,
test3: formState.test3
}
const newData = await API.graphql(graphqlOperation(updateUser, {input: user}))
} catch (err) {
alert('Error when updating user', err)
}
}
}
I also tried to do it another way by deleting the old record and create a new one. But the deleteUser function generated automatically from graphql mutation did not work either.

AWS Amplify Graphql Mutations - Batch Delete Join Table Item When Original Item Is Deleted

Category: API.graphql mutation
Amplify Version: 8.0.1
Service: AppSync
Research
I've come across this documentation that notes batch is not supported, and thus requires a custom resolver.
I've also come across this feature request for cascading delete with DynamoDB Streams and Lambda.
I see there is an example for a batch put custom resolver.
Problem:
There is a #model for JobListing, and one for Image within the Amplify Graphql Schema. They have a ManyToMany Relationship. When I delete an Image record from DynamoDB, all JobListingImage records for that image need to be deleted as well.
I've considered looping through the JobListingImage records, but there could be a possiblity where there are 100,000 records to query and then delete.
Code Snippet of failed attempt
const handleDelete = async (id, name) => {
try {
await API.graphql({
query: deleteJobListingImages,
variables: {
input: {
imageID: id,
imageemployerID: Auth.user.attributes["custom:id"],
},
},
authMode: "AMAZON_COGNITO_USER_POOLS",
});
await Storage.remove(name, {
level: "protected",
});
await API.graphql({
query: deleteImage,
variables: {
input: {
id: id,
employerID: Auth.user.attributes["custom:id"],
},
},
authMode: "AMAZON_COGNITO_USER_POOLS",
});
} catch (err) {
console.log(err);
}
};
Question
How do I delete all the JobListingImages that refer to the Image id I am deleting?

How to Access data from Dynamo db tables in react native using aws-amplify

I am using aws-amplify in my react-native application for user authentication. it works fine.
modules used for aws amplify
aws-amplify: "^3.0.23", aws-amplify-react-native: "^4.2.4"
Now I would like to connect the app or retrieve the data from a DynamoDB table and use that data in my react native application.
How can I Access data from Dynamodb tables in react native using aws-amplify is there any get methods or rest APIs to retrieve data from dynamodb.
How can I establish a connection with dB to my react-native application.
Thanks in advance.
I used
aws-sdk/dist/aws-sdk-react-native
var AWS = require('aws-sdk/dist/aws-sdk-react-native');
AWS.config.update({
region: 'your region',
dynamoDbCrc32: false
});
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'your identityPool Id'
});
const gp = AWS.config.credentials.getPromise();
gp.then(() => {
const documentClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName: "table name",
Key: "your key",
};
documentClient.get(params, function(err, data) {
if (err){
console.log(err)
} else {
console.log(data)
}
}
I m using graphql api and when i create any model, aws automatically creates queries and mutations for that.
Let's say i have this model in schema json.
type ExpoTicketsObject
#model
#auth(rules: [{ allow: private, provider: iam, operations: [read, create, update, delete] }]) {
id: ID!
tickets: AWSJSON!
}
here are related query and mutation:
in lambda function i defined the query:
const appsync = require("aws-appsync");
const gql = require("graphql-tag");
// inorder to use fetch api in node, we need this polyfill
require("cross-fetch/polyfill");
const getExpoToken = gql`
query getExpoToken($token: String!) {
# token is the primary key.
getExpoToken(token: $token) {
id
token
playerUsername
}
}
`;
then I call it:
const tokenRes = await graphqlClient.query({
query: getExpoToken,
variables: { token: event.arguments.token }
});

How should be the schema on relations between microservices

I'm using NestJS + Prisma + Apollo Federation.
On microservice A is the definition of user, on microservice B is defined posts.
The relation is 1 - N, a user can have N posts.
In Prisma, datamodel of Post is defined with a String for user, since userId is a uuid.
type Post {
id: Int! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
user: String!
}
In generated schema (with https://graphql-code-generator.com), Post has a attribute of type User, and this type User extends the id and a array of posts:
type Post #key(fields: "id") {
id: Int!
createdAt: DateTime!
updatedAt: DateTime!
user: User!
}
extend type User #key(fields: "id") {
id: ID! #external
posts: [Post]
}
In apollo federation, all works as expected, except when a query is made trying to link between both microservices.
On playground, if you try to query posts with its user without setting subfields, it breaks the schema and say you have to set the subfields of User, and if you set the subfields graphql responds with a message that you cannot use subfields because its type is String.
The only way that I could make this work correctly was setting in Prisma a userId field of type string and setting another field in schema called user of type User. But all the examples didn't show a field to work with db and a field to work with schema.
My question is if that is the recommended or am I missing something.
In order to get User from Post, you have to create a resolver in your post and user service.
Post Service
const resolvers = {
Post:{//before you do this you have to extend User schema which you already did.
// you are basically asking the 'User' service, which field should be used to query user.
user: ref => ({ __typename: 'User', id: ref.userId })
}
Query:{
// query resolvers
},
Mutation:{
// mutation resolvers
}
User service
const resolvers = {
User:{//the code below allows other services to extend User in their own schemas
__resolveReference: (ref, { userDataLoader }) => userDataLoader.load(ref.id),
}
Query:{
// query resolvers
},
Mutation:{
// mutation resolvers
}
Now linking arrays like [Post] must be done purely in the post service
Post Service
const resolvers = {
Post:{//before you do this you have to extend User schema which you already did.
// you are basically telling the user service, which field should be used to query user.
user: ref => ({ __typename: 'User', id: ref.user })
},
User:{
posts:(ref, args, {postDataLoader}) => getOrders(ref.postIds) //or ref.userId(foreign key)
},
Query:{
// query resolvers
},
Mutation:{
// mutation resolvers
}