aws-amplify / docs

AWS Amplify Framework Documentation
https://docs.amplify.aws
Apache License 2.0
482 stars 1.04k forks source link

Amplify PubSub - Production builds do not open websocket #7554

Open npellegrin opened 1 year ago

npellegrin commented 1 year ago

Before opening, please confirm:

JavaScript Framework

Vue

Amplify APIs

PubSub

Amplify Categories

Not applicable

Environment information

``` # Put output below this line ```

Describe the bug

This may be related to aws-amplify/amplify-js#10829. It could be related to my implementation of the Amplify initialization in Quasar.

When using the PubSub module, websockets are not opened by the framework with production builds. There is no error message. Development builds works properly.

Expected behavior

Websockets should be opened on both development build and production builds.

Reproduction steps

I have a Vue.js application, built with Vite and Quasar. Versions are:

  "dependencies": {
    "@aws-amplify/ui-vue": "^3.1.7",
    "@quasar/extras": "^1.0.0",
    "aws-amplify": "^5.0.14",
    "graphql-tag": "^2.12.6",
    "pinia": "^2.0.28",
    "quasar": "^2.6.0",
    "vue": "^3.2.45",
    "vue-router": "^4.1.6"
  },
  "devDependencies": {
    "@quasar/app-vite": "^1.0.0",
    "autoprefixer": "^10.4.2",
    "eslint": "^8.10.0",
    "eslint-config-prettier": "^8.1.0",
    "eslint-plugin-vue": "^9.0.0",
    "postcss": "^8.4.14",
    "prettier": "^2.5.1"
  },

I setup Amplify during the Quasar boot and the Amplify init looks like this:

import { boot } from "quasar/wrappers";

import { Amplify } from "aws-amplify";
import { AWSIoTProvider } from "@aws-amplify/pubsub/lib/Providers";

export default boot(() => {
  Amplify.configure({
    Auth: {
      identityPoolId: '',
      region: '',
      identityPoolRegion: '',
      userPoolId: '',
      userPoolWebClientId: '',
      mandatorySignIn: true,
    }
  });

  Amplify.addPluggable(
    new AWSIoTProvider({
      aws_pubsub_region: '....',
      aws_pubsub_endpoint: '....'
    })
  );
});

And the subscription is performed like this:

PubSub.subscribe('my-topic').subscribe({
  next: (data) => handleNotification(data),
  error: (error) => console.error(error),
  close: () => console.debug("Subscription closed"),
  complete: () => console.debug("Subscription complete"),
});

Also, I have in my index.html and quasar.config.js the magic lines to make Amplify work with production packaging:

index.html

    <script>
      window.global = window;
      var exports = {};
    </script>

quasar.config.js:

      extendViteConf(viteConf, { isClient, isServer }) {
        Object.assign(viteConf.resolve.alias, {
          "./runtimeConfig": "./runtimeConfig.browser",
        });

When testing the application with quasar dev, the subscription is performed. With a production build, the websocket request is never opened by the web browser, but no error message is shown.

Additional information and screenshots

With Amplify.Logger.LOG_LEVEL = 'DEBUG' activated, production builds have no reference to MqttOverWSProvider in the logs, when dev builds properly show the MqttOverWSProvider, and it work.

I found a possible workaround, it seems that adding the AWSIoTProvider pluggable directly using the PubSub API instead of using the Amplify API API works:

  PubSub.addPluggable(
    new AWSIoTProvider({
      aws_pubsub_region: '....',
      aws_pubsub_endpoint: '....'
    })
  );
npellegrin commented 1 year ago

I managed to put a breakpoint in the Amplify.addPluggable function call at runtime, and the bug seems to be related to the _components list of the Amplify object not having a PubSub object instance. This explain why the addPluggable instruction is never forwarded to PubSub component, but I did not managed to understand how the _component should have been populated at this step.

Below, the production-minified code of the Amplify function, with some watchers : image The screenshot correspond to this line is source code: https://github.com/aws-amplify/amplify-js/blob/e1b0b5be3e8ccb3c76e8e2e2f43f910d40d73254/packages/core/src/Amplify.ts#L80

npellegrin commented 1 year ago

Ok, I found it... The _componentsattribute is populated when register() function is called in the Amplify object. This call is performed automatically uppon import, at : https://github.com/aws-amplify/amplify-js/blob/5147c5ce555d042722e2888fc423430f517b91b7/packages/pubsub/src/PubSub.ts#L177

This import is clearly specified in the documentation, and the initialization should be performed as this: image

However, following strictly this documentation will not work in production builds, because minifiers will drop the unused PubSub import because it is actually never used in my boot() function (it is only used when performing subscribe later in the app), consequently, the Amplify.addPluggable fail silently and nothing is really plugged to the PubSub module.

I fixed my app by forcing the registration before calling addPluggable, or artificially using the PubSub module in my boot function. Below the both working hacks:

  PubSub.getModuleName();
  Amplify.addPluggable(
    new AWSIoTProvider({
      aws_pubsub_region: '',
      aws_pubsub_endpoint: ''
    })
  );
  Amplify.register(PubSub);
  Amplify.addPluggable(
    new AWSIoTProvider({
      aws_pubsub_region: '',
      aws_pubsub_endpoint: ''
    })
  );

I guess the documentation should be updated to warn about this side effect, or the behavior of addPluggable may be improved to prevent minifiers missing the register() function call before Amplify.addPluggable.

EandrewJones commented 1 year ago

Thanks for reporting this @npellegrin and digging into the problem/solution.

I am encountering the same issue with vite production build and react. The above fix(es) work for me as well.

versions

"aws-amplify": "^5.2.2"
"vite": "^3.2.3"
cwomack commented 4 months ago

Moving this issue to the amplify-docs repo. There needs to be updates to both the v5 and the v6 code examples for PubSub based on the workarounds and details in this issue.