id-interceptor.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
  2. import { GqlExecutionContext } from '@nestjs/graphql';
  3. import { VariableValues } from 'apollo-server-core';
  4. import { GraphQLNamedType, GraphQLSchema, OperationDefinitionNode } from 'graphql';
  5. import { Observable } from 'rxjs';
  6. import { GraphqlValueTransformer } from '../common/graphql-value-transformer';
  7. import { IdCodecService } from '../common/id-codec.service';
  8. import { parseContext } from '../common/parse-context';
  9. export const ID_CODEC_TRANSFORM_KEYS = 'idCodecTransformKeys';
  10. type TypeTreeNode = {
  11. type: GraphQLNamedType | undefined;
  12. parent: TypeTreeNode | null;
  13. isList: boolean;
  14. children: { [name: string]: TypeTreeNode };
  15. };
  16. /**
  17. * This interceptor automatically decodes incoming requests so that any
  18. * ID values are transformed correctly as per the configured EntityIdStrategy.
  19. *
  20. * ID values are defined as properties with the name "id", or properties with names matching any
  21. * arguments passed to the {@link Decode} decorator.
  22. */
  23. @Injectable()
  24. export class IdInterceptor implements NestInterceptor {
  25. private graphQlValueTransformers = new WeakMap<GraphQLSchema, GraphqlValueTransformer>();
  26. constructor(private idCodecService: IdCodecService) {}
  27. intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> {
  28. const { isGraphQL, req, info } = parseContext(context);
  29. if (isGraphQL && info) {
  30. const args = GqlExecutionContext.create(context).getArgs();
  31. const transformer = this.getTransformerForSchema(info.schema);
  32. this.decodeIdArguments(transformer, info.operation, args);
  33. }
  34. return next.handle();
  35. }
  36. private getTransformerForSchema(schema: GraphQLSchema): GraphqlValueTransformer {
  37. const existing = this.graphQlValueTransformers.get(schema);
  38. if (existing) {
  39. return existing;
  40. }
  41. const transformer = new GraphqlValueTransformer(schema);
  42. this.graphQlValueTransformers.set(schema, transformer);
  43. return transformer;
  44. }
  45. private decodeIdArguments(
  46. graphqlValueTransformer: GraphqlValueTransformer,
  47. definition: OperationDefinitionNode,
  48. variables: VariableValues = {},
  49. ) {
  50. const typeTree = graphqlValueTransformer.getInputTypeTree(definition);
  51. graphqlValueTransformer.transformValues(typeTree, variables, (value, type) => {
  52. const isIdType = type && type.name === 'ID';
  53. return isIdType ? this.idCodecService.decode(value) : value;
  54. });
  55. return variables;
  56. }
  57. }