File

src/app/graphql.module.ts

import {NgModule} from '@angular/core';
import {APOLLO_NAMED_OPTIONS, APOLLO_OPTIONS, NamedOptions} from 'apollo-angular';
import {ApolloClientOptions, ApolloLink, InMemoryCache} from '@apollo/client/core';
import {HttpLink} from 'apollo-angular/http';
import {setContext} from '@apollo/link-context';
import {onError} from '@apollo/client/link/error';
import {HttpClientModule} from '@angular/common/http';
import {AuthenticationService} from './auth/authentication.service';
import {environment} from '@environments/environment';
import {IndividualConfig, ToastrModule, ToastrService} from 'ngx-toastr';
import {DefaultOptions} from '@apollo/client/core/ApolloClient';

/**
 * This modules purpose is to provide define functions returning configurations
 * for the automatic creation of ApolloClients for graphql communication with the backend.
 * The key functions are providePublicApollo and provideDefaultApollo. The apollo instance
 * constructed from providePublicApollo is only used for registering.
 */

const defaultOptions: DefaultOptions = {
  query: {
    fetchPolicy: 'no-cache',
    errorPolicy: 'all'
  }
};

/**
 * Congiuration for network error toast on register page.
 */
const networkErrorToast: Partial<IndividualConfig> = {
  timeOut: 5000,
  closeButton: true,
  positionClass: 'toast-top-center',
  enableHtml: true
};

const basic = setContext((operation, context) => ({
  headers: {
    Accept: 'charset=utf-8'
  }
}));

export function createErrorLink(authService: AuthenticationService, toastr: ToastrService): ApolloLink {
  const errorLink = onError(({graphQLErrors, networkError, operation, forward}) => {
    if (graphQLErrors) {
      const message = graphQLErrors.map((err) => err.message).join('<br>');
      console.error(`[Graphql errors]: ${message}`);
      toastr.error(message, 'GraphQL error', networkErrorToast);
    }

    if (networkError) {
      console.error(`[Network error]: ${networkError.name}\n${networkError.message}\n${networkError.stack}`);
      // @ts-ignore
      if (networkError.status === 401) {
        authService.logout();
      } else {
        toastr.error(networkError.message, 'Server/Connection error', networkErrorToast);
      }
    }
  });
  return errorLink;
}

/**
 * Create Apollo instance where credentials from local storage are attached to requests
 * @param httpLink
 * @param authService
 * @param toastr
 */
export function provideDefaultApollo(
  httpLink: HttpLink,
  authService: AuthenticationService,
  toastr: ToastrService
): ApolloClientOptions<any> {
  const auth = setContext((_, {headers}) => {
    // get the authentication token from local storage if it exists
    // return the headers to the context so httpLink can read them
    return {
      headers: {
        ...headers,
        Authorization: localStorage.getItem('token') ? `Bearer ${localStorage.getItem('token')}` : ''
      }
    };
  });
  const errorLink = createErrorLink(authService, toastr);
  const link = ApolloLink.from([basic, errorLink, auth, httpLink.create({uri: environment.apiUrl})]);
  const cache = new InMemoryCache();
  return {link, cache, defaultOptions};
}

/**
 * Creates Apollo Client used for user registration with public endpoint. In contrast to
 * provideDefaultApollo no token is attached to reqeusts.
 * @param httpLink
 * @param authService
 * @param toastr
 */
export function providePublicApollo(httpLink: HttpLink, authService: AuthenticationService, toastr: ToastrService): NamedOptions {
  const errorLink = createErrorLink(authService, toastr);
  const link = ApolloLink.from([basic, errorLink, httpLink.create({uri: environment.signUpUrl})]);
  const cache = new InMemoryCache();
  return {
    publicClient: {
      link,
      cache,
      defaultOptions
    }
  };
}

@NgModule({
  imports: [ToastrModule],
  exports: [HttpClientModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: provideDefaultApollo,
      deps: [HttpLink, AuthenticationService, ToastrService]
    },
    {
      provide: APOLLO_NAMED_OPTIONS,
      useFactory: providePublicApollo,
      deps: [HttpLink, AuthenticationService, ToastrService]
    }
  ]
})
export class GraphQLModule {}

results matching ""

    No results matching ""