aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.43k stars 2.13k forks source link

Subscription not working in app (though it works on console), returning "null" all the time #4832

Closed jitendra-koodo closed 4 years ago

jitendra-koodo commented 4 years ago

Describe the bug

    const subscription = API.graphql(
      graphqlOperation(subscriptions.onCreateBank)
    ).subscribe({
      next: (eventData) => {
        console.log("new" + JSON.stringify(eventData.value.data));
      }
    });

Log always shows new{"onCreateBank":null}

while for the same new created object, console subscription shows

{ "data": { "onCreateBank": { "id": "6abcc124-8e83-4192-a331-de187b660f96", "name": "8abcdef", "__typename": "Bank" } } }

To Reproduce https://aws-amplify.github.io/docs/js/start?ref=amplify-rn-btn&platform=react-native

*Additional context Add any other context about the problem here. Windows 10

"dependencies": { "aws-amplify": "^2.2.2", "aws-amplify-react-native": "^3.2.0", "react": "16.9.0", "react-native": "0.61.5" },

ericclemmons commented 4 years ago

@jitendra-koodo Can you clarify what you mean by the following, or provide a screenshot?

while for the same new created object, console subscription shows

{ "data": { "onCreateBank": { "id": "6abcc124-8e83-4192-a331-de187b660f96", "name": "8abcdef", "__typename": "Bank" } } }

If you've been following along with https://aws-amplify.github.io/docs/js/start?ref=amplify-rn-btn&platform=react-native, could you also provide amplify/backend/api/photoalbumse2e/schema.graphql for us to reproduce with?

jitendra-koodo commented 4 years ago

@ericclemmons : Please find details below.

type Bank @model { id: ID! name: String! logoUrl: AWSURL! popularityScore: Int! dirty: Boolean disabled: Boolean smsCodes: [SmsCode!]! @connection(name: "BankSmsCodes") }

type SmsCode @model { id: ID! code: String! shortCode: String! bank: Bank! @connection(name: "BankSmsCodes") disabled: Boolean }

subs

jitendra-koodo commented 4 years ago

Found the problem/solution with the help of AWS support engineer. In case someone needs help...

Subscription only works in app if returned fields in corresponding mutation match with corresponding returned fields in subscription definition. If this is not the case, subscription would only work on console but not in APIs.

Its not documented anywhere...

ericclemmons commented 4 years ago

@jitendra-koodo I'm glad you got this resolved!

Oddly enough, I've been testing with a local app and had trouble reproducing, for other reasons:

  1. updateAlbum and onUpdateAlbum originally had the same fields returned.
  2. Removing fields from onUpdateAlbum (in src/graphql/subscriptions.js still returned the full payload:
export const onUpdateAlbum = `subscription OnUpdateAlbum($owner: String!) {
  onUpdateAlbum(owner: $owner) {
    # As long as this is valid, it returns the full shape
    id
  }
}
`;
  useEffect(() => {
    const subscription = API.graphql({
      authMode: owner ? 'AMAZON_COGNITO_USER_POOLS' : 'AWS_IAM',
      query: onUpdateAlbum,
      variables: { owner }
    }).subscribe({
      next(payload) {
        console.log(JSON.stringify(payload.value.data.onUpdateAlbum, null, 2));
        setAlbum(payload.value.data.onUpdateAlbum);
      }
    });

    return () => subscription.unsubscribe();
  }, [id, owner]);
{
  "id": "5c010de4-d1b9-4cfd-9e6e-96119cb700f7",
  "name": "Logos3",
  "version": 13,
  "owner": "Google_110029493523186990292",
  "photos": {
    "items": [
      {
        "id": "3dc395fd-e6b8-49e7-b75e-217849de9828",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "triangle",
          "text",
          "label"
        ],
        "description": "Vue Color@2x",
        "version": 7,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "f93cfa3b-9f66-42f1-ac5a-df0a34d207da",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "trademark",
          "logo",
          "symbol",
          "bomb",
          "weapon",
          "weaponry",
          "dynamite",
          "label",
          "text"
        ],
        "description": "aws_logo",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "e1df4619-ad39-4a25-a1e9-b8d4f0b2ee96",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "text",
          "number",
          "symbol",
          "trademark",
          "logo",
          "moon",
          "outdoors",
          "outer space",
          "night",
          "space",
          "nature",
          "universe",
          "astronomy"
        ],
        "description": "Ionic Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "277dc646-3d24-49a1-b5ba-a1e4c77228a2",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "symbol",
          "sign"
        ],
        "description": "Angular Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "a21dd9d0-b805-495a-bace-12949a17d58d",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "symbol",
          "sign"
        ],
        "description": "Angular Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "40ba044b-4e77-4269-a8c1-abcfdb83801f",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "label",
          "text",
          "sticker",
          "silhouette",
          "astronomy",
          "outer space",
          "space",
          "moon",
          "night",
          "outdoors",
          "nature",
          "universe"
        ],
        "description": "GitHub-Mark-120px-plus",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "39c55697-4c83-45fe-a415-6511dbd7a222",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "logo",
          "trademark",
          "symbol"
        ],
        "description": "React Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      }
    ],
    "nextToken": null,
    "__typename": "ModelPhotoConnection"
  },
  "__typename": "Album"
}

From the looks of it, because the schema.graphql is compiled down into schema.json and src/graphql/*, your app will always return the mutated model.

            {
              "name": "onUpdateAlbum",
              "description": null,
              "args": [
                {
                  "name": "owner",
                  "description": null,
                  "type": {
                    "kind": "NON_NULL",
                    "name": null,
                    "ofType": {
                      "kind": "SCALAR",
                      "name": "String",
                      "ofType": null
                    }
                  },
                  "defaultValue": null
                }
              ],
              "type": {
                "kind": "OBJECT",
                "name": "Album",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },

Why it's null in your logging, I still cannot say...

jitendra-koodo commented 4 years ago

@jitendra-koodo I'm glad you got this resolved!

Oddly enough, I've been testing with a local app and had trouble reproducing, for other reasons:

  1. updateAlbum and onUpdateAlbum originally had the same fields returned.
  2. Removing fields from onUpdateAlbum (in src/graphql/subscriptions.js still returned the full payload:
export const onUpdateAlbum = `subscription OnUpdateAlbum($owner: String!) {
  onUpdateAlbum(owner: $owner) {
    # As long as this is valid, it returns the full shape
    id
  }
}
`;
  useEffect(() => {
    const subscription = API.graphql({
      authMode: owner ? 'AMAZON_COGNITO_USER_POOLS' : 'AWS_IAM',
      query: onUpdateAlbum,
      variables: { owner }
    }).subscribe({
      next(payload) {
        console.log(JSON.stringify(payload.value.data.onUpdateAlbum, null, 2));
        setAlbum(payload.value.data.onUpdateAlbum);
      }
    });

    return () => subscription.unsubscribe();
  }, [id, owner]);
{
  "id": "5c010de4-d1b9-4cfd-9e6e-96119cb700f7",
  "name": "Logos3",
  "version": 13,
  "owner": "Google_110029493523186990292",
  "photos": {
    "items": [
      {
        "id": "3dc395fd-e6b8-49e7-b75e-217849de9828",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "triangle",
          "text",
          "label"
        ],
        "description": "Vue Color@2x",
        "version": 7,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "f93cfa3b-9f66-42f1-ac5a-df0a34d207da",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "trademark",
          "logo",
          "symbol",
          "bomb",
          "weapon",
          "weaponry",
          "dynamite",
          "label",
          "text"
        ],
        "description": "aws_logo",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "e1df4619-ad39-4a25-a1e9-b8d4f0b2ee96",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "text",
          "number",
          "symbol",
          "trademark",
          "logo",
          "moon",
          "outdoors",
          "outer space",
          "night",
          "space",
          "nature",
          "universe",
          "astronomy"
        ],
        "description": "Ionic Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "277dc646-3d24-49a1-b5ba-a1e4c77228a2",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "symbol",
          "sign"
        ],
        "description": "Angular Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "a21dd9d0-b805-495a-bace-12949a17d58d",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "symbol",
          "sign"
        ],
        "description": "Angular Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "40ba044b-4e77-4269-a8c1-abcfdb83801f",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "label",
          "text",
          "sticker",
          "silhouette",
          "astronomy",
          "outer space",
          "space",
          "moon",
          "night",
          "outdoors",
          "nature",
          "universe"
        ],
        "description": "GitHub-Mark-120px-plus",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      },
      {
        "id": "39c55697-4c83-45fe-a415-6511dbd7a222",
        "bucket": "photo-albums-e2e3b4b5dc769004b0c88c0195ee05bf4e3-cypress",
        "labels": [
          "logo",
          "trademark",
          "symbol"
        ],
        "description": "React Color@2x",
        "version": 1,
        "owner": "Google_110029493523186990292",
        "__typename": "Photo"
      }
    ],
    "nextToken": null,
    "__typename": "ModelPhotoConnection"
  },
  "__typename": "Album"
}

From the looks of it, because the schema.graphql is compiled down into schema.json and src/graphql/*, your app will always return the mutated model.

            {
              "name": "onUpdateAlbum",
              "description": null,
              "args": [
                {
                  "name": "owner",
                  "description": null,
                  "type": {
                    "kind": "NON_NULL",
                    "name": null,
                    "ofType": {
                      "kind": "SCALAR",
                      "name": "String",
                      "ofType": null
                    }
                  },
                  "defaultValue": null
                }
              ],
              "type": {
                "kind": "OBJECT",
                "name": "Album",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },

Why it's null in your logging, I still cannot say...

@ericclemmons Try removing any non-nullable field.

ericclemmons commented 4 years ago

I had version (as I'm using @versioned) removed in the above test. This is tricky... 🤔

slatemates commented 4 years ago

Facing the same issue in a react web app . @jitendra-koodo had pointed that we need to have the same structure for subscriptions and respective mutations . However it still returns null .

ericclemmons commented 4 years ago

@slatemates Can you share your schema, the query for your subscription, and the code that you're using for making the call (e.g. API.graphql(...))?

I was unable to reproduce this (https://github.com/aws-amplify/amplify-js/issues/4832#issuecomment-582111032) and was still getting the full response back rather than null (even with required fields removed).

slatemates commented 4 years ago

This is the code that we are using on the client App.

import React, { useState, useEffect } from "react"; import {listTeachers} from './graphql/queries' import {onCreateTeacher} from './graphql/subscriptions' import {onCreateWallet} from './graphql/subscriptions' import {mutations, updateTeacher} from './graphql/mutations' import {subscriptions} from './graphql/subscriptions' import awsconfig from './aws-exports'; import Amplify, { API, graphqlOperation } from 'aws-amplify'; import Teacher from './Teacher'

Amplify.configure(awsconfig);

function TeacherList () { const [teachers, setTeachers] = useState([]); useEffect(() => { const getTeachersList = async () => { const result = await API.graphql(graphqlOperation(listTeachers)); setTeachers( result.data.listTeachers.items ); };

    getTeachersList();
  }, []);

  useEffect(() => {
    const teacherSubscription = API.graphql(
      graphqlOperation(onCreateTeacher)
    ).subscribe({
        next: data => {
       //   const { value: { data: {onCreateTeacher} }} = data
      console.log(JSON.stringify(data.value.data.onCreateTeacher));

        }
    })

    const walletSubscription = API.graphql(
      graphqlOperation(onCreateWallet)
    ).subscribe({
        next: dataWallet => {
       //   const { value: { data: {onCreateTeacher} }} = data
       console.log(JSON.stringify(dataWallet.value.data.onCreateWallet));

        }
    })
    return () => {
      teacherSubscription.unsubscribe();
      walletSubscription.unsubscribe();
    }

  }, [teachers])

return(

    <div>
        Teacher List
    <h1>
    {

teachers.map(teacher =>(

<Teacher key = {teacher.id} {...teacher} ></Teacher>
))        

    }
    </h1>

    </div>
    );

}

export default TeacherList

Schema.graphql

type Wallet @model { id: ID! amount: Float createdAt: String updatedAt: String }

type Teacher @model { id: ID! name: String! teacherUserName: String!

}

Subscriptions..:

export const onCreateWallet = / GraphQL / subscription OnCreateWallet { onCreateWallet { id amount createdAt updatedAt } } ;

export const onCreateTeacher = / GraphQL / ` subscription OnCreateTeacher { onCreateTeacher { id name teacherUserName

}

} `;

slatemates commented 4 years ago

We have tested by pushing an entirely new api with the above schema . Amplify CLI Version (4.13.4) and aws-amplify(2.2.5) . We are getting null for the "teacherSubscription" whereas we are getting just the "id" for the "wallet subscription ".

Output :

null

{"amount":null,"createdAt":null,"id":"41689a23-2318-4df0-b06d-39372c4f6451","updatedAt":null}

slatemates commented 4 years ago

Also I forgot to mention, that we dont have any @auth transformers mentioned on the graphql schema . The subscription seems to work on appsync console, where "id" and "_type" are returned

slatemates commented 4 years ago

The problem seems to be resolved presently. We are able to get the data on the client web app. There was no change in front end code, schema . However, now on the app console the subscription is returning the complete data instead of just the "id" and the "_type" fields as reported earlier

slatemates commented 4 years ago

The appsync/amplify subscription appears to be inconsistent . Sometimes we are able to get data on the frontend , at other times we simply get a null object.

slatemates commented 4 years ago

It seems to working though in case the mutation returns the same structure as is expected in the subscription else the fields are returned null

rahul-nath commented 4 years ago

I don't like this solution, although it works. What if you make updates to fields as an admin and would like only some of the updated information propagated to real time users? This could be a security risk as a nefarious user would simply inspect their network tab to find compromising information if the data updated is not their own. Amplify needs to document this and change it, it's not the GraphQL way.

sunilb6 commented 4 years ago

Remove the non-nullable field and then try to fetch it will work

My Code


function App() {

//brought from src/graphql/subscriptions.js | Copy & Paste then Remove the fields one by one then test

const onCreateUser = /* GraphQL */ `
  subscription OnCreateUser {
    onCreateUser {
      name

    }
  }
`;

  async function fetchUsers(){
      try{
    const subscription = API.graphql(graphqlOperation(onCreateUser)
    ).subscribe({
        next: (todoData) => {setUsersOnline(...todoData.value.data.onCreateUser.name)}
    });
  }
  catch(err){console.log(err)}
}

  return (
    <div>
        Messages : {messages.length} Users: {usersOnline}
    </div>
    );
}

export default App;
cybercussion commented 3 years ago

Is there a fix for this? I had to just hack a refresh for now. Always comes back null.

sunilb6 commented 3 years ago

idk :(

github-actions[bot] commented 2 years ago

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels or Discussions for those types of questions.