awslabs / aws-mobile-appsync-sdk-js

JavaScript library files for Offline, Sync, Sigv4. includes support for React Native
Apache License 2.0
916 stars 265 forks source link

Subscriptions stop working in nodejs after update to 3.0.2 from 2.0.2 #493

Open philiiiiiipp opened 4 years ago

philiiiiiipp commented 4 years ago

Do you want to request a feature or report a bug? Possibly a bug

What is the current behavior? Prior to version 3.0.2 ( last known working version is 2.0.2) I could use subscriptions in nodejs in our app. Mostly using this guide https://docs.aws.amazon.com/appsync/latest/devguide/building-a-client-app-node.html

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.

After updating to 3.0.2 subscriptions give me null errors

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
    Error: Uncaught [Error: GraphQL error: Cannot return null for non-nullable type: 'AWSDateTime' within parent 'EventContactEmail' (/mutatedActivity/receivedAt)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/mailboxes)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/from)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/to)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/cc)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/bcc)
    GraphQL error: Cannot return null for non-nullable type: 'Boolean' within parent 'EventContactEmail' (/mutatedActivity/hasMore)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/attachments)
    GraphQL error: Cannot return null for non-nullable type: 'EmailDeliveryStatus' within parent 'EventContactEmail' (/mutatedActivity/deliveryStatus)]
        at reportException (/Users/philipp/node/dev/dh/sfa-api/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
        at Timeout.callback [as _onTimeout] (/Users/philipp/node/dev/dh/sfa-api/node_modules/jsdom/lib/jsdom/browser/Window.js:680:7)
        at ontimeout (timers.js:436:11)
        at tryOnTimeout (timers.js:300:5)
        at listOnTimeout (timers.js:263:5)
        at Timer.processTimers (timers.js:223:10) { Error: GraphQL error: Cannot return null for non-nullable type: 'AWSDateTime' within parent 'EventContactEmail' (/mutatedActivity/receivedAt)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/mailboxes)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/from)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/to)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/cc)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/bcc)
    GraphQL error: Cannot return null for non-nullable type: 'Boolean' within parent 'EventContactEmail' (/mutatedActivity/hasMore)
    GraphQL error: Cannot return null for non-nullable type: 'null' within parent 'EventContactEmail' (/mutatedActivity/attachments)
    GraphQL error: Cannot return null for non-nullable type: 'EmailDeliveryStatus' within parent 'EventContactEmail' (/mutatedActivity/deliveryStatus)
        at new ApolloError (/Users/philipp/node/dev/dh/sfa-api/node_modules/src/errors/ApolloError.ts:56:5)
        at /Users/philipp/node/dev/dh/sfa-api/node_modules/src/core/QueryManager.ts:925:19
        at Array.forEach (<anonymous>)
        at Object.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/src/core/QueryManager.ts:916:23)
        at notifySubscription (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:130:18)
        at onNotify (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:165:3)
        at SubscriptionObserver.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:219:7)
        at notifySubscription (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:130:18)
        at onNotify (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:165:3)
        at SubscriptionObserver.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:219:7)
        at notifySubscription (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:130:18)
        at onNotify (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:165:3)
        at SubscriptionObserver.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:219:7)
        at Object.RetryableOperation.onNext [as next] (/Users/philipp/node/dev/dh/sfa-api/node_modules/apollo-link-retry/src/retryLink.ts:137:16)
        at notifySubscription (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:130:18)
        at onNotify (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:165:3)
        at SubscriptionObserver.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:219:7)
        at notifySubscription (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:130:18)
        at onNotify (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:165:3)
        at SubscriptionObserver.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:219:7)
        at notifySubscription (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:130:18)
        at onNotify (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:165:3)
        at SubscriptionObserver.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:219:7)
        at Object.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:340:22)
        at notifySubscription (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:130:18)
        at onNotify (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:165:3)
        at SubscriptionObserver.next (/Users/philipp/node/dev/dh/sfa-api/node_modules/zen-observable/lib/Observable.js:219:7)
        at AppSyncRealTimeSubscriptionHandshakeLink.Object.<anonymous>.AppSyncRealTimeSubscriptionHandshakeLink._handleIncomingSubscriptionMessage (/Users/philipp/node/dev/dh/sfa-api/node_modules/aws-appsync-subscription-link/lib/realtime-subscription-handshake-link.js:623:26)
        at WebSocket.onMessage (/Users/philipp/node/dev/dh/sfa-api/node_modules/ws/lib/event-target.js:120:16)
        at WebSocket.emit (events.js:198:13)
        at WebSocket.EventEmitter.emit (domain.js:448:20)
        at Receiver.receiverOnMessage (/Users/philipp/node/dev/dh/sfa-api/node_modules/ws/lib/websocket.js:800:20)
        at Receiver.emit (events.js:198:13)
        at Receiver.EventEmitter.emit (domain.js:448:20)
        at Receiver.dataMessage (/Users/philipp/node/dev/dh/sfa-api/node_modules/ws/lib/receiver.js:423:14)
        at Receiver.getData (/Users/philipp/node/dev/dh/sfa-api/node_modules/ws/lib/receiver.js:353:17)
        at Receiver.startLoop (/Users/philipp/node/dev/dh/sfa-api/node_modules/ws/lib/receiver.js:139:22)
        at Receiver._write (/Users/philipp/node/dev/dh/sfa-api/node_modules/ws/lib/receiver.js:74:10)
        at doWrite (_stream_writable.js:415:12)
        at writeOrBuffer (_stream_writable.js:399:5)
        at Receiver.Writable.write (_stream_writable.js:299:11)
        at TLSSocket.socketOnData (/Users/philipp/node/dev/dh/sfa-api/node_modules/ws/lib/websocket.js:875:35)
        at TLSSocket.emit (events.js:198:13)
        at TLSSocket.EventEmitter.emit (domain.js:448:20)
        at addChunk (_stream_readable.js:288:12)
        at readableAddChunk (_stream_readable.js:269:11)
        at TLSSocket.Readable.push (_stream_readable.js:224:10)
        at TLSWrap.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
      graphQLErrors:
       [ { message:
            'Cannot return null for non-nullable type: \'AWSDateTime\' within parent \'EventContactEmail\' (/mutatedActivity/receivedAt)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/mailboxes)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/from)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/to)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/cc)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/bcc)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'Boolean\' within parent \'EventContactEmail\' (/mutatedActivity/hasMore)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/attachments)',
           path: [Array] },
         { message:
            'Cannot return null for non-nullable type: \'EmailDeliveryStatus\' within parent \'EventContactEmail\' (/mutatedActivity/deliveryStatus)',
           path: [Array] } ],
      networkError: null,
      message:
       'GraphQL error: Cannot return null for non-nullable type: \'AWSDateTime\' within parent \'EventContactEmail\' (/mutatedActivity/receivedAt)\nGraphQL error: Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/mailboxes)\nGraphQL error: Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/from)\nGraphQL error: Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/to)\nGraphQL error: Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/cc)\nGraphQL error: Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/bcc)\nGraphQL error: Cannot return null for non-nullable type: \'Boolean\' within parent \'EventContactEmail\' (/mutatedActivity/hasMore)\nGraphQL error: Cannot return null for non-nullable type: \'null\' within parent \'EventContactEmail\' (/mutatedActivity/attachments)\nGraphQL error: Cannot return null for non-nullable type: \'EmailDeliveryStatus\' within parent \'EventContactEmail\' (/mutatedActivity/deliveryStatus)',
      extraInfo: undefined }

This happens with ws set to 3.3.1 like described in the aws docs but also when I update it to the latest version. Did anything change with the version bump ?

Which versions and which environment (browser, react-native, nodejs) / OS are affected by this issue? Did this work in previous versions?

Environment: nodejs v10.16.0 OS: OSX Did this work in previous versions ? Yes, I am running exactly the same tests, without updating I get the correct subscription response, after updating I get the error.

pickfire commented 4 years ago

Same for subscriptions in ionic, it does not work as well after upgrading from 2.0.2 to 3.0.2.

mikedizon commented 4 years ago

@philiiiiiipp can you show us how you're configuring your client? are you using ApolloClient or AWSAppSyncClient?

philiiiiiipp commented 4 years ago

@mikedizon sure, no I do not. I am using the AWSAppsync client directly. It looks something like this:

import Auth from '@aws-amplify/auth';

// @ts-ignore
global.WebSocket = require('ws');
// @ts-ignore
global.window = global.window || {
  setTimeout: setTimeout,
  clearTimeout: clearTimeout,
  // @ts-ignore
  WebSocket: global.WebSocket,
  ArrayBuffer: global.ArrayBuffer,
  // @ts-ignore
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  addEventListener: function() {},
  navigator: { onLine: true },
};
// @ts-ignore
global.localStorage = {
  store: {},
  getItem: function(key) {
    return this.store[key];
  },
  setItem: function(key, value) {
    this.store[key] = value;
  },
  removeItem: function(key) {
    delete this.store[key];
  },
};
require('isomorphic-fetch');

const client = new AWSAppSyncClient({
  disableOffline: true,
  url: AmplifyConfig.aws_appsync_graphqlEndpoint,
  region: AmplifyConfig.aws_appsync_region,
  auth: {
    type: AmplifyConfig.aws_appsync_authenticationType,
    credentials: () => Auth.currentCredentials(),
  },
});
mikedizon commented 4 years ago

@philiiiiiipp try adding Auth.configure(AmplifyConfig): after you import your config file.

philiiiiiipp commented 4 years ago

I added this it, it did not change it. I already had

await Auth.signIn(email, password);
const cred = await Auth.currentCredentials();
AWS.config.credentials = Auth.essentialCredentials(cred);

It seems as it the issue is only happening in one of two subscription endpoints. One of them ( the working one ) only ever returns the same Graphql type, the other one is returning multiple ones implementing an interface, could it have anything to do with this ?

mikedizon commented 4 years ago

@philiiiiiipp something like this?


import Auth from '@aws-amplify/auth';

>>>> Auth.configure(AmplifyConfig);

// @ts-ignore
global.WebSocket = require('ws');
// @ts-ignore
global.window = global.window || {
  setTimeout: setTimeout,
  clearTimeout: clearTimeout,
  // @ts-ignore
  WebSocket: global.WebSocket,
  ArrayBuffer: global.ArrayBuffer,
  // @ts-ignore
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  addEventListener: function() {},
  navigator: { onLine: true },
};
// @ts-ignore
global.localStorage = {
  store: {},
  getItem: function(key) {
    return this.store[key];
  },
  setItem: function(key, value) {
    this.store[key] = value;
  },
  removeItem: function(key) {
    delete this.store[key];
  },
};
require('isomorphic-fetch');

const client = new AWSAppSyncClient({
  disableOffline: true,
  url: AmplifyConfig.aws_appsync_graphqlEndpoint,
  region: AmplifyConfig.aws_appsync_region,
  auth: {
    type: AmplifyConfig.aws_appsync_authenticationType,
    credentials: () => Auth.currentCredentials(),
  },
});

Presumably, your AmplifyConfig file looks something like


const awsmobile = {
    "aws_project_region": "us-east-1",
    "aws_cognito_identity_pool_id": "us-east-1:xxx",
    "aws_cognito_region": "us-east-1",
    "aws_user_pools_id": "us-east-xxx",
    "aws_user_pools_web_client_id": "xxx",
    "oauth": {},
    "aws_appsync_graphqlEndpoint": "https://xxx.appsync-api.us-east-1.amazonaws.com/graphql",
    "aws_appsync_region": "us-east-1",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS"
};
philiiiiiipp commented 4 years ago

Yes, exactly, I added Auth.configure(AmplifyConfig); after importing the Auth. For the AmplifyConfig we use AWS_IAM as the aws_appsync_authenticationType and are in eu-west-1.

I am wondering if this is an auth problem, since it does seem as if it makes a successful connection to all subscription endpoints but the moment I get data, one of the two gets this error after upgrading to 3.0.2

mikedizon commented 4 years ago

@philiiiiiipp You’ve added iam to your schema too right?

philiiiiiipp commented 4 years ago

Yes, here is the part of the CF entry:

AppSync:
        Type: AWS::AppSync::GraphQLApi
        Properties:
          Name: testappsyncname
          AuthenticationType: AWS_IAM

It also works flawlessly before upgrading to 3.0.2.

mikedizon commented 4 years ago

@philiiiiiipp

Relevant portions of my package.json

"devDependencies": {
    "@apollo/react-hooks": "^3.1.3",
    "@aws-amplify/api": "^2.1.2",
    "@aws-amplify/pubsub": "^2.1.5",
    "amazon-cognito-identity-js": "^3.2.4",
    "apollo-boost": "^0.4.7",
    "apollo-cache-inmemory": "^1.6.5",
    "apollo-link-http": "^1.5.16",
    "aws-amplify": "^2.2.4",
    "aws-amplify-react": "^3.1.2",
    "aws-amplify-react-native": "^3.2.2",
    "aws-appsync": "^3.0.2",
    "aws-appsync-react": "^3.0.2",
    "react-apollo": "^3.1.3",
  },
  "dependencies": {
    "@apollo/client": "^3.0.0-beta.37",
  }
philiiiiiipp commented 4 years ago

Right, I am not using apollo or react, just nodejs

const client = new AWSAppSyncClient({
    disableOffline: true,
    url: AmplifyConfig.aws_appsync_graphqlEndpoint,
    region: AmplifyConfig.aws_appsync_region,
    auth: {
      type: AmplifyConfig.aws_appsync_authenticationType,
      credentials: () => Auth.currentCredentials(),
    },
  });

  const hydrated = client.hydrated();

  hydrated
    .subscribe({
      fetchPolicy: 'no-cache',
      query: gql`
        subscription MutatedEndpoint($id: ID!) {
          mutatedEndpoint(id: $id) {
            ...somefields
          }
        }
      `,
      variables: {
        ...somevariables,
      },
    })
    .subscribe({
      /** Invoked when new data comes in */
      next: (...newItems) => console.log(newItems),
      onError: (...error) => console.log(error),
    });

The only dependency I have is aws-appsync: 3.0.2.

mikedizon commented 4 years ago

@philiiiiiipp are you using a the custom @key directive on your model?

philiiiiiipp commented 4 years ago

Yes we have the subscription directive. We do not use any of the others.

  mutatedEndpoint(id: ID!): SomeEntity
    @aws_subscribe(mutations: ["someEndpoint"])
pickfire commented 4 years ago

We also use aws_subscribe, maybe that is why subscription broke for us in 3.0.2? If you want the code, can check in https://github.com/ctiteam/apspace/blob/master/src/app/graphql.module.ts and https://github.com/ctiteam/apspace/blob/master/src/graphql/schema.gql.

mikedizon commented 4 years ago

@philiiiiiipp one issue I ran into was that the shape of the onUpdate function was not the same as the onSubscribe function.

veloware commented 4 years ago

i think I have run into the same issue, though I am able to get the same error in the AWS AppSync console, so it may not be related to this library?. My scenario is when I have mutation, say addPost(...): Post, and Post could be something simple like -

type Post {
   id: ID!
   text: String!
}

and a mutation -

addPost(...): Post

if my subscription looks like -

postAdded: Post @aws_subscribe(mutations: ["addPost"])

and I run the following in the AWS console -

subscription {
   postAdded {
      id,
      text
   }
}

I get the error Cannot return null for non-nullable type: \'ID\' within parent \'Post\' as soon as I try and run the subscription in the AWS console

however If i make id within Post optional, everything works