@financial-times/cp-content-pipeline-schema 5.2.1 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/lib/datasources/base-capi.d.ts +2 -1
  3. package/lib/datasources/base-capi.js +8 -3
  4. package/lib/datasources/base-capi.js.map +1 -1
  5. package/lib/datasources/content-helpers.d.ts +3 -0
  6. package/lib/datasources/content-helpers.js +10 -0
  7. package/lib/datasources/content-helpers.js.map +1 -0
  8. package/lib/datasources/content-helpers.test.d.ts +1 -0
  9. package/lib/datasources/content-helpers.test.js +46 -0
  10. package/lib/datasources/content-helpers.test.js.map +1 -0
  11. package/lib/datasources/content-tree.d.ts +9 -0
  12. package/lib/datasources/content-tree.js +54 -0
  13. package/lib/datasources/content-tree.js.map +1 -0
  14. package/lib/datasources/content-tree.test.d.ts +1 -0
  15. package/lib/datasources/content-tree.test.js +133 -0
  16. package/lib/datasources/content-tree.test.js.map +1 -0
  17. package/lib/datasources/index.d.ts +2 -0
  18. package/lib/datasources/index.js +2 -0
  19. package/lib/datasources/index.js.map +1 -1
  20. package/lib/fixtures/contentTreeObject.d.ts +2 -0
  21. package/lib/fixtures/contentTreeObject.js +58 -0
  22. package/lib/fixtures/contentTreeObject.js.map +1 -0
  23. package/lib/fixtures/dummyContext.js +4 -0
  24. package/lib/fixtures/dummyContext.js.map +1 -1
  25. package/lib/generated/index.d.ts +22 -3
  26. package/lib/index.d.ts +1 -0
  27. package/lib/index.js.map +1 -1
  28. package/lib/model/Content.d.ts +38 -3
  29. package/lib/model/Content.js +22 -16
  30. package/lib/model/Content.js.map +1 -1
  31. package/lib/model/Content.test.js +290 -0
  32. package/lib/model/Content.test.js.map +1 -1
  33. package/lib/model/ContentWithBodyTree.d.ts +26 -0
  34. package/lib/model/ContentWithBodyTree.js +152 -0
  35. package/lib/model/ContentWithBodyTree.js.map +1 -0
  36. package/lib/model/ContentWithBodyTree.test.d.ts +1 -0
  37. package/lib/model/ContentWithBodyTree.test.js +72 -0
  38. package/lib/model/ContentWithBodyTree.test.js.map +1 -0
  39. package/lib/model/List.js +2 -3
  40. package/lib/model/List.js.map +1 -1
  41. package/lib/model/List.test.d.ts +1 -0
  42. package/lib/model/List.test.js +91 -0
  43. package/lib/model/List.test.js.map +1 -0
  44. package/lib/model/RichText.js +28 -19
  45. package/lib/model/RichText.js.map +1 -1
  46. package/lib/model/RichText.test.js +117 -53
  47. package/lib/model/RichText.test.js.map +1 -1
  48. package/lib/model/schemas/content-tree/content.d.ts +28 -0
  49. package/lib/model/schemas/content-tree/content.js +16 -0
  50. package/lib/model/schemas/content-tree/content.js.map +1 -0
  51. package/lib/resolvers/content-tree/extractText.js +1 -1
  52. package/lib/resolvers/content-tree/extractText.js.map +1 -1
  53. package/lib/resolvers/content-tree/extractText.test.d.ts +1 -0
  54. package/lib/resolvers/content-tree/extractText.test.js +17 -0
  55. package/lib/resolvers/content-tree/extractText.test.js.map +1 -0
  56. package/lib/resolvers/content-tree/references/CustomCodeComponent.js +2 -1
  57. package/lib/resolvers/content-tree/references/CustomCodeComponent.js.map +1 -1
  58. package/lib/resolvers/content-tree/references/CustomCodeComponent.test.d.ts +1 -0
  59. package/lib/resolvers/content-tree/references/CustomCodeComponent.test.js +72 -0
  60. package/lib/resolvers/content-tree/references/CustomCodeComponent.test.js.map +1 -0
  61. package/lib/resolvers/content-tree/references/Recommended.js +2 -1
  62. package/lib/resolvers/content-tree/references/Recommended.js.map +1 -1
  63. package/lib/resolvers/content-tree/references/Recommended.test.d.ts +1 -0
  64. package/lib/resolvers/content-tree/references/Recommended.test.js +100 -0
  65. package/lib/resolvers/content-tree/references/Recommended.test.js.map +1 -0
  66. package/lib/resolvers/content-tree/references/Video.js +2 -1
  67. package/lib/resolvers/content-tree/references/Video.js.map +1 -1
  68. package/lib/resolvers/content-tree/references/Video.test.d.ts +1 -0
  69. package/lib/resolvers/content-tree/references/Video.test.js +60 -0
  70. package/lib/resolvers/content-tree/references/Video.test.js.map +1 -0
  71. package/lib/resolvers/content-tree/updateTreeWithReferenceIds.js +1 -1
  72. package/lib/resolvers/content-tree/updateTreeWithReferenceIds.js.map +1 -1
  73. package/lib/resolvers/content-tree/updateTreeWithReferenceIds.test.d.ts +1 -0
  74. package/lib/resolvers/content-tree/updateTreeWithReferenceIds.test.js +24 -0
  75. package/lib/resolvers/content-tree/updateTreeWithReferenceIds.test.js.map +1 -0
  76. package/lib/resolvers/core.d.ts +1 -0
  77. package/lib/resolvers/core.js +49 -33
  78. package/lib/resolvers/core.js.map +1 -1
  79. package/lib/resolvers/core.test.d.ts +1 -0
  80. package/lib/resolvers/core.test.js +143 -0
  81. package/lib/resolvers/core.test.js.map +1 -0
  82. package/lib/resolvers/debug.d.ts +35 -0
  83. package/lib/resolvers/debug.js +11 -0
  84. package/lib/resolvers/debug.js.map +1 -1
  85. package/lib/resolvers/index.d.ts +22 -2
  86. package/lib/resolvers/richText.d.ts +1 -1
  87. package/lib/resolvers/scalars.d.ts +2 -2
  88. package/lib/resolvers/scalars.js +1 -1
  89. package/lib/resolvers/scalars.js.map +1 -1
  90. package/lib/types/content-tree-api.d.ts +1 -1
  91. package/package.json +1 -1
  92. package/src/datasources/base-capi.ts +10 -3
  93. package/src/datasources/content-helpers.test.ts +63 -0
  94. package/src/datasources/content-helpers.ts +14 -0
  95. package/src/datasources/content-tree.test.ts +193 -0
  96. package/src/datasources/content-tree.ts +79 -0
  97. package/src/datasources/index.ts +2 -0
  98. package/src/fixtures/contentTreeObject.ts +56 -0
  99. package/src/fixtures/dummyContext.ts +4 -0
  100. package/src/generated/content-tree-api/index.d.ts +548 -93
  101. package/src/generated/index.ts +25 -2
  102. package/src/index.ts +1 -0
  103. package/src/model/Content.test.ts +403 -0
  104. package/src/model/Content.ts +31 -31
  105. package/src/model/ContentWithBodyTree.test.ts +105 -0
  106. package/src/model/ContentWithBodyTree.ts +226 -0
  107. package/src/model/List.test.ts +118 -0
  108. package/src/model/List.ts +18 -19
  109. package/src/model/RichText.test.ts +111 -58
  110. package/src/model/RichText.ts +34 -20
  111. package/src/model/__snapshots__/RichText.test.ts.snap +1 -1
  112. package/src/model/schemas/content-tree/content.ts +14 -0
  113. package/src/resolvers/content-tree/extractText.test.ts +13 -0
  114. package/src/resolvers/content-tree/extractText.ts +1 -1
  115. package/src/resolvers/content-tree/references/CustomCodeComponent.test.ts +98 -0
  116. package/src/resolvers/content-tree/references/CustomCodeComponent.ts +3 -1
  117. package/src/resolvers/content-tree/references/Recommended.test.ts +140 -0
  118. package/src/resolvers/content-tree/references/Recommended.ts +3 -1
  119. package/src/resolvers/content-tree/references/Video.test.ts +86 -0
  120. package/src/resolvers/content-tree/references/Video.ts +3 -1
  121. package/src/resolvers/content-tree/updateTreeWithReferenceIds.test.ts +21 -0
  122. package/src/resolvers/content-tree/updateTreeWithReferenceIds.ts +1 -1
  123. package/src/resolvers/core.test.ts +186 -0
  124. package/src/resolvers/core.ts +62 -34
  125. package/src/resolvers/debug.ts +14 -0
  126. package/src/resolvers/scalars.ts +2 -2
  127. package/src/types/content-tree-api.ts +1 -1
  128. package/tsconfig.tsbuildinfo +1 -1
  129. package/typedefs/debug.graphql +13 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.3.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v5.2.1...cp-content-pipeline-schema-v5.3.0) (2026-06-24)
4
+
5
+
6
+ ### Features
7
+
8
+ * add debug resolver for Content Tree API ([f603889](https://github.com/Financial-Times/cp-content-pipeline/commit/f603889f8664d62cd59538e280d345dedca5f353))
9
+ * add rest Apollo ContentTreeApiDataSource ([e11117e](https://github.com/Financial-Times/cp-content-pipeline/commit/e11117eb86eef04f2cd3749ac6e4a7913c926e6e))
10
+ * call /content-tree in Query.content ([a126760](https://github.com/Financial-Times/cp-content-pipeline/commit/a1267600b3a4bfbd1b7e81a3f2b1485bdc378bca))
11
+ * configure Content Tree API specific timeout ([bfc00e6](https://github.com/Financial-Times/cp-content-pipeline/commit/bfc00e6dc309182a5fd02bb4b3e5b0bdff660713))
12
+ * do not try to convert bodyTree to AST ([e7625fd](https://github.com/Financial-Times/cp-content-pipeline/commit/e7625fde484fe8187a58f67d388dde2cf831a93f))
13
+ * handle /content-tree request errors ([cb2d2d1](https://github.com/Financial-Times/cp-content-pipeline/commit/cb2d2d1acc8c8195ff5856b2fff9f576205a984f))
14
+ * mvp model for content with a bodyTree ([2b4aef3](https://github.com/Financial-Times/cp-content-pipeline/commit/2b4aef3504e3122153acac864485ef3262a683bf))
15
+ * nested content requests call /content-tree ([0bd88ac](https://github.com/Financial-Times/cp-content-pipeline/commit/0bd88ac65cedafc6325d1ee18df7caf32541dfaf))
16
+ * use ContentWithBodyTree model ([c88c023](https://github.com/Financial-Times/cp-content-pipeline/commit/c88c023641b928b1de6fdfb7505bcd0950168539))
17
+ * useContentTree api query string param ([fe99a8f](https://github.com/Financial-Times/cp-content-pipeline/commit/fe99a8f826a9cefde12f71771e055abd54f2b112))
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * add bodyTree as RichTextSource ([462bae8](https://github.com/Financial-Times/cp-content-pipeline/commit/462bae85bb68e97ac34e9c7baf14f02c2da50bd9))
23
+ * derive placeholder and podcast types ([4634657](https://github.com/Financial-Times/cp-content-pipeline/commit/4634657066028b7f573152bdac7024684e7ca8ca))
24
+ * handle bodyTree.children as null ([d31597e](https://github.com/Financial-Times/cp-content-pipeline/commit/d31597e854f36e444fefbbaae4758892969f1634))
25
+ * teaser title override needs types property to resolve ([42d700c](https://github.com/Financial-Times/cp-content-pipeline/commit/42d700c1741c289bd25d9e38698cf28acae1ecd6))
26
+ * teaserMetadata needs types property to resolve ([940071e](https://github.com/Financial-Times/cp-content-pipeline/commit/940071ecd00f3e60e9b6ecf2eb2d2abf82a75659))
27
+
3
28
  ## [5.2.1](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v5.2.0...cp-content-pipeline-schema-v5.2.1) (2026-06-23)
4
29
 
5
30
 
@@ -2,8 +2,9 @@ import { AugmentedRequest } from '@apollo/datasource-rest';
2
2
  import { InstrumentedRESTDataSource } from './instrumented';
3
3
  export declare class BaseCapiDataSource extends InstrumentedRESTDataSource {
4
4
  baseURL: string;
5
- capiKey: string;
6
5
  articleCacheTTL: number;
6
+ get requestTimeout(): number;
7
+ get capiKey(): string;
7
8
  get cachePrefix(): string;
8
9
  willSendRequest(path: string, request: AugmentedRequest): void;
9
10
  }
@@ -2,21 +2,26 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseCapiDataSource = void 0;
4
4
  const instrumented_1 = require("./instrumented");
5
- const REQUEST_TIMEOUT = process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT
5
+ const DEFAULT_REQUEST_TIMEOUT = process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT
6
6
  ? parseInt(process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT)
7
7
  : 500;
8
8
  class BaseCapiDataSource extends instrumented_1.InstrumentedRESTDataSource {
9
9
  baseURL = process.env.CAPI_URL || 'https://api-t.ft.com/';
10
- capiKey = process.env.CAPI_API_KEY || '';
11
10
  articleCacheTTL = process.env.ARTICLE_CACHE_TTL
12
11
  ? parseInt(process.env.ARTICLE_CACHE_TTL)
13
12
  : 60 * 60 * 24 * 7 * 4; // 4 weeks
13
+ get requestTimeout() {
14
+ return DEFAULT_REQUEST_TIMEOUT;
15
+ }
16
+ get capiKey() {
17
+ return process.env.CAPI_API_KEY || '';
18
+ }
14
19
  get cachePrefix() {
15
20
  return 'capi';
16
21
  }
17
22
  willSendRequest(path, request) {
18
23
  super.willSendRequest(path, request);
19
- request.signal = AbortSignal.timeout(REQUEST_TIMEOUT);
24
+ request.signal = AbortSignal.timeout(this.requestTimeout);
20
25
  request.headers['x-api-key'] = this.capiKey;
21
26
  }
22
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base-capi.js","sourceRoot":"","sources":["../../src/datasources/base-capi.ts"],"names":[],"mappings":";;;AACA,iDAA2D;AAE3D,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,+BAA+B;IACjE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC;IACvD,CAAC,CAAC,GAAG,CAAA;AAEP,MAAa,kBAAmB,SAAQ,yCAA0B;IAChE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAA;IACzD,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAA;IACxC,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7C,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACzC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA,CAAC,UAAU;IAEnC,IAAa,WAAW;QACtB,OAAO,MAAM,CAAA;IACf,CAAC;IAEQ,eAAe,CAAC,IAAY,EAAE,OAAyB;QAC9D,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAEpC,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;QACrD,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO,CAAA;IAC7C,CAAC;CACF;AAjBD,gDAiBC"}
1
+ {"version":3,"file":"base-capi.js","sourceRoot":"","sources":["../../src/datasources/base-capi.ts"],"names":[],"mappings":";;;AACA,iDAA2D;AAE3D,MAAM,uBAAuB,GAAG,OAAO,CAAC,GAAG,CAAC,+BAA+B;IACzE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC;IACvD,CAAC,CAAC,GAAG,CAAA;AAEP,MAAa,kBAAmB,SAAQ,yCAA0B;IAChE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,uBAAuB,CAAA;IACzD,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7C,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACzC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA,CAAC,UAAU;IAEnC,IAAI,cAAc;QAChB,OAAO,uBAAuB,CAAA;IAChC,CAAC;IAED,IAAI,OAAO;QACT,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAA;IACvC,CAAC;IAED,IAAa,WAAW;QACtB,OAAO,MAAM,CAAA;IACf,CAAC;IAEQ,eAAe,CAAC,IAAY,EAAE,OAAyB;QAC9D,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAEpC,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QACzD,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO,CAAA;IAC7C,CAAC;CACF;AAxBD,gDAwBC"}
@@ -0,0 +1,3 @@
1
+ import type { QueryContext } from '..';
2
+ import { Content } from '../model/Content';
3
+ export declare function getContentForRequest(context: QueryContext, uuid: string, packageContainer?: Content): Promise<Content>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getContentForRequest = getContentForRequest;
4
+ async function getContentForRequest(context, uuid, packageContainer) {
5
+ if (context.useContentTree) {
6
+ return context.dataSources.contentTree.getContent(uuid, packageContainer);
7
+ }
8
+ return context.dataSources.capi.getContent(uuid, packageContainer);
9
+ }
10
+ //# sourceMappingURL=content-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-helpers.js","sourceRoot":"","sources":["../../src/datasources/content-helpers.ts"],"names":[],"mappings":";;AAGA,oDAUC;AAVM,KAAK,UAAU,oBAAoB,CACxC,OAAqB,EACrB,IAAY,EACZ,gBAA0B;IAE1B,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAA;IAC3E,CAAC;IAED,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAA;AACpE,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const content_helpers_1 = require("./content-helpers");
4
+ const uuid = '00000000-0000-0000-0000-000000000000';
5
+ describe('getContentForRequest', () => {
6
+ beforeEach(() => {
7
+ jest.restoreAllMocks();
8
+ });
9
+ it('routes to internal content api when useContentTree is false', async () => {
10
+ const packageContainer = {};
11
+ const content = {};
12
+ const context = {
13
+ useContentTree: false,
14
+ dataSources: {
15
+ capi: {
16
+ getContent: jest.fn().mockResolvedValue(content),
17
+ },
18
+ contentTree: {
19
+ getContent: jest.fn(),
20
+ },
21
+ },
22
+ };
23
+ await expect((0, content_helpers_1.getContentForRequest)(context, uuid, packageContainer)).resolves.toBe(content);
24
+ expect(context.dataSources.capi.getContent).toHaveBeenCalledWith(uuid, packageContainer);
25
+ expect(context.dataSources.contentTree.getContent).not.toHaveBeenCalled();
26
+ });
27
+ it('routes to content tree api when useContentTree is true', async () => {
28
+ const packageContainer = {};
29
+ const content = {};
30
+ const context = {
31
+ useContentTree: true,
32
+ dataSources: {
33
+ capi: {
34
+ getContent: jest.fn(),
35
+ },
36
+ contentTree: {
37
+ getContent: jest.fn().mockResolvedValue(content),
38
+ },
39
+ },
40
+ };
41
+ await expect((0, content_helpers_1.getContentForRequest)(context, uuid, packageContainer)).resolves.toBe(content);
42
+ expect(context.dataSources.contentTree.getContent).toHaveBeenCalledWith(uuid, packageContainer);
43
+ expect(context.dataSources.capi.getContent).not.toHaveBeenCalled();
44
+ });
45
+ });
46
+ //# sourceMappingURL=content-helpers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-helpers.test.js","sourceRoot":"","sources":["../../src/datasources/content-helpers.test.ts"],"names":[],"mappings":";;AAEA,uDAAwD;AAExD,MAAM,IAAI,GAAG,sCAAsC,CAAA;AAEnD,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,eAAe,EAAE,CAAA;IACxB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,gBAAgB,GAAG,EAAa,CAAA;QACtC,MAAM,OAAO,GAAG,EAAa,CAAA;QAC7B,MAAM,OAAO,GAAG;YACd,cAAc,EAAE,KAAK;YACrB,WAAW,EAAE;gBACX,IAAI,EAAE;oBACJ,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC;iBACjD;gBACD,WAAW,EAAE;oBACX,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE;iBACtB;aACF;SACyB,CAAA;QAE5B,MAAM,MAAM,CACV,IAAA,sCAAoB,EAAC,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CACtD,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAExB,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAC9D,IAAI,EACJ,gBAAgB,CACjB,CAAA;QACD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,gBAAgB,GAAG,EAAa,CAAA;QACtC,MAAM,OAAO,GAAG,EAAa,CAAA;QAC7B,MAAM,OAAO,GAAG;YACd,cAAc,EAAE,IAAI;YACpB,WAAW,EAAE;gBACX,IAAI,EAAE;oBACJ,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE;iBACtB;gBACD,WAAW,EAAE;oBACX,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC;iBACjD;aACF;SACyB,CAAA;QAE5B,MAAM,MAAM,CACV,IAAA,sCAAoB,EAAC,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CACtD,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAExB,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,oBAAoB,CACrE,IAAI,EACJ,gBAAgB,CACjB,CAAA;QACD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;IACpE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { Content } from '../model/Content';
2
+ import { BaseCapiDataSource } from './base-capi';
3
+ export declare class ContentTreeDataSource extends BaseCapiDataSource {
4
+ get requestTimeout(): number;
5
+ get backendSystemCode(): string;
6
+ get capiKey(): string;
7
+ getContent(uuid: string, packageContainer?: Content): Promise<Content>;
8
+ getHTTPCacheKeyForContent(uuid: string): Promise<string>;
9
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentTreeDataSource = void 0;
4
+ const ContentWithBodyTree_1 = require("../model/ContentWithBodyTree");
5
+ const base_capi_1 = require("./base-capi");
6
+ const isPrefixing = (cache) => 'prefix' in cache;
7
+ function getWrappedCacheKey(cache) {
8
+ if (isPrefixing(cache)) {
9
+ return getWrappedCacheKey(cache['wrapped']) + cache['prefix'];
10
+ }
11
+ return '';
12
+ }
13
+ class ContentTreeDataSource extends base_capi_1.BaseCapiDataSource {
14
+ get requestTimeout() {
15
+ return process.env.CONTENT_TREE_DATASOURCE_REQUEST_TIMEOUT
16
+ ? parseInt(process.env.CONTENT_TREE_DATASOURCE_REQUEST_TIMEOUT)
17
+ : 1500;
18
+ }
19
+ get backendSystemCode() {
20
+ return 'cm-cta';
21
+ }
22
+ get capiKey() {
23
+ return process.env.CONTENT_TREE_API_KEY || '';
24
+ }
25
+ async getContent(uuid, packageContainer) {
26
+ this.context.addSurrogateKeys([
27
+ {
28
+ prefix: 'contentPipelineArticle',
29
+ id: uuid,
30
+ },
31
+ ]);
32
+ const path = `content-tree/${uuid}`;
33
+ const content = await this.get(path, {
34
+ params: {
35
+ includeRelations: 'true',
36
+ unrollContent: 'true',
37
+ },
38
+ cacheOptions: { ttl: this.articleCacheTTL },
39
+ });
40
+ this.context.contentRequestedOnce = true;
41
+ this.calls.push(uuid);
42
+ return ContentWithBodyTree_1.ContentWithBodyTree.fromContentTree(content, this.context, packageContainer);
43
+ }
44
+ async getHTTPCacheKeyForContent(uuid) {
45
+ const dummyRequest = {
46
+ params: new URLSearchParams(),
47
+ headers: {},
48
+ };
49
+ const cacheKey = this.cacheKeyFor(await this.resolveURL(`content-tree/${uuid}?includeRelations=true&unrollContent=true`, dummyRequest));
50
+ return `${getWrappedCacheKey(this.httpCache['keyValueCache'])}${cacheKey}`;
51
+ }
52
+ }
53
+ exports.ContentTreeDataSource = ContentTreeDataSource;
54
+ //# sourceMappingURL=content-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-tree.js","sourceRoot":"","sources":["../../src/datasources/content-tree.ts"],"names":[],"mappings":";;;AAQA,sEAAkE;AAClE,2CAAgD;AAEhD,MAAM,WAAW,GAAG,CAAC,KAAoB,EAAmC,EAAE,CAC5E,QAAQ,IAAI,KAAK,CAAA;AAEnB,SAAS,kBAAkB,CAAC,KAAoB;IAC9C,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC/D,CAAC;IAED,OAAO,EAAE,CAAA;AACX,CAAC;AAED,MAAa,qBAAsB,SAAQ,8BAAkB;IAC3D,IAAI,cAAc;QAChB,OAAO,OAAO,CAAC,GAAG,CAAC,uCAAuC;YACxD,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC;YAC/D,CAAC,CAAC,IAAI,CAAA;IACV,CAAC;IAED,IAAI,iBAAiB;QACnB,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAA;IAC/C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY,EAAE,gBAA0B;QACvD,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC5B;gBACE,MAAM,EAAE,wBAAwB;gBAChC,EAAE,EAAE,IAAI;aACT;SACF,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,gBAAgB,IAAI,EAAE,CAAA;QAEnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;YACnC,MAAM,EAAE;gBACN,gBAAgB,EAAE,MAAM;gBACxB,aAAa,EAAE,MAAM;aACtB;YACD,YAAY,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE;SAC5C,CAAC,CAAA;QACF,IAAI,CAAC,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAA;QACxC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAErB,OAAO,yCAAmB,CAAC,eAAe,CACxC,OAAgC,EAChC,IAAI,CAAC,OAAO,EACZ,gBAAgB,CACjB,CAAA;IACH,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,IAAY;QAC1C,MAAM,YAAY,GAAqB;YACrC,MAAM,EAAE,IAAI,eAAe,EAAE;YAC7B,OAAO,EAAE,EAAE;SACZ,CAAA;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAC/B,MAAM,IAAI,CAAC,UAAU,CACnB,gBAAgB,IAAI,2CAA2C,EAC/D,YAAY,CACb,CACF,CAAA;QAED,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,GAAG,QAAQ,EAAE,CAAA;IAC5E,CAAC;CACF;AAxDD,sDAwDC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const logger_1 = require("@dotcom-reliability-kit/logger");
4
+ const contentTreeObject_1 = require("../fixtures/contentTreeObject");
5
+ const ContentWithBodyTree_1 = require("../model/ContentWithBodyTree");
6
+ const capi_1 = require("./capi");
7
+ const content_tree_1 = require("./content-tree");
8
+ const logger = new logger_1.Logger();
9
+ const metrics = { count: jest.fn(), aggregate: jest.fn() };
10
+ const cache = {
11
+ get: jest.fn(),
12
+ set: jest.fn(),
13
+ delete: jest.fn(),
14
+ };
15
+ const context = {
16
+ versions: { api: '1.0.0', schema: '1.0.0' },
17
+ logger,
18
+ metrics,
19
+ addSurrogateKeys: jest.fn(),
20
+ };
21
+ const contentTree = new content_tree_1.ContentTreeDataSource({
22
+ cache,
23
+ context,
24
+ });
25
+ const capi = new capi_1.CapiDataSource({
26
+ cache,
27
+ context,
28
+ });
29
+ const id = '00000000-0000-0000-0000-000000000000';
30
+ const fetchResult = {
31
+ parsedBody: {
32
+ ...contentTreeObject_1.baseContentTreeObject,
33
+ id: `http://www.ft.com/thing/${id}`,
34
+ },
35
+ };
36
+ describe('ContentTreeDataSource', () => {
37
+ beforeAll(() => {
38
+ jest.spyOn(global, 'fetch').mockImplementation(async () => new Response(JSON.stringify({
39
+ ...contentTreeObject_1.baseContentTreeObject,
40
+ id: `http://www.ft.com/thing/${id}`,
41
+ }), {
42
+ headers: new Headers([['content-type', 'application/json']]),
43
+ }));
44
+ });
45
+ beforeEach(() => {
46
+ jest.clearAllMocks();
47
+ delete context.contentRequestedOnce;
48
+ delete context.requestId;
49
+ });
50
+ describe('cache keys', () => {
51
+ it('should return the same cache key that it actually stored in the cache', async () => {
52
+ await contentTree.getContent(id);
53
+ expect(cache.set).toBeCalled();
54
+ await expect(contentTree.getHTTPCacheKeyForContent(id)).resolves.toBe(cache.set.mock.lastCall?.[0]);
55
+ });
56
+ it('should use a different cache key from internal content for the same uuid', async () => {
57
+ const contentTreeCacheKey = await contentTree.getHTTPCacheKeyForContent(id);
58
+ const capiCacheKey = await capi.getHTTPCacheKeyForContent(id);
59
+ expect(capiCacheKey).toBe(`capi:httpcache:https://api-t.ft.com/internalcontent/${id}`);
60
+ expect(contentTreeCacheKey).toBe(`capi:httpcache:https://api-t.ft.com/content-tree/${id}?includeRelations=true&unrollContent=true`);
61
+ });
62
+ });
63
+ describe('error handling', () => {
64
+ it('should include the system code in errors', async () => {
65
+ ;
66
+ global.fetch.mockResolvedValueOnce(new Response('Errored', {
67
+ status: 500,
68
+ }));
69
+ await expect(contentTree.getContent(id)).rejects.toMatchObject({
70
+ relatesToSystems: ['cm-cta'],
71
+ });
72
+ });
73
+ });
74
+ describe('getContent', () => {
75
+ it('should return a ContentWithBodyTree model', async () => {
76
+ await expect(contentTree.getContent(id)).resolves.toBeInstanceOf(ContentWithBodyTree_1.ContentWithBodyTree);
77
+ });
78
+ it('should add the uuid to the context surrogate keys', async () => {
79
+ await contentTree.getContent(id);
80
+ expect(context.addSurrogateKeys).toBeCalledWith(expect.arrayContaining([
81
+ expect.objectContaining({
82
+ prefix: 'contentPipelineArticle',
83
+ id,
84
+ }),
85
+ ]));
86
+ });
87
+ it('should set cacheOptions ttl', async () => {
88
+ const fetchSpy = jest
89
+ .spyOn(contentTree, 'fetch')
90
+ .mockResolvedValue(fetchResult);
91
+ await contentTree.getContent(id);
92
+ expect(fetchSpy).toBeCalledWith(`content-tree/${id}`, {
93
+ params: {
94
+ includeRelations: 'true',
95
+ unrollContent: 'true',
96
+ },
97
+ method: 'GET',
98
+ cacheOptions: { ttl: contentTree.articleCacheTTL },
99
+ });
100
+ fetchSpy.mockRestore();
101
+ });
102
+ it('should fetch from "/content-tree"', async () => {
103
+ await contentTree.getContent(id);
104
+ expect(global.fetch).toBeCalledWith(`https://api-t.ft.com/content-tree/${id}?includeRelations=true&unrollContent=true`, expect.objectContaining({
105
+ headers: expect.objectContaining({
106
+ 'user-agent': 'cp-content-pipeline-api/1.0.0 cp-content-pipeline-schema/1.0.0 ',
107
+ 'x-api-key': '',
108
+ }),
109
+ signal: expect.any(AbortSignal),
110
+ }));
111
+ });
112
+ it('should include the request id on the first content request', async () => {
113
+ context.requestId = 'request-id';
114
+ await contentTree.getContent(id);
115
+ expect(global.fetch).toBeCalledWith(`https://api-t.ft.com/content-tree/${id}?includeRelations=true&unrollContent=true`, expect.objectContaining({
116
+ headers: expect.objectContaining({
117
+ 'x-request-id': 'request-id',
118
+ }),
119
+ }));
120
+ });
121
+ it('should not include the request id after content has already been requested once', async () => {
122
+ context.requestId = 'request-id';
123
+ await contentTree.getContent(id);
124
+ await contentTree.getContent(id);
125
+ expect(global.fetch).toHaveBeenNthCalledWith(2, `https://api-t.ft.com/content-tree/${id}?includeRelations=true&unrollContent=true`, expect.objectContaining({
126
+ headers: expect.not.objectContaining({
127
+ 'x-request-id': 'request-id',
128
+ }),
129
+ }));
130
+ });
131
+ });
132
+ });
133
+ //# sourceMappingURL=content-tree.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-tree.test.js","sourceRoot":"","sources":["../../src/datasources/content-tree.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { BaseDataSourceOptions } from './base';
2
2
  import { CapiDataSource } from './capi';
3
+ import { ContentTreeDataSource } from './content-tree';
3
4
  import { EnrichedContentDataSource } from './enriched-content';
4
5
  import { ListsDataSource } from './lists';
5
6
  import { OrigamiImageDataSource } from './origami-image';
@@ -8,6 +9,7 @@ import { TwitterDataSource } from './twitter';
8
9
  import { URLManagementDataSource } from './url-management';
9
10
  export declare const initDataSources: (options: BaseDataSourceOptions) => {
10
11
  capi: CapiDataSource;
12
+ contentTree: ContentTreeDataSource;
11
13
  enrichedContent: EnrichedContentDataSource;
12
14
  lists: ListsDataSource;
13
15
  origami: OrigamiImageDataSource;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.initDataSources = void 0;
4
4
  const capi_1 = require("./capi");
5
+ const content_tree_1 = require("./content-tree");
5
6
  const enriched_content_1 = require("./enriched-content");
6
7
  const lists_1 = require("./lists");
7
8
  const origami_image_1 = require("./origami-image");
@@ -10,6 +11,7 @@ const twitter_1 = require("./twitter");
10
11
  const url_management_1 = require("./url-management");
11
12
  const initDataSources = (options) => ({
12
13
  capi: new capi_1.CapiDataSource(options),
14
+ contentTree: new content_tree_1.ContentTreeDataSource(options),
13
15
  enrichedContent: new enriched_content_1.EnrichedContentDataSource(options),
14
16
  lists: new lists_1.ListsDataSource(options),
15
17
  origami: new origami_image_1.OrigamiImageDataSource(options),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/datasources/index.ts"],"names":[],"mappings":";;;AACA,iCAAuC;AACvC,yDAA8D;AAC9D,mCAAyC;AACzC,mDAAwD;AACxD,qCAA2C;AAC3C,uCAA6C;AAC7C,qDAA0D;AAEnD,MAAM,eAAe,GAAG,CAAC,OAA8B,EAAE,EAAE,CAAC,CAAC;IAClE,IAAI,EAAE,IAAI,qBAAc,CAAC,OAAO,CAAC;IACjC,eAAe,EAAE,IAAI,4CAAyB,CAAC,OAAO,CAAC;IACvD,KAAK,EAAE,IAAI,uBAAe,CAAC,OAAO,CAAC;IACnC,OAAO,EAAE,IAAI,sCAAsB,CAAC,OAAO,CAAC;IAC5C,MAAM,EAAE,IAAI,yBAAgB,CAAC,OAAO,CAAC;IACrC,UAAU,EAAE,IAAI,wCAAuB,CAAC,OAAO,CAAC;IAChD,OAAO,EAAE,IAAI,2BAAiB,CAAC,OAAO,CAAC;CACxC,CAAC,CAAA;AARW,QAAA,eAAe,mBAQ1B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/datasources/index.ts"],"names":[],"mappings":";;;AACA,iCAAuC;AACvC,iDAAsD;AACtD,yDAA8D;AAC9D,mCAAyC;AACzC,mDAAwD;AACxD,qCAA2C;AAC3C,uCAA6C;AAC7C,qDAA0D;AAEnD,MAAM,eAAe,GAAG,CAAC,OAA8B,EAAE,EAAE,CAAC,CAAC;IAClE,IAAI,EAAE,IAAI,qBAAc,CAAC,OAAO,CAAC;IACjC,WAAW,EAAE,IAAI,oCAAqB,CAAC,OAAO,CAAC;IAC/C,eAAe,EAAE,IAAI,4CAAyB,CAAC,OAAO,CAAC;IACvD,KAAK,EAAE,IAAI,uBAAe,CAAC,OAAO,CAAC;IACnC,OAAO,EAAE,IAAI,sCAAsB,CAAC,OAAO,CAAC;IAC5C,MAAM,EAAE,IAAI,yBAAgB,CAAC,OAAO,CAAC;IACrC,UAAU,EAAE,IAAI,wCAAuB,CAAC,OAAO,CAAC;IAChD,OAAO,EAAE,IAAI,2BAAiB,CAAC,OAAO,CAAC;CACxC,CAAC,CAAA;AATW,QAAA,eAAe,mBAS1B"}
@@ -0,0 +1,2 @@
1
+ import type { ContentTreeApiContent } from '../types/content-tree-api';
2
+ export declare const baseContentTreeObject: ContentTreeApiContent;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.baseContentTreeObject = void 0;
4
+ exports.baseContentTreeObject = {
5
+ id: 'http://www.ft.com/thing/3071cae8-a27c-4ab4-a0d4-7bb00df1d477',
6
+ type: 'http://www.ft.com/ontology/content/Article',
7
+ title: 'Kwarteng faces test persuading UK watchdog of fiscal plans',
8
+ standfirst: 'A standfirst from content-tree',
9
+ byline: 'Chris Giles in London',
10
+ webUrl: 'https://www.ft.com/content/3071cae8-a27c-4ab4-a0d4-7bb00df1d477',
11
+ publishedDate: '2024-04-02T10:00:09.935Z',
12
+ firstPublishedDate: '2024-04-02T10:00:09.935Z',
13
+ lastModified: '2024-04-02T12:34:56.789Z',
14
+ canBeSyndicated: 'yes',
15
+ accessLevel: 'premium',
16
+ editorialDesk: '/FT/WorldNews/UK',
17
+ annotations: [
18
+ {
19
+ apiUrl: 'http://api.ft.com/things/9bfe954c-e0ec-4716-ae91-24bd0f7860c9',
20
+ directType: 'http://www.ft.com/ontology/Topic',
21
+ id: 'http://api.ft.com/things/9bfe954c-e0ec-4716-ae91-24bd0f7860c9',
22
+ predicate: 'http://www.ft.com/ontology/annotation/about',
23
+ prefLabel: 'UK financial crisis',
24
+ },
25
+ {
26
+ apiUrl: 'http://api.ft.com/things/1d556016-ad16-4fe7-8724-42b3fb15ad28',
27
+ directType: 'http://www.ft.com/ontology/person/Person',
28
+ id: 'http://api.ft.com/things/1d556016-ad16-4fe7-8724-42b3fb15ad28',
29
+ predicate: 'http://www.ft.com/ontology/annotation/hasAuthor',
30
+ prefLabel: 'Chris Giles',
31
+ },
32
+ ],
33
+ bodyTree: {
34
+ type: 'body',
35
+ version: 1,
36
+ children: [
37
+ {
38
+ type: 'paragraph',
39
+ children: [
40
+ {
41
+ type: 'text',
42
+ value: 'First content tree paragraph.',
43
+ },
44
+ ],
45
+ },
46
+ {
47
+ type: 'paragraph',
48
+ children: [
49
+ {
50
+ type: 'text',
51
+ value: 'Second paragraph.',
52
+ },
53
+ ],
54
+ },
55
+ ],
56
+ },
57
+ };
58
+ //# sourceMappingURL=contentTreeObject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentTreeObject.js","sourceRoot":"","sources":["../../src/fixtures/contentTreeObject.ts"],"names":[],"mappings":";;;AAEa,QAAA,qBAAqB,GAA0B;IAC1D,EAAE,EAAE,8DAA8D;IAClE,IAAI,EAAE,4CAA4C;IAClD,KAAK,EAAE,4DAA4D;IACnE,UAAU,EAAE,gCAAgC;IAC5C,MAAM,EAAE,uBAAuB;IAC/B,MAAM,EAAE,iEAAiE;IACzE,aAAa,EAAE,0BAA0B;IACzC,kBAAkB,EAAE,0BAA0B;IAC9C,YAAY,EAAE,0BAA0B;IACxC,eAAe,EAAE,KAAK;IACtB,WAAW,EAAE,SAAS;IACtB,aAAa,EAAE,kBAAkB;IACjC,WAAW,EAAE;QACX;YACE,MAAM,EAAE,+DAA+D;YACvE,UAAU,EAAE,kCAAkC;YAC9C,EAAE,EAAE,+DAA+D;YACnE,SAAS,EAAE,6CAA6C;YACxD,SAAS,EAAE,qBAAqB;SACjC;QACD;YACE,MAAM,EAAE,+DAA+D;YACvE,UAAU,EAAE,0CAA0C;YACtD,EAAE,EAAE,+DAA+D;YACnE,SAAS,EAAE,iDAAiD;YAC5D,SAAS,EAAE,aAAa;SACzB;KACF;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,WAAW;gBACjB,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,KAAK,EAAE,+BAA+B;qBACvC;iBACF;aACF;YACD;gBACE,IAAI,EAAE,WAAW;gBACjB,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,KAAK,EAAE,mBAAmB;qBAC3B;iBACF;aACF;SACF;KACF;CACF,CAAA"}
@@ -13,6 +13,7 @@ const addSurrogateKeys = (ids) => {
13
13
  };
14
14
  exports.default = {
15
15
  addSurrogateKeys,
16
+ useContentTree: false,
16
17
  dataSources: {
17
18
  vanityUrls: {
18
19
  get: (url) => Promise.resolve(url),
@@ -24,6 +25,9 @@ exports.default = {
24
25
  publishedDate: new Date(now + parseInt(id.slice(-2), 10)).toISOString(),
25
26
  }), { addSurrogateKeys })),
26
27
  },
28
+ contentTree: {
29
+ getContent: jest.fn(),
30
+ },
27
31
  people: {
28
32
  getPerson: jest.fn(),
29
33
  },
@@ -1 +1 @@
1
- {"version":3,"file":"dummyContext.js","sourceRoot":"","sources":["../../src/fixtures/dummyContext.ts"],"names":[],"mappings":";;;;;AAAA,8CAA0C;AAC1C,6CAA6C;AAC7C,4DAAkC;AAElC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;AAEtB,MAAM,gBAAgB,GAAG,CAAC,GAAqC,EAAE,EAAE;IACjE,MAAM,IAAI,GAAG,EAAE,CAAA;IACf,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAA;AACnB,CAAC,CAAA;AAED,kBAAe;IACb,gBAAgB;IAChB,WAAW,EAAE;QACX,UAAU,EAAE;YACV,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;SAC3C;QACD,IAAI,EAAE;YACJ,UAAU,EAAE,CAAC,EAAU,EAAE,EAAE,CACzB,OAAO,CAAC,OAAO,CACb,IAAI,iBAAO,CACT,IAAA,oBAAS,EAAC;gBACR,GAAG,2BAAc;gBACjB,EAAE;gBACF,aAAa,EAAE,IAAI,IAAI,CACrB,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACjC,CAAC,WAAW,EAAE;aAChB,CAAC,EACF,EAAE,gBAAgB,EAAkB,CACrC,CACF;SACJ;QACD,MAAM,EAAE;YACN,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE;SACrB;KACF;IACD,MAAM,EAAE;QACN,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;QAChB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;QAChB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;QAChB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACf,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;KAChB;CACyB,CAAA"}
1
+ {"version":3,"file":"dummyContext.js","sourceRoot":"","sources":["../../src/fixtures/dummyContext.ts"],"names":[],"mappings":";;;;;AAAA,8CAA0C;AAC1C,6CAA6C;AAC7C,4DAAkC;AAElC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;AAEtB,MAAM,gBAAgB,GAAG,CAAC,GAAqC,EAAE,EAAE;IACjE,MAAM,IAAI,GAAG,EAAE,CAAA;IACf,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAA;AACnB,CAAC,CAAA;AAED,kBAAe;IACb,gBAAgB;IAChB,cAAc,EAAE,KAAK;IACrB,WAAW,EAAE;QACX,UAAU,EAAE;YACV,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC;SAC3C;QACD,IAAI,EAAE;YACJ,UAAU,EAAE,CAAC,EAAU,EAAE,EAAE,CACzB,OAAO,CAAC,OAAO,CACb,IAAI,iBAAO,CACT,IAAA,oBAAS,EAAC;gBACR,GAAG,2BAAc;gBACjB,EAAE;gBACF,aAAa,EAAE,IAAI,IAAI,CACrB,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACjC,CAAC,WAAW,EAAE;aAChB,CAAC,EACF,EAAE,gBAAgB,EAAkB,CACrC,CACF;SACJ;QACD,WAAW,EAAE;YACX,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE;SACtB;QACD,MAAM,EAAE;YACN,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE;SACrB;KACF;IACD,MAAM,EAAE;QACN,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;QAChB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;QAChB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;QAChB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACf,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;KAChB;CACyB,CAAA"}
@@ -16,6 +16,7 @@ import type { Author as AuthorNode, ClipSet as ClipSetNode, OldClip as OldClipNo
16
16
  import type { DataSourceFetchResult } from '@apollo/datasource-rest';
17
17
  import type { DataSources } from '../datasources';
18
18
  import type { CapiDataSource } from '../datasources/capi';
19
+ import type { ContentTreeDataSource } from '../datasources/content-tree';
19
20
  import type { EnrichedContentDataSource } from '../datasources/enriched-content';
20
21
  import type { OrigamiImageDataSource } from '../datasources/origami-image';
21
22
  import type { ListsDataSource } from '../datasources/lists';
@@ -118,8 +119,8 @@ export type Scalars = {
118
119
  output: 'special-report' | 'extra' | 'basic' | 'extra-wide';
119
120
  };
120
121
  RichTextSource: {
121
- input: 'standfirst' | 'summary' | 'bodyXML' | 'transcript';
122
- output: 'standfirst' | 'summary' | 'bodyXML' | 'transcript';
122
+ input: 'standfirst' | 'summary' | 'bodyXML' | 'transcript' | 'bodyTree';
123
+ output: 'standfirst' | 'summary' | 'bodyXML' | 'transcript' | 'bodyTree';
123
124
  };
124
125
  TopperBackgroundColour: {
125
126
  input: 'paper' | 'wheat' | 'white' | 'black' | 'claret' | 'oxford' | 'slate' | 'crimson' | 'sky' | 'matisse';
@@ -685,6 +686,14 @@ export type ContentPackageUrlArgs = {
685
686
  relative?: InputMaybe<Scalars['Boolean']['input']>;
686
687
  vanity?: InputMaybe<Scalars['Boolean']['input']>;
687
688
  };
689
+ export type ContentTreeDebug = {
690
+ /** Call the /content-tree endpoint for debugging */
691
+ readonly content: DebugResponse;
692
+ };
693
+ export type ContentTreeDebugContentArgs = {
694
+ skipCache?: InputMaybe<Scalars['Boolean']['input']>;
695
+ uuid: Scalars['String']['input'];
696
+ };
688
697
  export type CustomCodeComponent = Reference & {
689
698
  /** Configuration data to be passed to the component. */
690
699
  readonly attributes: Scalars['JSON']['output'];
@@ -702,8 +711,10 @@ export type CustomCodeComponent = Reference & {
702
711
  readonly versionRange: Scalars['String']['output'];
703
712
  };
704
713
  export type DataSourceDebug = {
705
- /** Call the Content API datasource for debugging */
714
+ /** Call the Internal Content API datasource for debugging */
706
715
  readonly capi?: Maybe<CapiDebug>;
716
+ /** Call the Content Tree datasource for debugging */
717
+ readonly contentTree?: Maybe<ContentTreeDebug>;
707
718
  /** Call the Enriched Content datasource for debugging */
708
719
  readonly enrichedContent?: Maybe<EnrichedContentDebug>;
709
720
  /** Call the Image Service datasource for debugging */
@@ -2081,6 +2092,7 @@ export type ResolversTypes = ResolversObject<{
2081
2092
  node: Maybe<ResolversTypes['Content']>;
2082
2093
  }>;
2083
2094
  ContentPackage: ResolverTypeWrapper<ContentModel>;
2095
+ ContentTreeDebug: ResolverTypeWrapper<ContentTreeDataSource>;
2084
2096
  ContentType: ResolverTypeWrapper<Scalars['ContentType']['output']>;
2085
2097
  CustomCodeComponent: ResolverTypeWrapper<ReferenceWithCAPIData<ContentTree.CustomCodeComponent>>;
2086
2098
  DataSourceDebug: ResolverTypeWrapper<DataSources>;
@@ -2203,6 +2215,7 @@ export type ResolversParentTypes = ResolversObject<{
2203
2215
  node: Maybe<ResolversParentTypes['Content']>;
2204
2216
  };
2205
2217
  ContentPackage: ContentModel;
2218
+ ContentTreeDebug: ContentTreeDataSource;
2206
2219
  ContentType: Scalars['ContentType']['output'];
2207
2220
  CustomCodeComponent: ReferenceWithCAPIData<ContentTree.CustomCodeComponent>;
2208
2221
  DataSourceDebug: DataSources;
@@ -2577,6 +2590,10 @@ export type ContentPackageResolvers<ContextType = QueryContext, ParentType exten
2577
2590
  url: Resolver<ResolversTypes['String'], ParentType, ContextType, Partial<ContentPackageUrlArgs>>;
2578
2591
  __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
2579
2592
  }>;
2593
+ export type ContentTreeDebugResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['ContentTreeDebug'] = ResolversParentTypes['ContentTreeDebug']> = ResolversObject<{
2594
+ content: Resolver<ResolversTypes['DebugResponse'], ParentType, ContextType, RequireFields<ContentTreeDebugContentArgs, 'uuid'>>;
2595
+ __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
2596
+ }>;
2580
2597
  export interface ContentTypeScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['ContentType'], any> {
2581
2598
  name: 'ContentType';
2582
2599
  }
@@ -2592,6 +2609,7 @@ export type CustomCodeComponentResolvers<ContextType = QueryContext, ParentType
2592
2609
  }>;
2593
2610
  export type DataSourceDebugResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['DataSourceDebug'] = ResolversParentTypes['DataSourceDebug']> = ResolversObject<{
2594
2611
  capi: Resolver<Maybe<ResolversTypes['CapiDebug']>, ParentType, ContextType>;
2612
+ contentTree: Resolver<Maybe<ResolversTypes['ContentTreeDebug']>, ParentType, ContextType>;
2595
2613
  enrichedContent: Resolver<Maybe<ResolversTypes['EnrichedContentDebug']>, ParentType, ContextType>;
2596
2614
  imageService: Resolver<Maybe<ResolversTypes['ImageServiceDebug']>, ParentType, ContextType>;
2597
2615
  lists: Resolver<Maybe<ResolversTypes['ListsDebug']>, ParentType, ContextType>;
@@ -3345,6 +3363,7 @@ export type Resolvers<ContextType = QueryContext> = ResolversObject<{
3345
3363
  ContentConnection: ContentConnectionResolvers<ContextType>;
3346
3364
  ContentEdge: ContentEdgeResolvers<ContextType>;
3347
3365
  ContentPackage: ContentPackageResolvers<ContextType>;
3366
+ ContentTreeDebug: ContentTreeDebugResolvers<ContextType>;
3348
3367
  ContentType: GraphQLScalarType;
3349
3368
  CustomCodeComponent: CustomCodeComponentResolvers<ContextType>;
3350
3369
  DataSourceDebug: DataSourceDebugResolvers<ContextType>;
package/lib/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export interface QueryContext {
21
21
  dataSources: DataSources;
22
22
  systemCode: string;
23
23
  requestId?: string;
24
+ readonly useContentTree: boolean;
24
25
  contentRequestedOnce?: boolean;
25
26
  contentTimedOut?: boolean;
26
27
  logger: Logger;
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAA4D;AAmDnD,gGAnDA,6BAAe,OAmDA;AAhDxB,8CAAoD;AACpD,4EAAsE;AACtE,2CAA4B;AAC5B,2BAAiC;AAiCpB,QAAA,oBAAoB,GAAG,IAAA,iBAAY,EAC9C,OAAO,CAAC,OAAO,CAAC,4BAA4B,CAAC,EAC7C,OAAO,CACR,CAAA;AAEY,QAAA,QAAQ,GAAG,IAAA,qBAAc,EACpC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,0BAA0B,CAAC,EAChD;IACE,OAAO,EAAE,CAAC,IAAI,uCAAiB,EAAE,CAAC;CACnC,CACF,CAAA;AAGD,yCAAkD;AAAzC,uHAAA,OAAO,OAAa;AAC7B,+CAAwD;AAA/C,mHAAA,OAAO,OAAW;AAC3B,yCAA0C;AAAjC,+FAAA,OAAO,OAAA;AAEhB,+FAA8E"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAA4D;AAoDnD,gGApDA,6BAAe,OAoDA;AAjDxB,8CAAoD;AACpD,4EAAsE;AACtE,2CAA4B;AAC5B,2BAAiC;AAkCpB,QAAA,oBAAoB,GAAG,IAAA,iBAAY,EAC9C,OAAO,CAAC,OAAO,CAAC,4BAA4B,CAAC,EAC7C,OAAO,CACR,CAAA;AAEY,QAAA,QAAQ,GAAG,IAAA,qBAAc,EACpC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,0BAA0B,CAAC,EAChD;IACE,OAAO,EAAE,CAAC,IAAI,uCAAiB,EAAE,CAAC;CACnC,CACF,CAAA;AAGD,yCAAkD;AAAzC,uHAAA,OAAO,OAAa;AAC7B,+CAAwD;AAA/C,mHAAA,OAAO,OAAW;AAC3B,yCAA0C;AAAjC,+FAAA,OAAO,OAAA;AAEhB,+FAA8E"}