@financial-times/cp-content-pipeline-schema 5.0.1 → 5.2.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.
- package/CHANGELOG.md +20 -0
- package/README.md +2 -0
- package/lib/datasources/base-capi.d.ts +9 -0
- package/lib/datasources/base-capi.js +24 -0
- package/lib/datasources/base-capi.js.map +1 -0
- package/lib/datasources/capi.d.ts +2 -15
- package/lib/datasources/capi.js +2 -43
- package/lib/datasources/capi.js.map +1 -1
- package/lib/datasources/capi.test.js +59 -5
- package/lib/datasources/capi.test.js.map +1 -1
- package/lib/datasources/enriched-content.d.ts +6 -0
- package/lib/datasources/enriched-content.js +17 -0
- package/lib/datasources/enriched-content.js.map +1 -0
- package/lib/datasources/enriched-content.test.d.ts +1 -0
- package/lib/datasources/enriched-content.test.js +73 -0
- package/lib/datasources/enriched-content.test.js.map +1 -0
- package/lib/datasources/index.d.ts +6 -0
- package/lib/datasources/index.js +6 -0
- package/lib/datasources/index.js.map +1 -1
- package/lib/datasources/lists.d.ts +6 -0
- package/lib/datasources/lists.js +22 -0
- package/lib/datasources/lists.js.map +1 -0
- package/lib/datasources/lists.test.d.ts +1 -0
- package/lib/datasources/lists.test.js +83 -0
- package/lib/datasources/lists.test.js.map +1 -0
- package/lib/datasources/people.d.ts +7 -0
- package/lib/datasources/people.js +21 -0
- package/lib/datasources/people.js.map +1 -0
- package/lib/datasources/people.test.d.ts +1 -0
- package/lib/datasources/people.test.js +74 -0
- package/lib/datasources/people.test.js.map +1 -0
- package/lib/fixtures/dummyContext.js +3 -0
- package/lib/fixtures/dummyContext.js.map +1 -1
- package/lib/generated/index.d.ts +57 -21
- package/lib/model/Concept.test.js +1 -1
- package/lib/model/Concept.test.js.map +1 -1
- package/lib/model/Content.js +2 -2
- package/lib/model/Content.js.map +1 -1
- package/lib/model/Content.test.js +1 -1
- package/lib/model/Content.test.js.map +1 -1
- package/lib/model/Person.test.js +1 -1
- package/lib/model/Person.test.js.map +1 -1
- package/lib/model/strategies/topper/PartnerContentStrategy.d.ts +4 -3
- package/lib/model/strategies/topper/PartnerContentStrategy.js +8 -3
- package/lib/model/strategies/topper/PartnerContentStrategy.js.map +1 -1
- package/lib/model/strategies/topper/PartnerContentStrategy.test.js +14 -0
- package/lib/model/strategies/topper/PartnerContentStrategy.test.js.map +1 -1
- package/lib/model/strategies/topper/TopperStrategy.integration.test.js +37 -3
- package/lib/model/strategies/topper/TopperStrategy.integration.test.js.map +1 -1
- package/lib/resolvers/concept.js +1 -1
- package/lib/resolvers/concept.js.map +1 -1
- package/lib/resolvers/content-tree/Workarounds.d.ts +2 -2
- package/lib/resolvers/content-tree/nodePredicates.d.ts +2 -2
- package/lib/resolvers/content-tree/nodePredicates.js +3 -0
- package/lib/resolvers/content-tree/nodePredicates.js.map +1 -1
- package/lib/resolvers/content-tree/references/ImageSet.js +1 -1
- package/lib/resolvers/content-tree/references/ImageSet.js.map +1 -1
- package/lib/resolvers/content-tree/references/ImageSet.test.js +12 -12
- package/lib/resolvers/content-tree/references/ImageSet.test.js.map +1 -1
- package/lib/resolvers/content-tree/tagMappings.js +30 -0
- package/lib/resolvers/content-tree/tagMappings.js.map +1 -1
- package/lib/resolvers/content-tree/tagMappings.test.js +70 -0
- package/lib/resolvers/content-tree/tagMappings.test.js.map +1 -1
- package/lib/resolvers/core.d.ts +3 -0
- package/lib/resolvers/core.js +1 -1
- package/lib/resolvers/core.js.map +1 -1
- package/lib/resolvers/debug.d.ts +81 -3
- package/lib/resolvers/debug.js +15 -3
- package/lib/resolvers/debug.js.map +1 -1
- package/lib/resolvers/index.d.ts +48 -3
- package/lib/types/content-tree-api.d.ts +6 -0
- package/lib/types/content-tree-api.js +3 -0
- package/lib/types/content-tree-api.js.map +1 -0
- package/package.json +3 -3
- package/src/datasources/base-capi.ts +25 -0
- package/src/datasources/capi.test.ts +91 -5
- package/src/datasources/capi.ts +2 -55
- package/src/datasources/enriched-content.test.ts +99 -0
- package/src/datasources/enriched-content.ts +16 -0
- package/src/datasources/index.ts +6 -0
- package/src/datasources/lists.test.ts +117 -0
- package/src/datasources/lists.ts +20 -0
- package/src/datasources/people.test.ts +104 -0
- package/src/datasources/people.ts +20 -0
- package/src/fixtures/dummyContext.ts +3 -0
- package/src/generated/content-tree-api/index.d.ts +279 -0
- package/src/generated/content-tree-api/readme.md +17 -0
- package/src/generated/index.ts +69 -27
- package/src/model/Concept.test.ts +3 -3
- package/src/model/Content.test.ts +1 -1
- package/src/model/Content.ts +2 -2
- package/src/model/Person.test.ts +3 -3
- package/src/model/strategies/topper/PartnerContentStrategy.test.ts +18 -0
- package/src/model/strategies/topper/PartnerContentStrategy.ts +17 -2
- package/src/model/strategies/topper/TopperStrategy.integration.test.ts +41 -3
- package/src/resolvers/concept.ts +1 -1
- package/src/resolvers/content-tree/Workarounds.ts +6 -0
- package/src/resolvers/content-tree/nodePredicates.ts +3 -0
- package/src/resolvers/content-tree/references/ImageSet.test.ts +16 -12
- package/src/resolvers/content-tree/references/ImageSet.ts +1 -1
- package/src/resolvers/content-tree/tagMappings.test.ts +83 -0
- package/src/resolvers/content-tree/tagMappings.ts +28 -0
- package/src/resolvers/core.ts +1 -1
- package/src/resolvers/debug.ts +25 -7
- package/src/types/content-tree-api.ts +16 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/debug.graphql +24 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.2.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v5.1.0...cp-content-pipeline-schema-v5.2.0) (2026-06-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **partner-content:** support basic topper treatment ([cfdedf2](https://github.com/Financial-Times/cp-content-pipeline/commit/cfdedf2e124fd0e4d0292a022222141e358803fd))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **partner-content:** suppress topper editorial concepts ([eff5bc3](https://github.com/Financial-Times/cp-content-pipeline/commit/eff5bc3e31018359ce9851bff22a8c07eea86b83))
|
|
14
|
+
|
|
15
|
+
## [5.1.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v5.0.1...cp-content-pipeline-schema-v5.1.0) (2026-06-04)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* ci-3602 carousel ([c97edd0](https://github.com/Financial-Times/cp-content-pipeline/commit/c97edd0004b086587a43a5476b5c992d0f909e85))
|
|
21
|
+
* ci-3615 beta carousel tracking ([2302de3](https://github.com/Financial-Times/cp-content-pipeline/commit/2302de35655b8eee941465fa2302bce850315995))
|
|
22
|
+
|
|
3
23
|
## [5.0.1](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v5.0.0...cp-content-pipeline-schema-v5.0.1) (2026-05-20)
|
|
4
24
|
|
|
5
25
|
|
package/README.md
CHANGED
|
@@ -106,6 +106,8 @@ The GraphQL resolvers which define the technique for fetching types defined in t
|
|
|
106
106
|
|
|
107
107
|
We use [GraphQL Code Generator](https://graphql-codegen.com) with the [TypeScript Resolvers](https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-resolvers) plugin to output TypeScript types for the expected function signatures for the resolvers. This prevents runtime GraphQL errors caused by unexpected data being returned.
|
|
108
108
|
|
|
109
|
+
We also generate TypeScript declarations from the [Content and Metadata Content Tree OpenAPI spec](https://github.com/Financial-Times/apig-tyk-api-documentation/blob/main/internal/api-definitions/prod/content-and-metadata-team/cm-content-tree-svc/cm-content-tree-svc.json) using [openapi-typescript](https://openapi-ts.dev/). See [`src/generated/content-tree-api`](./src/generated/content-tree-api/) readme for steps to update or regenerate these types.
|
|
110
|
+
|
|
109
111
|
#### Models
|
|
110
112
|
|
|
111
113
|
The resolvers accept and return data objects in the form of **models** instead of plain GraphQL objects. The **models** classes encapsulate business logic and interact with the data sources.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AugmentedRequest } from '@apollo/datasource-rest';
|
|
2
|
+
import { InstrumentedRESTDataSource } from './instrumented';
|
|
3
|
+
export declare class BaseCapiDataSource extends InstrumentedRESTDataSource {
|
|
4
|
+
baseURL: string;
|
|
5
|
+
capiKey: string;
|
|
6
|
+
articleCacheTTL: number;
|
|
7
|
+
get cachePrefix(): string;
|
|
8
|
+
willSendRequest(path: string, request: AugmentedRequest): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseCapiDataSource = void 0;
|
|
4
|
+
const instrumented_1 = require("./instrumented");
|
|
5
|
+
const REQUEST_TIMEOUT = process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT
|
|
6
|
+
? parseInt(process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT)
|
|
7
|
+
: 500;
|
|
8
|
+
class BaseCapiDataSource extends instrumented_1.InstrumentedRESTDataSource {
|
|
9
|
+
baseURL = process.env.CAPI_URL || 'https://api-t.ft.com/';
|
|
10
|
+
capiKey = process.env.CAPI_API_KEY || '';
|
|
11
|
+
articleCacheTTL = process.env.ARTICLE_CACHE_TTL
|
|
12
|
+
? parseInt(process.env.ARTICLE_CACHE_TTL)
|
|
13
|
+
: 60 * 60 * 24 * 7 * 4; // 4 weeks
|
|
14
|
+
get cachePrefix() {
|
|
15
|
+
return 'capi';
|
|
16
|
+
}
|
|
17
|
+
willSendRequest(path, request) {
|
|
18
|
+
super.willSendRequest(path, request);
|
|
19
|
+
request.signal = AbortSignal.timeout(REQUEST_TIMEOUT);
|
|
20
|
+
request.headers['x-api-key'] = this.capiKey;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.BaseCapiDataSource = BaseCapiDataSource;
|
|
24
|
+
//# sourceMappingURL=base-capi.js.map
|
|
@@ -0,0 +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,20 +1,7 @@
|
|
|
1
1
|
import { Content } from '../model/Content';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { List } from '../model/List';
|
|
5
|
-
import { Person } from '../model/Person';
|
|
6
|
-
import { ImageSet } from '../model/schemas/capi/internal-content';
|
|
7
|
-
export declare class CapiDataSource extends InstrumentedRESTDataSource {
|
|
8
|
-
baseURL: string;
|
|
9
|
-
capiKey: string;
|
|
10
|
-
articleCacheTTL: number;
|
|
11
|
-
peopleCacheTTL: number;
|
|
2
|
+
import { BaseCapiDataSource } from './base-capi';
|
|
3
|
+
export declare class CapiDataSource extends BaseCapiDataSource {
|
|
12
4
|
get backendSystemCode(): string;
|
|
13
|
-
nextNotificationLink?: string;
|
|
14
|
-
willSendRequest(path: string, request: AugmentedRequest): void;
|
|
15
5
|
getContent(uuid: string, packageContainer?: Content): Promise<Content>;
|
|
16
|
-
getImageSet(uuid: string): Promise<ImageSet>;
|
|
17
|
-
getPerson(uuid: string): Promise<Person>;
|
|
18
|
-
getList(uuid: string): Promise<List>;
|
|
19
6
|
getHTTPCacheKeyForContent(uuid: string): Promise<string>;
|
|
20
7
|
}
|
package/lib/datasources/capi.js
CHANGED
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CapiDataSource = void 0;
|
|
4
4
|
const Content_1 = require("../model/Content");
|
|
5
|
-
const
|
|
6
|
-
const List_1 = require("../model/List");
|
|
7
|
-
const Person_1 = require("../model/Person");
|
|
8
|
-
const REQUEST_TIMEOUT = process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT
|
|
9
|
-
? parseInt(process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT)
|
|
10
|
-
: 500;
|
|
5
|
+
const base_capi_1 = require("./base-capi");
|
|
11
6
|
const isPrefixing = (cache) => 'prefix' in cache;
|
|
12
7
|
function getWrappedCacheKey(cache) {
|
|
13
8
|
if (isPrefixing(cache)) {
|
|
@@ -15,24 +10,10 @@ function getWrappedCacheKey(cache) {
|
|
|
15
10
|
}
|
|
16
11
|
return '';
|
|
17
12
|
}
|
|
18
|
-
class CapiDataSource extends
|
|
19
|
-
baseURL = process.env.CAPI_URL || 'https://api-t.ft.com/';
|
|
20
|
-
capiKey = process.env.CAPI_API_KEY || '';
|
|
21
|
-
articleCacheTTL = process.env.ARTICLE_CACHE_TTL
|
|
22
|
-
? parseInt(process.env.ARTICLE_CACHE_TTL)
|
|
23
|
-
: 60 * 60 * 24 * 7 * 4; // 4 weeks
|
|
24
|
-
peopleCacheTTL = process.env.PEOPLE_CACHE_TTL
|
|
25
|
-
? parseInt(process.env.PEOPLE_CACHE_TTL)
|
|
26
|
-
: 600; // 10 minutes
|
|
13
|
+
class CapiDataSource extends base_capi_1.BaseCapiDataSource {
|
|
27
14
|
get backendSystemCode() {
|
|
28
15
|
return 'up-ica';
|
|
29
16
|
}
|
|
30
|
-
nextNotificationLink;
|
|
31
|
-
willSendRequest(path, request) {
|
|
32
|
-
super.willSendRequest(path, request);
|
|
33
|
-
request.signal = AbortSignal.timeout(REQUEST_TIMEOUT);
|
|
34
|
-
request.headers['x-api-key'] = this.capiKey;
|
|
35
|
-
}
|
|
36
17
|
async getContent(uuid, packageContainer) {
|
|
37
18
|
this.context.addSurrogateKeys([
|
|
38
19
|
{
|
|
@@ -47,28 +28,6 @@ class CapiDataSource extends instrumented_1.InstrumentedRESTDataSource {
|
|
|
47
28
|
this.calls.push(uuid);
|
|
48
29
|
return Content_1.Content.fromJSON(content, this.context, packageContainer);
|
|
49
30
|
}
|
|
50
|
-
async getImageSet(uuid) {
|
|
51
|
-
const content = await this.get(`enrichedcontent/${uuid}`, {
|
|
52
|
-
cacheOptions: { ttl: this.articleCacheTTL },
|
|
53
|
-
});
|
|
54
|
-
return content;
|
|
55
|
-
}
|
|
56
|
-
async getPerson(uuid) {
|
|
57
|
-
const person = await this.get(`people/${uuid}`, {
|
|
58
|
-
cacheOptions: { ttl: this.peopleCacheTTL },
|
|
59
|
-
});
|
|
60
|
-
return Person_1.Person.fromJson(person, this.context);
|
|
61
|
-
}
|
|
62
|
-
async getList(uuid) {
|
|
63
|
-
this.context.addSurrogateKeys([
|
|
64
|
-
{
|
|
65
|
-
prefix: 'contentPipelineList',
|
|
66
|
-
id: uuid,
|
|
67
|
-
},
|
|
68
|
-
]);
|
|
69
|
-
const list = await this.get(`lists/${uuid}`);
|
|
70
|
-
return List_1.List.fromJSON(list, this.context);
|
|
71
|
-
}
|
|
72
31
|
// replicates the logic implicit in PrefixingKeyValueCache to
|
|
73
32
|
// construct a cache key for an article used to purge Redis
|
|
74
33
|
async getHTTPCacheKeyForContent(uuid) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capi.js","sourceRoot":"","sources":["../../src/datasources/capi.ts"],"names":[],"mappings":";;;AAAA,8CAA0C;
|
|
1
|
+
{"version":3,"file":"capi.js","sourceRoot":"","sources":["../../src/datasources/capi.ts"],"names":[],"mappings":";;;AAAA,8CAA0C;AAM1C,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,cAAe,SAAQ,8BAAkB;IACpD,IAAI,iBAAiB;QACnB,OAAO,QAAQ,CAAA;IACjB,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,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,EAAE;YACxD,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,iBAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAA;IAClE,CAAC;IAED,6DAA6D;IAC7D,2DAA2D;IAC3D,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,CAAC,mBAAmB,IAAI,EAAE,EAAE,YAAY,CAAC,CAC/D,CAAA;QAED,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,GAAG,QAAQ,EAAE,CAAA;IAC5E,CAAC;CACF;AAnCD,wCAmCC"}
|
|
@@ -5,6 +5,7 @@ const capi_1 = require("./capi");
|
|
|
5
5
|
const Content_1 = require("../model/Content");
|
|
6
6
|
const logger = new logger_1.Logger();
|
|
7
7
|
const metrics = { count: jest.fn(), aggregate: jest.fn() };
|
|
8
|
+
let fetchMock;
|
|
8
9
|
const cache = {
|
|
9
10
|
get: jest.fn(),
|
|
10
11
|
set: jest.fn(),
|
|
@@ -21,15 +22,23 @@ const capi = new capi_1.CapiDataSource({
|
|
|
21
22
|
context,
|
|
22
23
|
});
|
|
23
24
|
const id = '00000000-0000-0000-0000-000000000000';
|
|
25
|
+
const fetchResult = {
|
|
26
|
+
parsedBody: { id },
|
|
27
|
+
};
|
|
24
28
|
describe('CapiDataSource', () => {
|
|
25
29
|
beforeAll(() => {
|
|
26
|
-
jest.spyOn(global, 'fetch').mockImplementation(async () => new Response(JSON.stringify({ id }), {
|
|
30
|
+
fetchMock = jest.spyOn(global, 'fetch').mockImplementation(async () => new Response(JSON.stringify({ id }), {
|
|
27
31
|
headers: new Headers([['content-type', 'application/json']]),
|
|
28
32
|
}));
|
|
29
33
|
jest
|
|
30
34
|
.spyOn(Content_1.Content, 'fromJSON')
|
|
31
35
|
.mockImplementation((content, context) => new Content_1.Content(content, context));
|
|
32
36
|
});
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
jest.clearAllMocks();
|
|
39
|
+
delete context.contentRequestedOnce;
|
|
40
|
+
delete context.requestId;
|
|
41
|
+
});
|
|
33
42
|
describe('cache keys', () => {
|
|
34
43
|
it('should return the same cache key that it actually stored in the cache', async () => {
|
|
35
44
|
await capi.getContent(id);
|
|
@@ -37,7 +46,17 @@ describe('CapiDataSource', () => {
|
|
|
37
46
|
await expect(capi.getHTTPCacheKeyForContent(id)).resolves.toBe(cache.set.mock.lastCall?.[0]);
|
|
38
47
|
});
|
|
39
48
|
});
|
|
40
|
-
describe('
|
|
49
|
+
describe('error handling', () => {
|
|
50
|
+
it('should include the system code in errors', async () => {
|
|
51
|
+
fetchMock.mockResolvedValueOnce(new Response('Errored', {
|
|
52
|
+
status: 500,
|
|
53
|
+
}));
|
|
54
|
+
await expect(capi.getContent(id)).rejects.toMatchObject({
|
|
55
|
+
relatesToSystems: ['up-ica'],
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('getContent', () => {
|
|
41
60
|
it('should add uuid to the context surrogate keys', async () => {
|
|
42
61
|
await capi.getContent(id);
|
|
43
62
|
expect(context.addSurrogateKeys).toBeCalledWith(expect.arrayContaining([
|
|
@@ -47,9 +66,44 @@ describe('CapiDataSource', () => {
|
|
|
47
66
|
}),
|
|
48
67
|
]));
|
|
49
68
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
it('should set cacheOptions ttl', async () => {
|
|
70
|
+
const fetchSpy = jest.spyOn(capi, 'fetch').mockResolvedValue(fetchResult);
|
|
71
|
+
await capi.getContent(id);
|
|
72
|
+
expect(fetchSpy).toBeCalledWith(`internalcontent/${id}`, {
|
|
73
|
+
method: 'GET',
|
|
74
|
+
cacheOptions: { ttl: capi.articleCacheTTL },
|
|
75
|
+
});
|
|
76
|
+
fetchSpy.mockRestore();
|
|
77
|
+
});
|
|
78
|
+
it('should fetch from "/internalcontent"', async () => {
|
|
79
|
+
await capi.getContent(id);
|
|
80
|
+
expect(global.fetch).toBeCalledWith(`https://api-t.ft.com/internalcontent/${id}`, expect.objectContaining({
|
|
81
|
+
headers: expect.objectContaining({
|
|
82
|
+
'user-agent': 'cp-content-pipeline-api/1.0.0 cp-content-pipeline-schema/1.0.0 ',
|
|
83
|
+
'x-api-key': '',
|
|
84
|
+
}),
|
|
85
|
+
signal: expect.any(AbortSignal),
|
|
86
|
+
}));
|
|
87
|
+
});
|
|
88
|
+
it('should include the request id on the first content request', async () => {
|
|
89
|
+
context.requestId = 'request-id';
|
|
90
|
+
await capi.getContent(id);
|
|
91
|
+
expect(global.fetch).toBeCalledWith(`https://api-t.ft.com/internalcontent/${id}`, expect.objectContaining({
|
|
92
|
+
headers: expect.objectContaining({
|
|
93
|
+
'x-request-id': 'request-id',
|
|
94
|
+
}),
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
97
|
+
it('should not include the request id after content has already been requested once', async () => {
|
|
98
|
+
context.requestId = 'request-id';
|
|
99
|
+
await capi.getContent(id);
|
|
100
|
+
await capi.getContent(id);
|
|
101
|
+
expect(global.fetch).toHaveBeenNthCalledWith(2, `https://api-t.ft.com/internalcontent/${id}`, expect.objectContaining({
|
|
102
|
+
headers: expect.not.objectContaining({
|
|
103
|
+
'x-request-id': 'request-id',
|
|
104
|
+
}),
|
|
105
|
+
}));
|
|
106
|
+
});
|
|
53
107
|
});
|
|
54
108
|
});
|
|
55
109
|
//# sourceMappingURL=capi.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capi.test.js","sourceRoot":"","sources":["../../src/datasources/capi.test.ts"],"names":[],"mappings":";;AAAA,2DAAuD;AACvD,iCAAuC;
|
|
1
|
+
{"version":3,"file":"capi.test.js","sourceRoot":"","sources":["../../src/datasources/capi.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ImageSet } from '../model/schemas/capi/internal-content';
|
|
2
|
+
import { BaseCapiDataSource } from './base-capi';
|
|
3
|
+
export declare class EnrichedContentDataSource extends BaseCapiDataSource {
|
|
4
|
+
get backendSystemCode(): string;
|
|
5
|
+
getImageSet(uuid: string): Promise<ImageSet>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EnrichedContentDataSource = void 0;
|
|
4
|
+
const base_capi_1 = require("./base-capi");
|
|
5
|
+
class EnrichedContentDataSource extends base_capi_1.BaseCapiDataSource {
|
|
6
|
+
get backendSystemCode() {
|
|
7
|
+
return 'enriched-content-read-api';
|
|
8
|
+
}
|
|
9
|
+
async getImageSet(uuid) {
|
|
10
|
+
const content = await this.get(`enrichedcontent/${uuid}`, {
|
|
11
|
+
cacheOptions: { ttl: this.articleCacheTTL },
|
|
12
|
+
});
|
|
13
|
+
return content;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.EnrichedContentDataSource = EnrichedContentDataSource;
|
|
17
|
+
//# sourceMappingURL=enriched-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enriched-content.js","sourceRoot":"","sources":["../../src/datasources/enriched-content.ts"],"names":[],"mappings":";;;AACA,2CAAgD;AAEhD,MAAa,yBAA0B,SAAQ,8BAAkB;IAC/D,IAAI,iBAAiB;QACnB,OAAO,2BAA2B,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAY;QAC5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,EAAE;YACxD,YAAY,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE;SAC5C,CAAC,CAAA;QAEF,OAAO,OAAO,CAAA;IAChB,CAAC;CACF;AAZD,8DAYC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const logger_1 = require("@dotcom-reliability-kit/logger");
|
|
4
|
+
const enriched_content_1 = require("./enriched-content");
|
|
5
|
+
const logger = new logger_1.Logger();
|
|
6
|
+
const metrics = { count: jest.fn(), aggregate: jest.fn() };
|
|
7
|
+
let fetchMock;
|
|
8
|
+
const cache = {
|
|
9
|
+
get: jest.fn(),
|
|
10
|
+
set: jest.fn(),
|
|
11
|
+
delete: jest.fn(),
|
|
12
|
+
};
|
|
13
|
+
const context = {
|
|
14
|
+
versions: { api: '1.0.0', schema: '1.0.0' },
|
|
15
|
+
logger,
|
|
16
|
+
metrics,
|
|
17
|
+
addSurrogateKeys: jest.fn(),
|
|
18
|
+
};
|
|
19
|
+
const enrichedContent = new enriched_content_1.EnrichedContentDataSource({
|
|
20
|
+
cache,
|
|
21
|
+
context,
|
|
22
|
+
});
|
|
23
|
+
const id = '00000000-0000-0000-0000-000000000000';
|
|
24
|
+
const fetchResult = {
|
|
25
|
+
parsedBody: { id },
|
|
26
|
+
};
|
|
27
|
+
describe('EnrichedContentDataSource', () => {
|
|
28
|
+
beforeAll(() => {
|
|
29
|
+
fetchMock = jest.spyOn(global, 'fetch').mockImplementation(async () => new Response(JSON.stringify({ id }), {
|
|
30
|
+
headers: new Headers([['content-type', 'application/json']]),
|
|
31
|
+
}));
|
|
32
|
+
});
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
jest.clearAllMocks();
|
|
35
|
+
delete context.requestId;
|
|
36
|
+
});
|
|
37
|
+
describe('error handling', () => {
|
|
38
|
+
it('should include the system code in errors', async () => {
|
|
39
|
+
fetchMock.mockResolvedValueOnce(new Response('Errored', {
|
|
40
|
+
status: 500,
|
|
41
|
+
}));
|
|
42
|
+
await expect(enrichedContent.getImageSet(id)).rejects.toMatchObject({
|
|
43
|
+
relatesToSystems: ['enriched-content-read-api'],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('getImageSet', () => {
|
|
48
|
+
it('should fetch from "/enrichedcontent"', async () => {
|
|
49
|
+
context.requestId = 'request-image-id';
|
|
50
|
+
await enrichedContent.getImageSet(id);
|
|
51
|
+
expect(global.fetch).toBeCalledWith(`https://api-t.ft.com/enrichedcontent/${id}`, expect.objectContaining({
|
|
52
|
+
headers: expect.objectContaining({
|
|
53
|
+
'user-agent': 'cp-content-pipeline-api/1.0.0 cp-content-pipeline-schema/1.0.0 ',
|
|
54
|
+
'x-api-key': '',
|
|
55
|
+
'x-request-id': 'request-image-id',
|
|
56
|
+
}),
|
|
57
|
+
signal: expect.any(AbortSignal),
|
|
58
|
+
}));
|
|
59
|
+
});
|
|
60
|
+
it('should set cacheOptions ttl', async () => {
|
|
61
|
+
const fetchSpy = jest
|
|
62
|
+
.spyOn(enrichedContent, 'fetch')
|
|
63
|
+
.mockResolvedValue(fetchResult);
|
|
64
|
+
await enrichedContent.getImageSet(id);
|
|
65
|
+
expect(fetchSpy).toBeCalledWith(`enrichedcontent/${id}`, {
|
|
66
|
+
method: 'GET',
|
|
67
|
+
cacheOptions: { ttl: enrichedContent.articleCacheTTL },
|
|
68
|
+
});
|
|
69
|
+
fetchSpy.mockRestore();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=enriched-content.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enriched-content.test.js","sourceRoot":"","sources":["../../src/datasources/enriched-content.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { BaseDataSourceOptions } from './base';
|
|
2
2
|
import { CapiDataSource } from './capi';
|
|
3
|
+
import { EnrichedContentDataSource } from './enriched-content';
|
|
4
|
+
import { ListsDataSource } from './lists';
|
|
3
5
|
import { OrigamiImageDataSource } from './origami-image';
|
|
6
|
+
import { PeopleDataSource } from './people';
|
|
4
7
|
import { TwitterDataSource } from './twitter';
|
|
5
8
|
import { URLManagementDataSource } from './url-management';
|
|
6
9
|
export declare const initDataSources: (options: BaseDataSourceOptions) => {
|
|
7
10
|
capi: CapiDataSource;
|
|
11
|
+
enrichedContent: EnrichedContentDataSource;
|
|
12
|
+
lists: ListsDataSource;
|
|
8
13
|
origami: OrigamiImageDataSource;
|
|
14
|
+
people: PeopleDataSource;
|
|
9
15
|
vanityUrls: URLManagementDataSource;
|
|
10
16
|
twitter: TwitterDataSource;
|
|
11
17
|
};
|
package/lib/datasources/index.js
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.initDataSources = void 0;
|
|
4
4
|
const capi_1 = require("./capi");
|
|
5
|
+
const enriched_content_1 = require("./enriched-content");
|
|
6
|
+
const lists_1 = require("./lists");
|
|
5
7
|
const origami_image_1 = require("./origami-image");
|
|
8
|
+
const people_1 = require("./people");
|
|
6
9
|
const twitter_1 = require("./twitter");
|
|
7
10
|
const url_management_1 = require("./url-management");
|
|
8
11
|
const initDataSources = (options) => ({
|
|
9
12
|
capi: new capi_1.CapiDataSource(options),
|
|
13
|
+
enrichedContent: new enriched_content_1.EnrichedContentDataSource(options),
|
|
14
|
+
lists: new lists_1.ListsDataSource(options),
|
|
10
15
|
origami: new origami_image_1.OrigamiImageDataSource(options),
|
|
16
|
+
people: new people_1.PeopleDataSource(options),
|
|
11
17
|
vanityUrls: new url_management_1.URLManagementDataSource(options),
|
|
12
18
|
twitter: new twitter_1.TwitterDataSource(options),
|
|
13
19
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/datasources/index.ts"],"names":[],"mappings":";;;AACA,iCAAuC;AACvC,mDAAwD;AACxD,uCAA6C;AAC7C,qDAA0D;AAEnD,MAAM,eAAe,GAAG,CAAC,OAA8B,EAAE,EAAE,CAAC,CAAC;IAClE,IAAI,EAAE,IAAI,qBAAc,CAAC,OAAO,CAAC;IACjC,OAAO,EAAE,IAAI,sCAAsB,CAAC,OAAO,CAAC;IAC5C,UAAU,EAAE,IAAI,wCAAuB,CAAC,OAAO,CAAC;IAChD,OAAO,EAAE,IAAI,2BAAiB,CAAC,OAAO,CAAC;CACxC,CAAC,CAAA;
|
|
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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ListsDataSource = void 0;
|
|
4
|
+
const List_1 = require("../model/List");
|
|
5
|
+
const base_capi_1 = require("./base-capi");
|
|
6
|
+
class ListsDataSource extends base_capi_1.BaseCapiDataSource {
|
|
7
|
+
get backendSystemCode() {
|
|
8
|
+
return 'public-lists-api';
|
|
9
|
+
}
|
|
10
|
+
async getList(uuid) {
|
|
11
|
+
this.context.addSurrogateKeys([
|
|
12
|
+
{
|
|
13
|
+
prefix: 'contentPipelineList',
|
|
14
|
+
id: uuid,
|
|
15
|
+
},
|
|
16
|
+
]);
|
|
17
|
+
const list = await this.get(`lists/${uuid}`);
|
|
18
|
+
return List_1.List.fromJSON(list, this.context);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.ListsDataSource = ListsDataSource;
|
|
22
|
+
//# sourceMappingURL=lists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lists.js","sourceRoot":"","sources":["../../src/datasources/lists.ts"],"names":[],"mappings":";;;AAAA,wCAAoC;AACpC,2CAAgD;AAEhD,MAAa,eAAgB,SAAQ,8BAAkB;IACrD,IAAI,iBAAiB;QACnB,OAAO,kBAAkB,CAAA;IAC3B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC5B;gBACE,MAAM,EAAE,qBAAqB;gBAC7B,EAAE,EAAE,IAAI;aACT;SACF,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAA;QAC5C,OAAO,WAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAC1C,CAAC;CACF;AAhBD,0CAgBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const logger_1 = require("@dotcom-reliability-kit/logger");
|
|
4
|
+
const List_1 = require("../model/List");
|
|
5
|
+
const lists_1 = require("./lists");
|
|
6
|
+
const logger = new logger_1.Logger();
|
|
7
|
+
const metrics = { count: jest.fn(), aggregate: jest.fn() };
|
|
8
|
+
let fetchMock;
|
|
9
|
+
const cache = {
|
|
10
|
+
get: jest.fn(),
|
|
11
|
+
set: jest.fn(),
|
|
12
|
+
delete: jest.fn(),
|
|
13
|
+
};
|
|
14
|
+
const context = {
|
|
15
|
+
versions: { api: '1.0.0', schema: '1.0.0' },
|
|
16
|
+
logger,
|
|
17
|
+
metrics,
|
|
18
|
+
addSurrogateKeys: jest.fn(),
|
|
19
|
+
};
|
|
20
|
+
const lists = new lists_1.ListsDataSource({
|
|
21
|
+
cache,
|
|
22
|
+
context,
|
|
23
|
+
});
|
|
24
|
+
const id = '00000000-0000-0000-0000-000000000000';
|
|
25
|
+
const fetchResult = {
|
|
26
|
+
parsedBody: { id },
|
|
27
|
+
};
|
|
28
|
+
describe('ListsDataSource', () => {
|
|
29
|
+
beforeAll(() => {
|
|
30
|
+
fetchMock = jest.spyOn(global, 'fetch').mockImplementation(async () => new Response(JSON.stringify({ id }), {
|
|
31
|
+
headers: new Headers([['content-type', 'application/json']]),
|
|
32
|
+
}));
|
|
33
|
+
jest
|
|
34
|
+
.spyOn(List_1.List, 'fromJSON')
|
|
35
|
+
.mockImplementation((list, context) => new List_1.List(list, context));
|
|
36
|
+
});
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
jest.clearAllMocks();
|
|
39
|
+
delete context.requestId;
|
|
40
|
+
});
|
|
41
|
+
describe('error handling', () => {
|
|
42
|
+
it('should include the system code in errors', async () => {
|
|
43
|
+
fetchMock.mockResolvedValueOnce(new Response('Errored', {
|
|
44
|
+
status: 500,
|
|
45
|
+
}));
|
|
46
|
+
await expect(lists.getList(id)).rejects.toMatchObject({
|
|
47
|
+
relatesToSystems: ['public-lists-api'],
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('getList', () => {
|
|
52
|
+
it('should add uuid to the context surrogate keys', async () => {
|
|
53
|
+
await lists.getList(id);
|
|
54
|
+
expect(context.addSurrogateKeys).toBeCalledWith(expect.arrayContaining([
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
prefix: 'contentPipelineList',
|
|
57
|
+
id,
|
|
58
|
+
}),
|
|
59
|
+
]));
|
|
60
|
+
});
|
|
61
|
+
it('should fetch from "/lists"', async () => {
|
|
62
|
+
context.requestId = 'request-id-4';
|
|
63
|
+
await lists.getList(id);
|
|
64
|
+
expect(global.fetch).toBeCalledWith(`https://api-t.ft.com/lists/${id}`, expect.objectContaining({
|
|
65
|
+
headers: expect.objectContaining({
|
|
66
|
+
'user-agent': 'cp-content-pipeline-api/1.0.0 cp-content-pipeline-schema/1.0.0 ',
|
|
67
|
+
'x-api-key': '',
|
|
68
|
+
'x-request-id': 'request-id-4',
|
|
69
|
+
}),
|
|
70
|
+
signal: expect.any(AbortSignal),
|
|
71
|
+
}));
|
|
72
|
+
});
|
|
73
|
+
it('should not set cacheOptions', async () => {
|
|
74
|
+
const fetchSpy = jest.spyOn(lists, 'fetch').mockResolvedValue(fetchResult);
|
|
75
|
+
await lists.getList(id);
|
|
76
|
+
expect(fetchSpy).toBeCalledWith(`lists/${id}`, {
|
|
77
|
+
method: 'GET',
|
|
78
|
+
});
|
|
79
|
+
fetchSpy.mockRestore();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=lists.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lists.test.js","sourceRoot":"","sources":["../../src/datasources/lists.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Person } from '../model/Person';
|
|
2
|
+
import { BaseCapiDataSource } from './base-capi';
|
|
3
|
+
export declare class PeopleDataSource extends BaseCapiDataSource {
|
|
4
|
+
peopleCacheTTL: number;
|
|
5
|
+
get backendSystemCode(): string;
|
|
6
|
+
getPerson(uuid: string): Promise<Person>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PeopleDataSource = void 0;
|
|
4
|
+
const Person_1 = require("../model/Person");
|
|
5
|
+
const base_capi_1 = require("./base-capi");
|
|
6
|
+
class PeopleDataSource extends base_capi_1.BaseCapiDataSource {
|
|
7
|
+
peopleCacheTTL = process.env.PEOPLE_CACHE_TTL
|
|
8
|
+
? parseInt(process.env.PEOPLE_CACHE_TTL)
|
|
9
|
+
: 600; // 10 minutes
|
|
10
|
+
get backendSystemCode() {
|
|
11
|
+
return 'public-people-api';
|
|
12
|
+
}
|
|
13
|
+
async getPerson(uuid) {
|
|
14
|
+
const person = await this.get(`people/${uuid}`, {
|
|
15
|
+
cacheOptions: { ttl: this.peopleCacheTTL },
|
|
16
|
+
});
|
|
17
|
+
return Person_1.Person.fromJson(person, this.context);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.PeopleDataSource = PeopleDataSource;
|
|
21
|
+
//# sourceMappingURL=people.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"people.js","sourceRoot":"","sources":["../../src/datasources/people.ts"],"names":[],"mappings":";;;AAAA,4CAAwC;AACxC,2CAAgD;AAEhD,MAAa,gBAAiB,SAAQ,8BAAkB;IACtD,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAC3C,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QACxC,CAAC,CAAC,GAAG,CAAA,CAAC,aAAa;IAErB,IAAI,iBAAiB;QACnB,OAAO,mBAAmB,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY;QAC1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,EAAE;YAC9C,YAAY,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE;SAC3C,CAAC,CAAA;QAEF,OAAO,eAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAC9C,CAAC;CACF;AAhBD,4CAgBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|