@financial-times/cp-content-pipeline-schema 2.14.2 → 2.15.1
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 +16 -0
- package/lib/datasources/capi.d.ts +2 -0
- package/lib/datasources/capi.js +17 -1
- package/lib/datasources/capi.js.map +1 -1
- package/lib/datasources/capi.test.js +6 -1
- package/lib/datasources/capi.test.js.map +1 -1
- package/lib/datasources/instrumented.js.map +1 -1
- package/lib/datasources/origami-image.js.map +1 -1
- package/lib/datasources/twitter.js.map +1 -1
- package/lib/datasources/url-management.js.map +1 -1
- package/lib/fixtures/dummyContext.js.map +1 -1
- package/lib/generated/index.d.ts +78 -16
- package/lib/helpers/decorateHeadshotUrl.js +1 -1
- package/lib/helpers/decorateHeadshotUrl.js.map +1 -1
- package/lib/helpers/flatten-formatted-zod-errors.d.ts +6 -0
- package/lib/helpers/flatten-formatted-zod-errors.js +39 -0
- package/lib/helpers/flatten-formatted-zod-errors.js.map +1 -0
- package/lib/helpers/imageService.js +1 -1
- package/lib/helpers/imageService.js.map +1 -1
- package/lib/helpers/isError.js +1 -1
- package/lib/helpers/isError.js.map +1 -1
- package/lib/helpers/metadata.js +1 -2
- package/lib/helpers/metadata.js.map +1 -1
- package/lib/index.d.ts +5 -1
- package/lib/index.js.map +1 -1
- package/lib/model/Byline.js +1 -1
- package/lib/model/Byline.js.map +1 -1
- package/lib/model/CapiList.d.ts +17 -0
- package/lib/model/CapiList.js +87 -0
- package/lib/model/CapiList.js.map +1 -0
- package/lib/model/CapiResponse.d.ts +1 -1
- package/lib/model/CapiResponse.js +8 -39
- package/lib/model/CapiResponse.js.map +1 -1
- package/lib/model/Clip.js.map +1 -1
- package/lib/model/Concept.js.map +1 -1
- package/lib/model/FlourishSource.js.map +1 -1
- package/lib/model/Image.d.ts +2 -34
- package/lib/model/Image.js +1 -1
- package/lib/model/Image.js.map +1 -1
- package/lib/model/Person.js.map +1 -1
- package/lib/model/Picture.js.map +1 -1
- package/lib/model/RichText.d.ts +1 -1
- package/lib/model/RichText.js.map +1 -1
- package/lib/model/Topper.d.ts +2 -2
- package/lib/model/Topper.js.map +1 -1
- package/lib/model/schemas/capi/article.d.ts +3 -3
- package/lib/model/schemas/capi/audio.d.ts +3 -3
- package/lib/model/schemas/capi/base-schema.d.ts +3 -3
- package/lib/model/schemas/capi/base-schema.js +1 -1
- package/lib/model/schemas/capi/base-schema.js.map +1 -1
- package/lib/model/schemas/capi/content-package.d.ts +3 -3
- package/lib/model/schemas/capi/index.d.ts +15 -15
- package/lib/model/schemas/capi/index.js.map +1 -1
- package/lib/model/schemas/capi/list.d.ts +48 -0
- package/lib/model/schemas/capi/list.js +35 -0
- package/lib/model/schemas/capi/list.js.map +1 -0
- package/lib/model/schemas/capi/live-blog-package.d.ts +3 -3
- package/lib/model/schemas/capi/placeholder.d.ts +3 -3
- package/lib/model/schemas/capi/video.d.ts +3 -3
- package/lib/resolvers/clip.d.ts +5 -5
- package/lib/resolvers/content-tree/bodyXMLToTree.js +1 -1
- package/lib/resolvers/content-tree/bodyXMLToTree.js.map +1 -1
- package/lib/resolvers/content-tree/extractText.js +1 -1
- package/lib/resolvers/content-tree/extractText.js.map +1 -1
- package/lib/resolvers/content-tree/nodePredicates.d.ts +3 -3
- package/lib/resolvers/content-tree/nodePredicates.js.map +1 -1
- package/lib/resolvers/content-tree/references/ClipSet.d.ts +2 -2
- package/lib/resolvers/content-tree/references/ClipSet.js.map +1 -1
- package/lib/resolvers/content-tree/references/Flourish.js.map +1 -1
- package/lib/resolvers/content-tree/references/RawImage.js.map +1 -1
- package/lib/resolvers/content-tree/references/Recommended.js.map +1 -1
- package/lib/resolvers/content-tree/references/Reference.d.ts +2 -2
- package/lib/resolvers/content-tree/references/Reference.js.map +1 -1
- package/lib/resolvers/content-tree/references/Tweet.js.map +1 -1
- package/lib/resolvers/content-tree/references/Video.js.map +1 -1
- package/lib/resolvers/content-tree/tagMappings.js +5 -5
- package/lib/resolvers/content-tree/tagMappings.js.map +1 -1
- package/lib/resolvers/content-tree/updateTreeWithReferenceIds.js +1 -1
- package/lib/resolvers/content-tree/updateTreeWithReferenceIds.js.map +1 -1
- package/lib/resolvers/content.d.ts +16 -16
- package/lib/resolvers/content.js.map +1 -1
- package/lib/resolvers/core.d.ts +1 -0
- package/lib/resolvers/core.js +27 -21
- package/lib/resolvers/core.js.map +1 -1
- package/lib/resolvers/image.js.map +1 -1
- package/lib/resolvers/index.d.ts +56 -44
- package/lib/resolvers/index.js +2 -0
- package/lib/resolvers/index.js.map +1 -1
- package/lib/resolvers/list.d.ts +14 -0
- package/lib/resolvers/list.js +16 -0
- package/lib/resolvers/list.js.map +1 -0
- package/lib/resolvers/literal-union.js.map +1 -1
- package/lib/resolvers/meta-link.js.map +1 -1
- package/lib/resolvers/richText.d.ts +5 -5
- package/lib/resolvers/scalars.d.ts +4 -0
- package/lib/resolvers/scalars.js +18 -1
- package/lib/resolvers/scalars.js.map +1 -1
- package/lib/types/cache.js +1 -2
- package/lib/types/cache.js.map +1 -1
- package/package.json +1 -1
- package/src/datasources/capi.test.ts +6 -1
- package/src/datasources/capi.ts +19 -1
- package/src/fixtures/dummyContext.ts +1 -1
- package/src/generated/index.ts +78 -16
- package/src/helpers/flatten-formatted-zod-errors.ts +67 -0
- package/src/index.ts +3 -1
- package/src/model/CapiList.ts +103 -0
- package/src/model/CapiResponse.ts +8 -70
- package/src/model/schemas/capi/base-schema.ts +1 -1
- package/src/model/schemas/capi/list.ts +36 -0
- package/src/resolvers/core.ts +29 -22
- package/src/resolvers/index.ts +2 -0
- package/src/resolvers/list.ts +15 -0
- package/src/resolvers/scalars.ts +30 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/content.graphql +8 -8
- package/typedefs/core.graphql +3 -0
- package/typedefs/list.graphql +20 -0
- package/typedefs/scalars.graphql +2 -0
package/src/datasources/capi.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
KeyValueCache,
|
|
7
7
|
PrefixingKeyValueCache,
|
|
8
8
|
} from '@apollo/utils.keyvaluecache'
|
|
9
|
+
import { CapiList } from '../model/CapiList'
|
|
9
10
|
import TimeoutError from '../helpers/timeout-error'
|
|
10
11
|
|
|
11
12
|
const REQUEST_TIMEOUT = process.env.CAPI_DATASOURCE_REQUEST_TIMEOUT
|
|
@@ -57,7 +58,12 @@ export class CapiDataSource extends InstrumentedRESTDataSource {
|
|
|
57
58
|
uuid: string,
|
|
58
59
|
packageContainer?: CapiResponse
|
|
59
60
|
): Promise<CapiResponse> {
|
|
60
|
-
this.context.addSurrogateKeys([
|
|
61
|
+
this.context.addSurrogateKeys([
|
|
62
|
+
{
|
|
63
|
+
prefix: 'contentPipelineArticle',
|
|
64
|
+
id: uuid,
|
|
65
|
+
},
|
|
66
|
+
])
|
|
61
67
|
|
|
62
68
|
const content = await this.get(`internalcontent/${uuid}`, {
|
|
63
69
|
cacheOptions: { ttl: this.articleCacheTTL },
|
|
@@ -79,6 +85,18 @@ export class CapiDataSource extends InstrumentedRESTDataSource {
|
|
|
79
85
|
})
|
|
80
86
|
}
|
|
81
87
|
|
|
88
|
+
async getList(uuid: string): Promise<CapiList> {
|
|
89
|
+
this.context.addSurrogateKeys([
|
|
90
|
+
{
|
|
91
|
+
prefix: 'contentPipelineList',
|
|
92
|
+
id: uuid,
|
|
93
|
+
},
|
|
94
|
+
])
|
|
95
|
+
|
|
96
|
+
const list = await this.get(`lists/${uuid}`)
|
|
97
|
+
return CapiList.fromJSON(list, this.context)
|
|
98
|
+
}
|
|
99
|
+
|
|
82
100
|
// replicates the logic implicit in PrefixingKeyValueCache to
|
|
83
101
|
// construct a cache key for an article used to purge Redis
|
|
84
102
|
async getHTTPCacheKeyForContent(uuid: string) {
|
package/src/generated/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
|
|
2
2
|
import type { Concept as ConceptModel } from '../model/Concept';
|
|
3
3
|
import type { Person as PersonModel } from '../model/Person';
|
|
4
|
+
import type { CapiList } from '../model/CapiList';
|
|
4
5
|
import type { CapiResponse } from '../model/CapiResponse';
|
|
5
6
|
import type { Image as ImageModel } from '../model/Image';
|
|
6
7
|
import type { Clip as ClipModel } from '../model/Clip';
|
|
@@ -37,6 +38,8 @@ export type Scalars = {
|
|
|
37
38
|
ImageFormat: { input: 'standard' | 'standard-inline' | 'desktop' | 'mobile' | 'wide' | 'square' | 'square-ftedit' | 'portrait' | 'landscape'; output: 'standard' | 'standard-inline' | 'desktop' | 'mobile' | 'wide' | 'square' | 'square-ftedit' | 'portrait' | 'landscape'; }
|
|
38
39
|
ImageType: { input: 'image' | 'graphic'; output: 'image' | 'graphic'; }
|
|
39
40
|
JSON: { input: any; output: any; }
|
|
41
|
+
LayoutHint: { input: 'standaloneimage' | 'landscape' | 'bigstory' | 'assassination'; output: 'standaloneimage' | 'landscape' | 'bigstory' | 'assassination'; }
|
|
42
|
+
ListType: { input: 'OpinionAnalysis' | 'Promotional' | 'Recommended' | 'TopStories' | 'TopStoriesBeta' | 'KeyDevelopments'; output: 'OpinionAnalysis' | 'Promotional' | 'Recommended' | 'TopStories' | 'TopStoriesBeta' | 'KeyDevelopments'; }
|
|
40
43
|
PackageDesign: { input: 'special-report' | 'extra' | 'basic' | 'extra-wide'; output: 'special-report' | 'extra' | 'basic' | 'extra-wide'; }
|
|
41
44
|
RichTextSource: { input: 'standfirst' | 'summary' | 'bodyXML' | 'transcript'; output: 'standfirst' | 'summary' | 'bodyXML' | 'transcript'; }
|
|
42
45
|
TopperBackgroundColour: { input: 'paper' | 'wheat' | 'white' | 'black' | 'claret' | 'oxford' | 'slate' | 'crimson' | 'sky' | 'matisse'; output: 'paper' | 'wheat' | 'white' | 'black' | 'claret' | 'oxford' | 'slate' | 'crimson' | 'sky' | 'matisse'; }
|
|
@@ -95,7 +98,7 @@ export type Article = Content & {
|
|
|
95
98
|
/** The party that originated the article, eg. 'FT'. */
|
|
96
99
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
97
100
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
98
|
-
readonly publishReference
|
|
101
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
99
102
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
100
103
|
readonly publishedDate: Scalars['String']['output'];
|
|
101
104
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -161,7 +164,7 @@ export type Audio = Content & {
|
|
|
161
164
|
/** The party that originated the article, eg. 'FT'. */
|
|
162
165
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
163
166
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
164
|
-
readonly publishReference
|
|
167
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
165
168
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
166
169
|
readonly publishedDate: Scalars['String']['output'];
|
|
167
170
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -372,7 +375,7 @@ export type Content = {
|
|
|
372
375
|
/** The party that originated the article, eg. 'FT'. */
|
|
373
376
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
374
377
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
375
|
-
readonly publishReference
|
|
378
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
376
379
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
377
380
|
readonly publishedDate: Scalars['String']['output'];
|
|
378
381
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -440,7 +443,7 @@ export type ContentPackage = Content & {
|
|
|
440
443
|
/** The party that originated the article, eg. 'FT'. */
|
|
441
444
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
442
445
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
443
|
-
readonly publishReference
|
|
446
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
444
447
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
445
448
|
readonly publishedDate: Scalars['String']['output'];
|
|
446
449
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -942,6 +945,27 @@ export type LeadFlourish = {
|
|
|
942
945
|
readonly type?: Maybe<Scalars['String']['output']>;
|
|
943
946
|
};
|
|
944
947
|
|
|
948
|
+
export type List = {
|
|
949
|
+
/** The upstream API URL for the list */
|
|
950
|
+
readonly apiUrl: Scalars['String']['output'];
|
|
951
|
+
/** The UUID of the list */
|
|
952
|
+
readonly id: Scalars['String']['output'];
|
|
953
|
+
/** The content items referenced by this list */
|
|
954
|
+
readonly items: ReadonlyArray<Content>;
|
|
955
|
+
/** Used by ft.com to determine the visual style to use on the website */
|
|
956
|
+
readonly layoutHint?: Maybe<Scalars['LayoutHint']['output']>;
|
|
957
|
+
/** The type of the list, e.g. Recommended */
|
|
958
|
+
readonly listType: Scalars['ListType']['output'];
|
|
959
|
+
/** Array of publication IDs this list pertains to, e.g. 88fdde6c-2aa4-4f78-af02-9f680097cfd6 for FT Pink */
|
|
960
|
+
readonly publication?: Maybe<ReadonlyArray<Scalars['String']['output']>>;
|
|
961
|
+
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
962
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
963
|
+
/** The date when this list was published */
|
|
964
|
+
readonly publishedDate: Scalars['String']['output'];
|
|
965
|
+
/** The title of the list */
|
|
966
|
+
readonly title: Scalars['String']['output'];
|
|
967
|
+
};
|
|
968
|
+
|
|
945
969
|
export type LiveBlogPackage = Content & {
|
|
946
970
|
/** A scalar representing the access level of the article, eg. 'free'. */
|
|
947
971
|
readonly accessLevel?: Maybe<Scalars['AccessLevel']['output']>;
|
|
@@ -980,7 +1004,7 @@ export type LiveBlogPackage = Content & {
|
|
|
980
1004
|
/** The pinned article of this live blog package. */
|
|
981
1005
|
readonly pinnedPost?: Maybe<Content>;
|
|
982
1006
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
983
|
-
readonly publishReference
|
|
1007
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
984
1008
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
985
1009
|
readonly publishedDate: Scalars['String']['output'];
|
|
986
1010
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -1058,7 +1082,7 @@ export type LiveBlogPost = Content & {
|
|
|
1058
1082
|
/** The party that originated the article, eg. 'FT'. */
|
|
1059
1083
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
1060
1084
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
1061
|
-
readonly publishReference
|
|
1085
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
1062
1086
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
1063
1087
|
readonly publishedDate: Scalars['String']['output'];
|
|
1064
1088
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -1288,7 +1312,7 @@ export type Placeholder = Content & {
|
|
|
1288
1312
|
/** The party that originated the article, eg. 'FT'. */
|
|
1289
1313
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
1290
1314
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
1291
|
-
readonly publishReference
|
|
1315
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
1292
1316
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
1293
1317
|
readonly publishedDate: Scalars['String']['output'];
|
|
1294
1318
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -1359,6 +1383,8 @@ export type Query = {
|
|
|
1359
1383
|
readonly content: Content;
|
|
1360
1384
|
/** The article content as resolved from passed-in content-api json data. */
|
|
1361
1385
|
readonly contentFromJSON: Content;
|
|
1386
|
+
/** Resolve a content list from a uuid. */
|
|
1387
|
+
readonly list: List;
|
|
1362
1388
|
/** The version of the schema, eg. 2.0.0. */
|
|
1363
1389
|
readonly version: Scalars['String']['output'];
|
|
1364
1390
|
};
|
|
@@ -1373,6 +1399,11 @@ export type QueryContentFromJsonArgs = {
|
|
|
1373
1399
|
content: Scalars['JSON']['input'];
|
|
1374
1400
|
};
|
|
1375
1401
|
|
|
1402
|
+
|
|
1403
|
+
export type QueryListArgs = {
|
|
1404
|
+
uuid: Scalars['String']['input'];
|
|
1405
|
+
};
|
|
1406
|
+
|
|
1376
1407
|
export type RawImage = Reference & {
|
|
1377
1408
|
/** The raw image object. */
|
|
1378
1409
|
readonly image: Image;
|
|
@@ -1619,7 +1650,7 @@ export type Video = Content & {
|
|
|
1619
1650
|
/** The party that originated the article, eg. 'FT'. */
|
|
1620
1651
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
1621
1652
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
1622
|
-
readonly publishReference
|
|
1653
|
+
readonly publishReference?: Maybe<Scalars['String']['output']>;
|
|
1623
1654
|
/** The ISO string date the article was published, eg. '2024-04-03T10:35:52.443Z'. */
|
|
1624
1655
|
readonly publishedDate: Scalars['String']['output'];
|
|
1625
1656
|
/** The number of milliseconds since the unix epoch the article was published, eg '1712140552443'. */
|
|
@@ -1793,8 +1824,11 @@ export type ResolversTypes = ResolversObject<{
|
|
|
1793
1824
|
Indicators: ResolverTypeWrapper<Indicators>;
|
|
1794
1825
|
Int: ResolverTypeWrapper<Scalars['Int']['output']>;
|
|
1795
1826
|
JSON: ResolverTypeWrapper<Scalars['JSON']['output']>;
|
|
1827
|
+
LayoutHint: ResolverTypeWrapper<Scalars['LayoutHint']['output']>;
|
|
1796
1828
|
LayoutImage: ResolverTypeWrapper<ReferenceWithCAPIData<ContentTree.LayoutImage>>;
|
|
1797
1829
|
LeadFlourish: ResolverTypeWrapper<LeadFlourishModel>;
|
|
1830
|
+
List: ResolverTypeWrapper<CapiList>;
|
|
1831
|
+
ListType: ResolverTypeWrapper<Scalars['ListType']['output']>;
|
|
1798
1832
|
LiveBlogPackage: ResolverTypeWrapper<CapiResponse>;
|
|
1799
1833
|
LiveBlogPost: ResolverTypeWrapper<CapiResponse>;
|
|
1800
1834
|
MainImage: ResolverTypeWrapper<ReferenceWithCAPIData<ContentTree.ImageSet>>;
|
|
@@ -1884,8 +1918,11 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
1884
1918
|
Indicators: Indicators;
|
|
1885
1919
|
Int: Scalars['Int']['output'];
|
|
1886
1920
|
JSON: Scalars['JSON']['output'];
|
|
1921
|
+
LayoutHint: Scalars['LayoutHint']['output'];
|
|
1887
1922
|
LayoutImage: ReferenceWithCAPIData<ContentTree.LayoutImage>;
|
|
1888
1923
|
LeadFlourish: LeadFlourishModel;
|
|
1924
|
+
List: CapiList;
|
|
1925
|
+
ListType: Scalars['ListType']['output'];
|
|
1889
1926
|
LiveBlogPackage: CapiResponse;
|
|
1890
1927
|
LiveBlogPost: CapiResponse;
|
|
1891
1928
|
MainImage: ReferenceWithCAPIData<ContentTree.ImageSet>;
|
|
@@ -1965,7 +2002,7 @@ export type ArticleResolvers<ContextType = QueryContext, ParentType extends Reso
|
|
|
1965
2002
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
1966
2003
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
1967
2004
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1968
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2005
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1969
2006
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1970
2007
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
1971
2008
|
standfirst: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -1995,7 +2032,7 @@ export type AudioResolvers<ContextType = QueryContext, ParentType extends Resolv
|
|
|
1995
2032
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
1996
2033
|
media: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['Media']>>>, ParentType, ContextType>;
|
|
1997
2034
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1998
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2035
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1999
2036
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2000
2037
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
2001
2038
|
standfirst: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -2120,7 +2157,7 @@ export type ContentResolvers<ContextType = QueryContext, ParentType extends Reso
|
|
|
2120
2157
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2121
2158
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2122
2159
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2123
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2160
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2124
2161
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2125
2162
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
2126
2163
|
standfirst: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -2150,7 +2187,7 @@ export type ContentPackageResolvers<ContextType = QueryContext, ParentType exten
|
|
|
2150
2187
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2151
2188
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2152
2189
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2153
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2190
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2154
2191
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2155
2192
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
2156
2193
|
standfirst: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -2426,6 +2463,10 @@ export interface JsonScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes
|
|
|
2426
2463
|
name: 'JSON';
|
|
2427
2464
|
}
|
|
2428
2465
|
|
|
2466
|
+
export interface LayoutHintScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['LayoutHint'], any> {
|
|
2467
|
+
name: 'LayoutHint';
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2429
2470
|
export type LayoutImageResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['LayoutImage'] = ResolversParentTypes['LayoutImage']> = ResolversObject<{
|
|
2430
2471
|
picture: Resolver<Maybe<ResolversTypes['Picture']>, ParentType, ContextType>;
|
|
2431
2472
|
type: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -2440,6 +2481,23 @@ export type LeadFlourishResolvers<ContextType = QueryContext, ParentType extends
|
|
|
2440
2481
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2441
2482
|
}>;
|
|
2442
2483
|
|
|
2484
|
+
export type ListResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['List'] = ResolversParentTypes['List']> = ResolversObject<{
|
|
2485
|
+
apiUrl: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2486
|
+
id: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2487
|
+
items: Resolver<ReadonlyArray<ResolversTypes['Content']>, ParentType, ContextType>;
|
|
2488
|
+
layoutHint: Resolver<Maybe<ResolversTypes['LayoutHint']>, ParentType, ContextType>;
|
|
2489
|
+
listType: Resolver<ResolversTypes['ListType'], ParentType, ContextType>;
|
|
2490
|
+
publication: Resolver<Maybe<ReadonlyArray<ResolversTypes['String']>>, ParentType, ContextType>;
|
|
2491
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2492
|
+
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2493
|
+
title: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2494
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2495
|
+
}>;
|
|
2496
|
+
|
|
2497
|
+
export interface ListTypeScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['ListType'], any> {
|
|
2498
|
+
name: 'ListType';
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2443
2501
|
export type LiveBlogPackageResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['LiveBlogPackage'] = ResolversParentTypes['LiveBlogPackage']> = ResolversObject<{
|
|
2444
2502
|
accessLevel: Resolver<Maybe<ResolversTypes['AccessLevel']>, ParentType, ContextType>;
|
|
2445
2503
|
altStandfirst: Resolver<Maybe<ResolversTypes['AltStandfirst']>, ParentType, ContextType>;
|
|
@@ -2459,7 +2517,7 @@ export type LiveBlogPackageResolvers<ContextType = QueryContext, ParentType exte
|
|
|
2459
2517
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2460
2518
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2461
2519
|
pinnedPost: Resolver<Maybe<ResolversTypes['Content']>, ParentType, ContextType>;
|
|
2462
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2520
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2463
2521
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2464
2522
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
2465
2523
|
realtime: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
@@ -2493,7 +2551,7 @@ export type LiveBlogPostResolvers<ContextType = QueryContext, ParentType extends
|
|
|
2493
2551
|
isPinned: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
2494
2552
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2495
2553
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2496
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2554
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2497
2555
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2498
2556
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
2499
2557
|
standfirst: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -2628,7 +2686,7 @@ export type PlaceholderResolvers<ContextType = QueryContext, ParentType extends
|
|
|
2628
2686
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2629
2687
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2630
2688
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2631
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2689
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2632
2690
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2633
2691
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
2634
2692
|
standfirst: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -2660,6 +2718,7 @@ export type PodcastTopperResolvers<ContextType = QueryContext, ParentType extend
|
|
|
2660
2718
|
export type QueryResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query']> = ResolversObject<{
|
|
2661
2719
|
content: Resolver<ResolversTypes['Content'], ParentType, ContextType, RequireFields<QueryContentArgs, 'uuid'>>;
|
|
2662
2720
|
contentFromJSON: Resolver<ResolversTypes['Content'], ParentType, ContextType, RequireFields<QueryContentFromJsonArgs, 'content'>>;
|
|
2721
|
+
list: Resolver<ResolversTypes['List'], ParentType, ContextType, RequireFields<QueryListArgs, 'uuid'>>;
|
|
2663
2722
|
version: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2664
2723
|
}>;
|
|
2665
2724
|
|
|
@@ -2830,7 +2889,7 @@ export type VideoResolvers<ContextType = QueryContext, ParentType extends Resolv
|
|
|
2830
2889
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2831
2890
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2832
2891
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2833
|
-
publishReference: Resolver<ResolversTypes['String']
|
|
2892
|
+
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2834
2893
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2835
2894
|
publishedTimestamp: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
|
2836
2895
|
standfirst: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -2892,8 +2951,11 @@ export type Resolvers<ContextType = QueryContext> = ResolversObject<{
|
|
|
2892
2951
|
ImageWide: ImageWideResolvers<ContextType>;
|
|
2893
2952
|
Indicators: IndicatorsResolvers<ContextType>;
|
|
2894
2953
|
JSON: GraphQLScalarType;
|
|
2954
|
+
LayoutHint: GraphQLScalarType;
|
|
2895
2955
|
LayoutImage: LayoutImageResolvers<ContextType>;
|
|
2896
2956
|
LeadFlourish: LeadFlourishResolvers<ContextType>;
|
|
2957
|
+
List: ListResolvers<ContextType>;
|
|
2958
|
+
ListType: GraphQLScalarType;
|
|
2897
2959
|
LiveBlogPackage: LiveBlogPackageResolvers<ContextType>;
|
|
2898
2960
|
LiveBlogPost: LiveBlogPostResolvers<ContextType>;
|
|
2899
2961
|
MainImage: MainImageResolvers<ContextType>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
function isPlainObject(object: unknown): object is Record<string, unknown> {
|
|
2
|
+
if (object && typeof object === 'object') {
|
|
3
|
+
return true
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
return false
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type FormattedZodIssues = {
|
|
10
|
+
_errors: string[]
|
|
11
|
+
[key: string]: FormattedZodIssues | string[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function flattenFormattedZodIssues(
|
|
15
|
+
issues: FormattedZodIssues,
|
|
16
|
+
object: unknown,
|
|
17
|
+
path: string[] = [],
|
|
18
|
+
flattened: string[] = []
|
|
19
|
+
): string[] {
|
|
20
|
+
if (issues._errors.length > 0) {
|
|
21
|
+
const literalErrors = issues._errors.filter((error) =>
|
|
22
|
+
error.startsWith('Invalid literal value')
|
|
23
|
+
)
|
|
24
|
+
const otherErrors = issues._errors.filter(
|
|
25
|
+
(error) => !error.startsWith('Invalid literal value')
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if (literalErrors.length > 0) {
|
|
29
|
+
flattened.push(
|
|
30
|
+
`${path.join(
|
|
31
|
+
'.'
|
|
32
|
+
)}: Invalid literal value, received "${object}", expected one of ` +
|
|
33
|
+
literalErrors
|
|
34
|
+
.flatMap((error) => {
|
|
35
|
+
const match = error.match(/expected (".+")$/)
|
|
36
|
+
if (match && match[1]) {
|
|
37
|
+
return [match[1]]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return []
|
|
41
|
+
})
|
|
42
|
+
.join()
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
flattened.push(...otherErrors.map((error) => `${path.join('.')}: ${error}`))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const [key, value] of Object.entries(issues)) {
|
|
50
|
+
if (
|
|
51
|
+
key !== '_errors' &&
|
|
52
|
+
!Array.isArray(value) &&
|
|
53
|
+
object &&
|
|
54
|
+
(Array.isArray(object) || isPlainObject(object))
|
|
55
|
+
) {
|
|
56
|
+
const isArrayKey = !Number.isNaN(parseInt(key))
|
|
57
|
+
flattenFormattedZodIssues(
|
|
58
|
+
value,
|
|
59
|
+
Array.isArray(object) ? object[parseInt(key)] : object[key],
|
|
60
|
+
[...path, isArrayKey ? '[]' : key],
|
|
61
|
+
flattened
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return flattened
|
|
67
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,8 @@ export type BodyXMLToTreeError = {
|
|
|
17
17
|
actual: AnyNode['type'] | AnyNode['type'][]
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export type SurrogateKey = { prefix: string; id: string }
|
|
21
|
+
|
|
20
22
|
export interface QueryContext {
|
|
21
23
|
redisAdapter: KeyValueCache
|
|
22
24
|
metrics: Metrics
|
|
@@ -30,7 +32,7 @@ export interface QueryContext {
|
|
|
30
32
|
schema: string
|
|
31
33
|
}
|
|
32
34
|
aggregatedErrors?: { bodyXMLToTree: BodyXMLToTreeError[] }
|
|
33
|
-
addSurrogateKeys(keys:
|
|
35
|
+
addSurrogateKeys(keys: SurrogateKey[]): void
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export const articleDocumentQuery = readFileSync(
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { BaseError, OperationalError } from '@dotcom-reliability-kit/errors'
|
|
2
|
+
import { QueryContext } from '..'
|
|
3
|
+
import { uuidFromUrl } from '../helpers/metadata'
|
|
4
|
+
import { List, listSchema } from './schemas/capi/list'
|
|
5
|
+
import flattenFormattedZodIssues from '../helpers/flatten-formatted-zod-errors'
|
|
6
|
+
|
|
7
|
+
export class CapiList {
|
|
8
|
+
static fromJSON(list: unknown, context: QueryContext): CapiList {
|
|
9
|
+
const result = listSchema.safeParse(list)
|
|
10
|
+
|
|
11
|
+
if (!result.success) {
|
|
12
|
+
context.logger.warn({
|
|
13
|
+
event: 'RECOVERABLE_ERROR',
|
|
14
|
+
error: new OperationalError({
|
|
15
|
+
message:
|
|
16
|
+
'The data received from the CAPI data source does not match our data source schema. It is likely that our schema will require updating to handle all possible responses from CAPI.',
|
|
17
|
+
code: 'CAPI_SCHEMA_VALIDATION_FAILURE',
|
|
18
|
+
schemaError: flattenFormattedZodIssues(result.error.format(), list),
|
|
19
|
+
contentId: (list as { id?: string }).id,
|
|
20
|
+
contentType: 'List',
|
|
21
|
+
}),
|
|
22
|
+
})
|
|
23
|
+
context.metrics?.count(
|
|
24
|
+
`graphql.datasource.CapiDataSource.List.validation.failure.count`,
|
|
25
|
+
1
|
|
26
|
+
)
|
|
27
|
+
} else {
|
|
28
|
+
context.metrics?.count(
|
|
29
|
+
`graphql.datasource.CapiDataSource.List.validation.success.count`,
|
|
30
|
+
1
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return new CapiList(list as List, context)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor(private list: List, private context: QueryContext) {}
|
|
38
|
+
|
|
39
|
+
async items() {
|
|
40
|
+
if (!this.list.items) return []
|
|
41
|
+
|
|
42
|
+
const items = await Promise.all(
|
|
43
|
+
this.list.items.map(({ id }) =>
|
|
44
|
+
this.context.dataSources.capi
|
|
45
|
+
.getContent(uuidFromUrl(id))
|
|
46
|
+
.catch((error) => {
|
|
47
|
+
if (error instanceof BaseError) {
|
|
48
|
+
if (error.data.upstreamStatusCode === 404) {
|
|
49
|
+
this.context.logger.warn({
|
|
50
|
+
event: 'RECOVERABLE_ERROR',
|
|
51
|
+
error: new OperationalError({
|
|
52
|
+
code: 'LIST_CONTENT_NOT_FOUND',
|
|
53
|
+
message: 'List content not found in Content API',
|
|
54
|
+
uuid: uuidFromUrl(error.data.upstreamUrl),
|
|
55
|
+
cause: error,
|
|
56
|
+
}),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
throw error
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
// filter out missing list items to match n-lists-client behaviour
|
|
69
|
+
return items.filter((item) => item !== null)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
id() {
|
|
73
|
+
return uuidFromUrl(this.list.id)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
apiUrl() {
|
|
77
|
+
return this.list.apiUrl
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
title() {
|
|
81
|
+
return this.list.title
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
listType() {
|
|
85
|
+
return this.list.listType
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
publishedDate() {
|
|
89
|
+
return this.list.publishedDate
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
layoutHint() {
|
|
93
|
+
return this.list.layoutHint ?? null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
publication() {
|
|
97
|
+
return this.list.publication ?? null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
publishReference() {
|
|
101
|
+
return this.list.publishReference ?? null
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -39,25 +39,13 @@ import { Topper } from './Topper'
|
|
|
39
39
|
import { z } from 'zod'
|
|
40
40
|
import { Byline } from './Byline'
|
|
41
41
|
import { RichText } from './RichText'
|
|
42
|
-
|
|
43
|
-
function isPlainObject(object: unknown): object is Record<string, unknown> {
|
|
44
|
-
if (object && typeof object === 'object') {
|
|
45
|
-
return true
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return false
|
|
49
|
-
}
|
|
42
|
+
import flattenFormattedZodIssues from '../helpers/flatten-formatted-zod-errors'
|
|
50
43
|
|
|
51
44
|
type Design = {
|
|
52
45
|
theme: LiteralUnionScalarValues<typeof PackageDesign>
|
|
53
46
|
layout?: 'default' | 'wide'
|
|
54
47
|
}
|
|
55
48
|
|
|
56
|
-
type FormattedZodIssues = {
|
|
57
|
-
_errors: string[]
|
|
58
|
-
[key: string]: FormattedZodIssues | string[]
|
|
59
|
-
}
|
|
60
|
-
|
|
61
49
|
type ZodInputValue =
|
|
62
50
|
| ZodInputObject
|
|
63
51
|
| ZodInputValue[]
|
|
@@ -71,61 +59,6 @@ type ZodInputObject = {
|
|
|
71
59
|
[key: string]: ZodInputValue
|
|
72
60
|
}
|
|
73
61
|
|
|
74
|
-
function flattenFormattedZodIssues(
|
|
75
|
-
issues: FormattedZodIssues,
|
|
76
|
-
object: unknown,
|
|
77
|
-
path: string[] = [],
|
|
78
|
-
flattened: string[] = []
|
|
79
|
-
): string[] {
|
|
80
|
-
if (issues._errors.length > 0) {
|
|
81
|
-
const literalErrors = issues._errors.filter((error) =>
|
|
82
|
-
error.startsWith('Invalid literal value')
|
|
83
|
-
)
|
|
84
|
-
const otherErrors = issues._errors.filter(
|
|
85
|
-
(error) => !error.startsWith('Invalid literal value')
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
if (literalErrors.length > 0) {
|
|
89
|
-
flattened.push(
|
|
90
|
-
`${path.join(
|
|
91
|
-
'.'
|
|
92
|
-
)}: Invalid literal value, received "${object}", expected one of ` +
|
|
93
|
-
literalErrors
|
|
94
|
-
.flatMap((error) => {
|
|
95
|
-
const match = error.match(/expected (".+")$/)
|
|
96
|
-
if (match && match[1]) {
|
|
97
|
-
return [match[1]]
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return []
|
|
101
|
-
})
|
|
102
|
-
.join()
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
flattened.push(...otherErrors.map((error) => `${path.join('.')}: ${error}`))
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
for (const [key, value] of Object.entries(issues)) {
|
|
110
|
-
if (
|
|
111
|
-
key !== '_errors' &&
|
|
112
|
-
!Array.isArray(value) &&
|
|
113
|
-
object &&
|
|
114
|
-
(Array.isArray(object) || isPlainObject(object))
|
|
115
|
-
) {
|
|
116
|
-
const isArrayKey = !Number.isNaN(parseInt(key))
|
|
117
|
-
flattenFormattedZodIssues(
|
|
118
|
-
value,
|
|
119
|
-
Array.isArray(object) ? object[parseInt(key)] : object[key],
|
|
120
|
-
[...path, isArrayKey ? '[]' : key],
|
|
121
|
-
flattened
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return flattened
|
|
127
|
-
}
|
|
128
|
-
|
|
129
62
|
function getContentType(
|
|
130
63
|
content: BaselineContent,
|
|
131
64
|
validate?: true
|
|
@@ -390,7 +323,7 @@ export class CapiResponse {
|
|
|
390
323
|
return this.capiData.firstPublishedDate || this.capiData.publishedDate
|
|
391
324
|
}
|
|
392
325
|
publishReference() {
|
|
393
|
-
return this.capiData.publishReference
|
|
326
|
+
return this.capiData.publishReference ?? null
|
|
394
327
|
}
|
|
395
328
|
alternativeTitle() {
|
|
396
329
|
return this.capiData.alternativeTitles ?? null
|
|
@@ -730,7 +663,12 @@ export class CapiResponse {
|
|
|
730
663
|
const contains = await this.contains()
|
|
731
664
|
|
|
732
665
|
if (contains && contains.length) {
|
|
733
|
-
this.context.addSurrogateKeys(
|
|
666
|
+
this.context.addSurrogateKeys(
|
|
667
|
+
contains.map((article) => ({
|
|
668
|
+
prefix: 'contentPipelineArticle',
|
|
669
|
+
id: article.id(),
|
|
670
|
+
}))
|
|
671
|
+
)
|
|
734
672
|
|
|
735
673
|
const liveBlogPosts = contains.sort(
|
|
736
674
|
(a, b) => b.publishedTimestamp() - a.publishedTimestamp()
|
|
@@ -194,7 +194,7 @@ export const baseMetadataSchema = z.object({
|
|
|
194
194
|
.optional(),
|
|
195
195
|
publishedDate: z.string(),
|
|
196
196
|
firstPublishedDate: z.string(),
|
|
197
|
-
publishReference: z.string(),
|
|
197
|
+
publishReference: z.string().optional(),
|
|
198
198
|
realtime: z.boolean(),
|
|
199
199
|
editorialDesk: z.string().optional(),
|
|
200
200
|
accessLevel: z
|