id-interceptor.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  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 } = parseContext(context);
  29. if (isGraphQL) {
  30. const args = GqlExecutionContext.create(context).getArgs();
  31. const info = GqlExecutionContext.create(context).getInfo();
  32. const transformer = this.getTransformerForSchema(info.schema);
  33. this.decodeIdArguments(transformer, info.operation, args);
  34. }
  35. return next.handle();
  36. }
  37. private getTransformerForSchema(schema: GraphQLSchema): GraphqlValueTransformer {
  38. const existing = this.graphQlValueTransformers.get(schema);
  39. if (existing) {
  40. return existing;
  41. }
  42. const transformer = new GraphqlValueTransformer(schema);
  43. this.graphQlValueTransformers.set(schema, transformer);
  44. return transformer;
  45. }
  46. private decodeIdArguments(
  47. graphqlValueTransformer: GraphqlValueTransformer,
  48. definition: OperationDefinitionNode,
  49. variables: VariableValues = {},
  50. ) {
  51. const typeTree = graphqlValueTransformer.getInputTypeTree(definition);
  52. graphqlValueTransformer.transformValues(typeTree, variables, (value, type) => {
  53. const isIdType = type && type.name === 'ID';
  54. return isIdType ? this.idCodecService.decode(value) : value;
  55. });
  56. return variables;
  57. }
  58. }