asset-interceptor.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import { ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
  2. import { GqlExecutionContext } from '@nestjs/graphql';
  3. import { Observable } from 'rxjs';
  4. import { map } from 'rxjs/operators';
  5. import { Type } from '../../../../shared/shared-types';
  6. import { AssetStorageStrategy } from '../../config/asset-storage-strategy/asset-storage-strategy';
  7. import { ConfigService } from '../../config/config.service';
  8. import { Asset } from '../../entity/asset/asset.entity';
  9. /**
  10. * Transforms outputs so that any Asset instances are run through the {@link AssetStorageStrategy.toAbsoluteUrl}
  11. * method before being returned in the response.
  12. */
  13. @Injectable()
  14. export class AssetInterceptor implements NestInterceptor {
  15. private readonly toAbsoluteUrl: AssetStorageStrategy['toAbsoluteUrl'] | undefined;
  16. constructor(private configService: ConfigService) {
  17. const { assetOptions } = this.configService;
  18. if (assetOptions.assetStorageStrategy.toAbsoluteUrl) {
  19. this.toAbsoluteUrl = assetOptions.assetStorageStrategy.toAbsoluteUrl.bind(
  20. assetOptions.assetStorageStrategy,
  21. );
  22. }
  23. }
  24. intercept<T = any>(context: ExecutionContext, call$: Observable<T>): Observable<T> {
  25. const toAbsoluteUrl = this.toAbsoluteUrl;
  26. if (toAbsoluteUrl === undefined) {
  27. return call$;
  28. }
  29. const ctx = GqlExecutionContext.create(context).getContext();
  30. const request = ctx.req;
  31. return call$.pipe(
  32. map(data => {
  33. visitType(data, Asset, asset => {
  34. asset.preview = toAbsoluteUrl(request, asset.preview);
  35. asset.source = toAbsoluteUrl(request, asset.source);
  36. });
  37. return data;
  38. }),
  39. );
  40. }
  41. }
  42. /**
  43. * Traverses the object and when encountering a property with a value which
  44. * is an instance of class T, invokes the visitor function on that value.
  45. */
  46. function visitType<T>(obj: any, type: Type<T>, visit: (instance: T) => void) {
  47. const keys = Object.keys(obj || {});
  48. for (const key of keys) {
  49. const value = obj[key];
  50. if (value instanceof type) {
  51. visit(value);
  52. } else {
  53. if (typeof value === 'object') {
  54. visitType(value, type, visit);
  55. }
  56. }
  57. }
  58. }