Appsync Fine Graine Control on Mutation with Multiple Tables - aws-appsync

I have the following schema where the author of an Event can make notes on the Event. Only the author of the event should be able to create notes. I store the author in the Event. However, I'm finding other users are able to create a note on an event they didn't author by simply passing the eventId of an another users event, like so:
mutation {
noteOnEvent(input: { eventId: "***", content: "A comment"}) {
eventId
content
}
}
How can i prevent this? I don't see a way to access the EventTable author to in the noteOnEvent resolver
Schema
type Note {
eventId: ID!
notetId: ID!
content: String
author: String
}
input CreateNoteInput {
eventId: ID!
noteId: String!
content: String
}
type Event {
id: ID!
name: String
author: String
notes: [Note]
}

You can accomplish this using a Nested Resolver.
If you modify your schema slightly, you can accomplish it like so:
type EventCheckedNote {
// Add a resolver on note which creates the note. The event will be available as $cxt.source, and you can make an authZ check before making the mutation.
note: Note
}
type Mutation {
// Add a resolver on noteOnEvent which queries the Event table.
noteOnEvent(input: CreateNoteInput!): EventCheckedNote
}
Here is a tutorial on using nested resolvers to perform authorization checks with multiple data sources involved: https://hackernoon.com/graphql-authorization-with-multiple-data-sources-using-aws-appsync-dfae2e350bf2

Related

Best practices with connecting data from 2 models

I've got two models: Note and Profile. Note contains foreign key of connected profile as you can see below.
Note: {
profile_id: String,
date: String,
content: String,
}
Profile: {
id: String,
name: String,
profilePicture: String
}
I want to get all notes and also name and profile picture of note.
In this situation should I:
get all notes and all profiles and then join them locally in for loop,
get all notes and then in for loop ask DB for name and picture of matching profile,
other option
Which way is recomended?
Take a look at mongoose's Populate. You can declare a Schema property with type: Schema.Types.ObjectId, ref: 'Profile'. When you run a Query you can .populate() this field with the corresponding document.

AWS AppSync Graphql equivalent of a left join query

I am trying to join two dynamoDb tables to get a result set similar to a left join. Here is a scaled down version of the tables in question
type MaintenanceRecomendationsMaster
#model
#key(name: "byrecomendID", fields: ["recomendId"], queryField: "byrecomendID") {
id: ID!
recomendId: Int!
displayName: String!
history: [UserMaintenanceHistory]
#connection(keyName: "byRecomendationsMasterId", fields: ["id"])
}
Here is the other table it joins to
type UserMaintenanceHistory
#model
#key(name: "byUserVehicleId", fields: ["uservehicleId"])
#key(name: "byRecomendationsMasterId", fields: ["RecomendationsMasterId"]) {
id: ID!
status: String
actionTakenDate: AWSDateTime
uservehicleId: ID!
RecomendationsMasterId: ID!
maintenanceCache: MaintenanceRecomendationsMaster
#connection(fields: ["RecomendationsMasterId"])
}
This is what the tables looks like with the data
I want to be able to send in recommendId =100 and uservehicleId = 1 to get a response like this
The query I am using like this works but it uses the filter clause which uses a scan operation and has a limitation of only fetching the first 100 records and then applying the filter on those 100 records. I want to run a query where I am currently running a filter right now if possible. I don't want to use custom resolvers if possible, unless that is the only way out.
query byrecomendIdQuery{
byrecomendId(recomendId: 100) {
items {
id
displayName
history (filter: {uservehicleId: {eq: "1"}}) {
items{
status
actionTakenDate
}
}
}
}
}

Generate Subscription with arguments in AWS AppSync with GraphQL Transform

I’m currently using GraphQL transform lib to generate all my schema.
I have a model defined like this:
type Feedback #model {
id: ID!
event: Event! #connection(name: "EventFeedbacks")
submittedDate: AWSDateTime!
}
and the auto-generated subscription schema is like this:
type Subscription {
onCreateFeedback: Feedback
#aws_subscribe(mutations: ["createFeedback"])
}
I would like to have an argument for the subscription so that I can subscribe to that event only, like this:
type Subscription {
onCreateFeedback(eventId: ID): Feedback
#aws_subscribe(mutations: ["createFeedback"])
}
What do I need to do to get this subscription auto generated? Thanks!
Customizing the subscription fields arguments is currently not supported. The only supported customization is to create multiple subscription fields tied to a single mutation.
Example:
type Feedback #model(subscriptions: { onCreate: ["onCreateFeedback", "onCreateFeedbackById"] }) {
id: ID!
event: Event! #connection(name: "EventFeedbacks")
submittedDate: AWSDateTime!
}
will generate for the subscription type:
type Subscription {
onCreateFeedback: Feedback
#aws_subscribe(mutations: ["createFeedback"])
onCreateFeedbackById: Feedback
#aws_subscribe(mutations: ["createFeedback"])
}
but then you will have to add the eventId argument manually on the onCreateFeedbackById field.
Though, I would suggest to open a feature request in https://github.com/aws-amplify/amplify-cli/issues
As #Tinou correctly outlines, you can rename and turn off subscription fields that are generated by #model using the subscriptions arg but you also the ability to create custom subscriptions by adding a Subscription type to your schema.
type Subscription {
customField(arg: String): String #aws_subscribe(mutations:["customPublish"])
}
With this approach, you can add any fields and arguments that you need.

How to write JOIN in graphQL or get result from multiple types - AWS App sync iOS

I am using AWS AppSync for a chat app in one of the my applications. We are able to do setup and basic query successfully.
In one of the case I need to write a customized GraphQL query so that I can have additional data using reference of one type from another. For example, I can have allMessageGroup from a user and also allMessages from a particular group.
Now I want to add the last message in the group and its sender with the list of all message group just like what's app home page.
But I am not able to understand how make JOIN or write such query which give mixed results based on Conversation/Message/User types/table.
Platform:iOS
Language: Swift
For detail below is my Schema and API/Query I am using
Schema
type Conversation {
conversation_cover_pic: String
conversation_type: String!
createdAt: String
id: ID!
messages(after: String, first: Int): MessageConnection
name: String!
privacy: String
}
type Message {
author: User
content: String!
conversationId: ID!
createdAt: String
id: ID!
recipient: User
sender: String
}
type MessageConnection {
messages: [Message]
nextToken: String
}
Query
query getUserConversationConnectionThroughUser($after: String, $first: Int)
{
me
{
id
__typename
conversations(first: $first, after: $after)
{
__typename
nextToken
userConversations
{
__typename
userId
conversationId
associated
{
__typename
userId
}
conversation
{
__typename
id
name
privacy
messages
{
__typename
id
conversationId
content
createdAt
sender
isSent
}
}
}
}
}
}
It sounds like you need multiple requests to one or more datasources to fulfill this graphQL query. In this case, you can use AppSync's pipeline resolver feature.
With pipeline resolvers, you can create multiple functions, each of which can use the results of the previous function and query a database. These functions run in an order you specify.
An example of something you could do with a pipeline resolver:
One function will query the chat group database
A second function will use the results of the chat group to fetch messages
Consolidate all the results into one graphQL response containing group information and messages
Here is the documentation for pipeline resolvers: https://docs.aws.amazon.com/appsync/latest/devguide/pipeline-resolvers.html

RESTful API design advice on getting all A associated with B

I'm trying to design a RESTful API as a side project.
I am also using MongoDB as database
(I'm new to NoSQL design, so I need help, If I have misunderstanding in how documents should be designed).
I have example entities as following:
Event {
id: string
name: string
date: date
location: location
subgroups: group[]
}
Group {
id: string
owners: user[]
members: user[]
parentEvent: event
posts: post[]
}
User {
id: string
Name: string
attendingGroups: group[]
owningGroups: group[]
}
post {
id: string
parentgroup: Group
}
location {
id: string
city: string
}
For above example,
Should I have a designated get call for having all groups associated with the user?
or should I get a user and get the associated groups from the user retrieved?
Depends how you design it. You can embed resources in other resources to save you from N+1 select problem, nothing is against with that.
Hal+json format is the format you should be embedding resources.
In REST you can even have ?_embed=groups parameter to embed or not.
Embedding or not embedding is up to your applications needs, not embedding way = you should design a filter like /groups?user=eralpb to get the groups. Or sub-resources work as well like /users/eralpb/groups should only return my groups.