nativescript-angular icon indicating copy to clipboard operation
nativescript-angular copied to clipboard

"Error calling module function" with WebSocketLink (apollo-link-ws)

Open darkbasic opened this issue 7 years ago • 11 comments

https://github.com/darkbasic/nativescript-repro2/blob/master/app/app.module.ts

    const subscriptionLink = new WebSocketLink({
      uri: 'ws://localhost:3000/subscriptions',
      options: {
        reconnect: true,
      },
      webSocketImpl: SocketIO,
    });

3

Full Error: https://paste.ubuntu.com/p/NrffmYWRDJ/

Removing the previous snippet of code fixes the error.

I think it could be related to https://github.com/NativeScript/nativescript-angular/issues/1284

darkbasic avatar Apr 16 '18 17:04 darkbasic

Hi @darkbasic, The apollo-link-ws plugin seems not to be supported in NativeScript due to some DOM related dependencies. For using WebSocket's in NativeScript, you can use nativescript-websockets plugin.

tsonevn avatar Apr 17 '18 12:04 tsonevn

Hi @tsonevn

    const subscriptionLink = new WebSocketLink({
      uri: 'ws://localhost:3000/subscriptions',
      options: {
        reconnect: true,
      },
      webSocketImpl: SocketIO,
    });

As you can see from the previous code snippet I'm using SocketIO as webSocketImpl. I also tried with nativescript-websockets without luck.

I cannot use plain nativescript-websockets (I still need to pass through WebSocketLink) because the websocket will be used for Graphql subscriptions.

darkbasic avatar Apr 17 '18 14:04 darkbasic

export * from './server'; is the culprit: https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/index.ts

Even if we don't use nor even import SubscriptionServer, this is enough to trigger the error. Commenting out export * from './server'; from subscriptions-transport-ws is enough to fix the issue, but this is not a solution I can pursue with the upstream, because they need to export it for node.js users.

If they'll accept such a PR I could export ony the client in a different namespace like subscriptions-transport-ws/client, but this is a workaround. Why does Nativescript trigger errors for a class which I didn't even import?

darkbasic avatar Apr 30 '18 15:04 darkbasic

Not directly an angular issue but more of an apollo-link-ws issue. At any rate, this can be worked around using webpack's NormalModuleReplacementPlugin to swap in nativescript-websockets for /^ws$/. Not perfect, but it beats hacking apart a copy of apollo-link-ws.

parktheredcar avatar Aug 14 '19 09:08 parktheredcar

@darkbasic

Thanks for sharing. One question: which websocket implementation did you end up using? SocketIO, nativescript or the default?

tafelnl avatar Nov 21 '19 17:11 tafelnl

HI recently I have the same probleme, Im trying to implement, realtime with apollo client using angular, and at configure the subscriptions, getting ERROR, there is solution to this problem. I,m lost :(, how alternative i try to use https://market.nativescript.org/plugins/nativescript-subscriptions-transport-ws, but i didnt had success.

Richard095 avatar Nov 25 '19 09:11 Richard095

@Richard095 Hey, did you found a solution ?

dudipsh avatar Jun 27 '20 10:06 dudipsh

@Richard095 Hey, did you found a solution ?

Hi, No, by time issues, i decided to change to SocketIO,It was a good option on Real-Time.

Richard095 avatar Jun 28 '20 02:06 Richard095

This seems to be the only way;)

Apparently yes, I had that problem a long time ago, however I think SocketIO is also going well. Only in this way was I able to continue with my project.

Richard095 avatar Jun 28 '20 07:06 Richard095

@Richard095, @darkbasic, @NickIliev Half a year later ... It works for me!!

webpack.config.js

 plugins: [
      // Define useful constants like TNS_WEBPACK
      new webpack.NormalModuleReplacementPlugin(
          /^ws$/,
          'nativescript-websockets'
      ),

main.ts

const WS = require('nativescript-websockets');
var WebSocket = require('nativescript-websockets');

graphql.module.ts

import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
import {NativeScriptHttpClientModule, NativeScriptModule} from "@nativescript/angular";

import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpHeaders} from '@angular/common/http';

// @ts-ignore
import {Apollo, APOLLO_OPTIONS, ApolloModule} from 'apollo-angular';
import {Subject} from 'rxjs';
import {WebSocketLink} from "@apollo/client/link/ws";

import {RouterModule} from "@angular/router";
import {tap} from "rxjs/internal/operators";

import {getMainDefinition} from '@apollo/client/utilities';

const uri = 'http://192.168.1.21:3000/graphql';
const GRAPHQL_ENDPOINT = 'ws://192.168.1.21:3000/graphql';


const errorSubject = new Subject<any>();
const getErrorSubject = () => errorSubject.asObservable();
import {HttpLink} from 'apollo-angular/http';

import {ApolloLink, InMemoryCache, split} from '@apollo/client/core';
import {onError} from "@apollo/client/link/error";
import {RetryLink} from "@apollo/client/link/retry";

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJkdWRpcHNoQGdtYWlsLmNvbSIsInZlcmlmaWVkIjp0cnVlLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJERUZBVUxUX1JPTEVfQURNSU4iLCJkZXNjcmlwdGlvbiI6bnVsbCwicmVhZCI6WyIyMTQ3NDE4MDc1IiwiOCJdLCJ1cGRhdGUiOlsiMjE0NzQ4MzYwMSIsIjgiXSwiZGVzdHJveSI6WyIyMTQ3NDE4MDc1IiwiOCJdLCJyZWFkT3RoZXIiOlsiMjE0NzQ4MzYwMSIsIjgiXSwidXBkYXRlT3RoZXIiOlsiMjE0NzQxODA3NSIsIjgiXSwiZGVzdHJveU90aGVyIjpbIjIxNDc0MTgwNzUiLCI4Il0sImFwcGx5T25PdGhlcnMiOmZhbHNlLCJyb2xlTGV2ZWwiOjR9XSwibGVnYWxOYW1lIjoiRHVkaSBQYXJ0dXNoIiwiYWNjb3VudElkIjoxLCJhY2Nlc3NMZXZlbCI6NCwiYnVja2V0TmFtZSI6Ii90aW1lLXRhbGstYnVja2V0cy1jb21wYW55LXRlc3Rjb21wMSIsImVtcGxveWVlQ2hhdElkIjoiNjAzMjIxY2JkZWE5MjM4OGY5OTFiODI4IiwiY2hhdEFwcElkIjoiNjAzMjIxY2JkZWE5MjM4OGY5OTFiODI1IiwiY2hhdEJvdElkIjoiNjAzMjIxY2JkZWE5MjM4OGY5OTFiODI2IiwibXlDaGFubmVsSWQiOiI2MDMyMjFjY2RlYTkyMzg4Zjk5MWI4MmMiLCJpYXQiOjE2MTM5MDQ2NTIsImV4cCI6MTYxNDUwOTQ1Mn0.ISt0akAnQn6Adjd-nYXHcyapRT7zAwkcBIvz3s31gGs";

const HTTP_STATUS = {
    NO_CONNECTION: 0,
    INTERNAL_SERVER_ERROR: 500,
    SERVICE_UNAVAILABLE: 503,
    GATEWAY_TIMEOUT: 504,
    BAD_GATEWAY: 502,
    UNAUTHORIZED: 401,
    FORBIDDEN: 403,
    OK: 200,
    CREATED: 201,
};
@NgModule({
    imports: [
        RouterModule,
        NativeScriptHttpClientModule,
    ],
    providers: [],
    schemas: [
        NO_ERRORS_SCHEMA
    ]

})


export class GraphQLModule {
    constructor(
        apollo: Apollo,
        httpLink: HttpLink,
    ) {
        const http = httpLink.create({
            uri
        });
        const error = onError(({networkError, graphQLErrors}) => {
            console.log('ERROR@@@@')
            console.log(networkError)
            console.log(graphQLErrors)
        })

        const wsClient = new WebSocketLink({
            uri: 'ws://192.168.1.21:3000/graphql',
            options: {
                reconnect: true,
                connectionParams: {
                    authorization: token,
                },
            },
            connectionParams: async () => {
                return {
                    authorization: token
                };
            },
            webSocketImpl: WebSocket,
            reconnectionAttempts: 99,
        });
        const retryLink = new RetryLink({
            delay: {
                initial: 1000,
                max: Infinity,
            },
            attempts: {
                max: 10,
                retryIf: (error) => {
                    console.log(error)
                    return error.status === HTTP_STATUS.SERVICE_UNAVAILABLE ||
                        error.status === HTTP_STATUS.GATEWAY_TIMEOUT
                }
            },
        });
        const auth = new ApolloLink((operation, forward) => {
            operation.setContext({
                headers: new HttpHeaders().set('authorization', `${token}`),
            });

            return forward(operation);
        });

        const link = split(
            ({query}) => {
                const {kind, operation}: any = getMainDefinition(query);
                return kind === 'OperationDefinition' && operation === 'subscription';
            },
            wsClient,
            auth.concat(http)
        );
        getErrorSubject().subscribe((data) => {
            console.log(data)

        }, (error) => console.log(error));

        apollo.create({
            link: ApolloLink.from([retryLink, link]),
            cache: new InMemoryCache({
                addTypename: false,
                dataIdFromObject: (object: any) => object.id
            }),
            defaultOptions: {
                watchQuery: {
                    errorPolicy: 'all'
                }
            }
        });


    }
}




`

dudipsh avatar Feb 23 '21 08:02 dudipsh

@Richard095, @darkbasic, @NickIliev Half a year later ... It works for me!!

main.ts

const WS = require('nativescript-websockets');
var WebSocket = require('nativescript-websockets');

graphql.module.ts

import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
import {NativeScriptHttpClientModule, NativeScriptModule} from "@nativescript/angular";

import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpHeaders} from '@angular/common/http';

// @ts-ignore
import {Apollo, APOLLO_OPTIONS, ApolloModule} from 'apollo-angular';
import {Subject} from 'rxjs';
import {WebSocketLink} from "@apollo/client/link/ws";

import {RouterModule} from "@angular/router";
import {tap} from "rxjs/internal/operators";

import {getMainDefinition} from '@apollo/client/utilities';

const uri = 'http://192.168.1.21:3000/graphql';
const GRAPHQL_ENDPOINT = 'ws://192.168.1.21:3000/graphql';


const errorSubject = new Subject<any>();
const getErrorSubject = () => errorSubject.asObservable();
import {HttpLink} from 'apollo-angular/http';

import {ApolloLink, InMemoryCache, split} from '@apollo/client/core';
import {onError} from "@apollo/client/link/error";
import {RetryLink} from "@apollo/client/link/retry";

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJkdWRpcHNoQGdtYWlsLmNvbSIsInZlcmlmaWVkIjp0cnVlLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJERUZBVUxUX1JPTEVfQURNSU4iLCJkZXNjcmlwdGlvbiI6bnVsbCwicmVhZCI6WyIyMTQ3NDE4MDc1IiwiOCJdLCJ1cGRhdGUiOlsiMjE0NzQ4MzYwMSIsIjgiXSwiZGVzdHJveSI6WyIyMTQ3NDE4MDc1IiwiOCJdLCJyZWFkT3RoZXIiOlsiMjE0NzQ4MzYwMSIsIjgiXSwidXBkYXRlT3RoZXIiOlsiMjE0NzQxODA3NSIsIjgiXSwiZGVzdHJveU90aGVyIjpbIjIxNDc0MTgwNzUiLCI4Il0sImFwcGx5T25PdGhlcnMiOmZhbHNlLCJyb2xlTGV2ZWwiOjR9XSwibGVnYWxOYW1lIjoiRHVkaSBQYXJ0dXNoIiwiYWNjb3VudElkIjoxLCJhY2Nlc3NMZXZlbCI6NCwiYnVja2V0TmFtZSI6Ii90aW1lLXRhbGstYnVja2V0cy1jb21wYW55LXRlc3Rjb21wMSIsImVtcGxveWVlQ2hhdElkIjoiNjAzMjIxY2JkZWE5MjM4OGY5OTFiODI4IiwiY2hhdEFwcElkIjoiNjAzMjIxY2JkZWE5MjM4OGY5OTFiODI1IiwiY2hhdEJvdElkIjoiNjAzMjIxY2JkZWE5MjM4OGY5OTFiODI2IiwibXlDaGFubmVsSWQiOiI2MDMyMjFjY2RlYTkyMzg4Zjk5MWI4MmMiLCJpYXQiOjE2MTM5MDQ2NTIsImV4cCI6MTYxNDUwOTQ1Mn0.ISt0akAnQn6Adjd-nYXHcyapRT7zAwkcBIvz3s31gGs";

const HTTP_STATUS = {
    NO_CONNECTION: 0,
    INTERNAL_SERVER_ERROR: 500,
    SERVICE_UNAVAILABLE: 503,
    GATEWAY_TIMEOUT: 504,
    BAD_GATEWAY: 502,
    UNAUTHORIZED: 401,
    FORBIDDEN: 403,
    OK: 200,
    CREATED: 201,
};
@NgModule({
    imports: [
        RouterModule,
        NativeScriptHttpClientModule,
    ],
    providers: [],
    schemas: [
        NO_ERRORS_SCHEMA
    ]

})


export class GraphQLModule {
    constructor(
        apollo: Apollo,
        httpLink: HttpLink,
    ) {
        const http = httpLink.create({
            uri
        });
        const error = onError(({networkError, graphQLErrors}) => {
            console.log('ERROR@@@@')
            console.log(networkError)
            console.log(graphQLErrors)
        })

        const wsClient = new WebSocketLink({
            uri: 'ws://192.168.1.21:3000/graphql',
            options: {
                reconnect: true,
                connectionParams: {
                    authorization: token,
                },
            },
            connectionParams: async () => {
                return {
                    authorization: token
                };
            },
            webSocketImpl: WebSocket,
            reconnectionAttempts: 99,
        });
        const retryLink = new RetryLink({
            delay: {
                initial: 1000,
                max: Infinity,
            },
            attempts: {
                max: 10,
                retryIf: (error) => {
                    console.log(error)
                    return error.status === HTTP_STATUS.SERVICE_UNAVAILABLE ||
                        error.status === HTTP_STATUS.GATEWAY_TIMEOUT
                }
            },
        });
        const auth = new ApolloLink((operation, forward) => {
            operation.setContext({
                headers: new HttpHeaders().set('authorization', `${token}`),
            });

            return forward(operation);
        });

        const link = split(
            ({query}) => {
                const {kind, operation}: any = getMainDefinition(query);
                return kind === 'OperationDefinition' && operation === 'subscription';
            },
            wsClient,
            auth.concat(http)
        );
        getErrorSubject().subscribe((data) => {
            console.log(data)

        }, (error) => console.log(error));

        apollo.create({
            link: ApolloLink.from([retryLink, link]),
            cache: new InMemoryCache({
                addTypename: false,
                dataIdFromObject: (object: any) => object.id
            }),
            defaultOptions: {
                watchQuery: {
                    errorPolicy: 'all'
                }
            }
        });


    }
}




`

I don't understand

NazoSnare avatar Apr 15 '21 15:04 NazoSnare