Home > Net >  AppSync onUpdate subscription not triggered by python requests POST to GraphQL endpoint
AppSync onUpdate subscription not triggered by python requests POST to GraphQL endpoint

Time:12-19

I have created a full stack application using Angular and Amplify using the Tutorial at https://docs.amplify.aws/start/getting-started/data-model/q/integration/angular/. The frontend is successfully creating GraphQL entries through AppSync which triggers the onCreate subscription, but changes to that data from a python lambda function on the backend is not triggering the onUpdate subscription.

body.component.ts

  ngOnInit(): void {
        this.api.ListDestinationFiles().then((event) => {
          // do stuff here... (this works)
        });

        this.update_subscription = <Subscription>(
          this.api.OnUpdateDestinationFileListener(user.username).subscribe((event: any) => {
            // this never fires
            console.log('OnUpdate', event);
          }));
        this.create_subscription = <Subscription>(
          this.api.OnCreateDestinationFileListener(user.username).subscribe((event: any) => {
            // this does fire.
            console.log('OnCreate', event);
          }));
      })
  }

My webpage allows the user to upload a file which triggers this function in the same body.component.ts:

  upload(): void {
    API.post(this.apiName, this.path, this.myInit)
      .then(response => {
        let keys = Object.keys(response['dest_file_urls']);
        this.graphql_api
          .CreateDestinationFile({
            filename: keys[0],
            signedURL: response['dest_file_urls'][keys[0]],
            status: 'Started'
          })
          .then((event) => {
            // this triggers successfully, and the item is indeed created
            console.log('item created', event);
          })

So far everything is great and working as expected. The API.post sends the file to a python lambda function that saves the file to an S3 bucket, triggers a second lambda function to do the heavy lifting of processing the file, and returns a 201 to the client (that's when the 'item created' event occurs).

The second lambda function contains this code:

destination_file = '{id: "'   str(gql_id)   '", filename: "'   d_filename   '", signedURL: "'   signedURL   '", status: "Finished"}'
mutation = 'mutation update {updateDestinationFile(input: '   destination_file   ') { filename status }}'
response = session.request(url=endpoint, method='POST', headers=headers, json={'query': mutation})

The AppSync POST is working just fine, the data is being updated in the backend, and there are no error messages in CloudWatch, Java Console, etc. The only thing that doesn't work is the triggering of the onUpdate subscription where I have code to update the webpage indicating the process is complete.

I've found various articles and documentation about enabling real-time updates for AppSync that include adding or replacing the auto-generated resolver with data source type of NONE and doing a pass-through of sorts, but that appears to be legacy documentation no longer relevant to the latest version of AppSync. My auto-generated resolver for update is a 4-step pipeline handling authentication, updating the "updatedAt" field, etc., and already has the NONE datasource in two of those steps.

Here is the complete Schema as I have a strong inclination this is what I need to modify somehow, although it's being auto-generated and I haven't found clear documentation for making modifications inside the schema.graphql file.

input CreateDestinationFileInput {
    id: ID
    filename: String!
    signedURL: String
    status: String
}

input DeleteDestinationFileInput {
    id: ID!
}

type DestinationFile {
    id: ID!
    filename: String!
    signedURL: String
    status: String
    createdAt: AWSDateTime!
    updatedAt: AWSDateTime!
    owner: String
}

enum ModelAttributeTypes {
    binary
    binarySet
    bool
    list
    map
    number
    numberSet
    string
    stringSet
    _null
}

input ModelBooleanInput {
    ne: Boolean
    eq: Boolean
    attributeExists: Boolean
    attributeType: ModelAttributeTypes
}

input ModelDestinationFileConditionInput {
    filename: ModelStringInput
    signedURL: ModelStringInput
    status: ModelStringInput
    and: [ModelDestinationFileConditionInput]
    or: [ModelDestinationFileConditionInput]
    not: ModelDestinationFileConditionInput
}

type ModelDestinationFileConnection {
    items: [DestinationFile!]!
    nextToken: String
}

input ModelDestinationFileFilterInput {
    id: ModelIDInput
    filename: ModelStringInput
    signedURL: ModelStringInput
    status: ModelStringInput
    and: [ModelDestinationFileFilterInput]
    or: [ModelDestinationFileFilterInput]
    not: ModelDestinationFileFilterInput
}

input ModelFloatInput {
    ne: Float
    eq: Float
    le: Float
    lt: Float
    ge: Float
    gt: Float
    between: [Float]
    attributeExists: Boolean
    attributeType: ModelAttributeTypes
}

input ModelIDInput {
    ne: ID
    eq: ID
    le: ID
    lt: ID
    ge: ID
    gt: ID
    contains: ID
    notContains: ID
    between: [ID]
    beginsWith: ID
    attributeExists: Boolean
    attributeType: ModelAttributeTypes
    size: ModelSizeInput
}

input ModelIntInput {
    ne: Int
    eq: Int
    le: Int
    lt: Int
    ge: Int
    gt: Int
    between: [Int]
    attributeExists: Boolean
    attributeType: ModelAttributeTypes
}

input ModelSizeInput {
    ne: Int
    eq: Int
    le: Int
    lt: Int
    ge: Int
    gt: Int
    between: [Int]
}

enum ModelSortDirection {
    ASC
    DESC
}

input ModelStringInput {
    ne: String
    eq: String
    le: String
    lt: String
    ge: String
    gt: String
    contains: String
    notContains: String
    between: [String]
    beginsWith: String
    attributeExists: Boolean
    attributeType: ModelAttributeTypes
    size: ModelSizeInput
}

type Mutation {
    createDestinationFile(input: CreateDestinationFileInput!, condition: ModelDestinationFileConditionInput): DestinationFile
    updateDestinationFile(input: UpdateDestinationFileInput!, condition: ModelDestinationFileConditionInput): DestinationFile
    deleteDestinationFile(input: DeleteDestinationFileInput!, condition: ModelDestinationFileConditionInput): DestinationFile
}

type Query {
    getDestinationFile(id: ID!): DestinationFile
    listDestinationFiles(filter: ModelDestinationFileFilterInput, limit: Int, nextToken: String): ModelDestinationFileConnection
}

type Subscription {
    onCreateDestinationFile(owner: String): DestinationFile
        @aws_subscribe(mutations: ["createDestinationFile"])
    onUpdateDestinationFile(owner: String): DestinationFile
        @aws_subscribe(mutations: ["updateDestinationFile"])
    onDeleteDestinationFile(owner: String): DestinationFile
        @aws_subscribe(mutations: ["deleteDestinationFile"])
}

input UpdateDestinationFileInput {
    id: ID!
    filename: String
    signedURL: String
    status: String
}

This is all getting created from this in the schema.graphql file:

type DestinationFile @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  filename: String!
  signedURL: String
  status: String
}

CodePudding user response:

Have you by any chance enabled DataStore? If you have then it adds a _version field to all your Models (similar to the updateAt fields you mention). When doing any update in GraphQL you will have to pass through that _version field with the last good value stored for that Model otherwise the update will fail (and so not fire off your update event). But this theory only stands if you've actually enabled DataStore :)

CodePudding user response:

I should have known it would be something silly.

mutation = 'mutation update {updateDestinationFile(input: '   destination_file   ') { filename status }}'

The line above was missing owner from the return set. Since my schema includes:

type DestinationFile @model @auth(rules: [{ allow: owner }])

I have to return owner or the update subscription never see's it.

mutation = 'mutation update {updateDestinationFile(input: '   destination_file   ') { filename status owner }}

The above works

  • Related