@financial-times/cp-content-pipeline-schema 3.0.2 → 3.0.4
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/generated/index.d.ts +16 -16
- package/lib/model/CapiResponse.d.ts +1 -1
- package/lib/model/CapiResponse.js +23 -2
- package/lib/model/CapiResponse.js.map +1 -1
- package/lib/model/Person.d.ts +2 -0
- package/lib/model/Person.js +16 -0
- package/lib/model/Person.js.map +1 -1
- package/lib/model/Topper.js +19 -1
- 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/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/content.d.ts +8 -8
- package/lib/resolvers/index.d.ts +8 -8
- package/package.json +1 -1
- package/queries/article.graphql +0 -7
- package/src/generated/index.ts +16 -16
- package/src/model/CapiResponse.ts +22 -6
- package/src/model/Person.ts +19 -0
- package/src/model/Topper.ts +18 -1
- package/src/model/schemas/capi/base-schema.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/content.graphql +8 -8
package/src/generated/index.ts
CHANGED
|
@@ -96,7 +96,7 @@ export type Article = Content & {
|
|
|
96
96
|
/** An image object containing the url and the caption, to be displayed usually before the article content. */
|
|
97
97
|
readonly mainImage?: Maybe<Image>;
|
|
98
98
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
99
|
-
readonly modifiedTimestamp
|
|
99
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
100
100
|
/** The party that originated the article, eg. 'FT'. */
|
|
101
101
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
102
102
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
@@ -169,7 +169,7 @@ export type Audio = Content & {
|
|
|
169
169
|
/** An array of media objects containing the url, the duration, the filesize and the media type. */
|
|
170
170
|
readonly media?: Maybe<ReadonlyArray<Maybe<Media>>>;
|
|
171
171
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
172
|
-
readonly modifiedTimestamp
|
|
172
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
173
173
|
/** The party that originated the article, eg. 'FT'. */
|
|
174
174
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
175
175
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
@@ -466,7 +466,7 @@ export type Content = {
|
|
|
466
466
|
/** An image object containing the url and the caption, to be displayed usually before the article content. */
|
|
467
467
|
readonly mainImage?: Maybe<Image>;
|
|
468
468
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
469
|
-
readonly modifiedTimestamp
|
|
469
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
470
470
|
/** The party that originated the article, eg. 'FT'. */
|
|
471
471
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
472
472
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
@@ -541,7 +541,7 @@ export type ContentPackage = Content & {
|
|
|
541
541
|
/** An image object containing the url and the caption, to be displayed usually before the article content. */
|
|
542
542
|
readonly mainImage?: Maybe<Image>;
|
|
543
543
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
544
|
-
readonly modifiedTimestamp
|
|
544
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
545
545
|
/** The party that originated the article, eg. 'FT'. */
|
|
546
546
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
547
547
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
@@ -1096,7 +1096,7 @@ export type LiveBlogPackage = Content & {
|
|
|
1096
1096
|
/** An image object containing the url and the caption, to be displayed usually before the article content. */
|
|
1097
1097
|
readonly mainImage?: Maybe<Image>;
|
|
1098
1098
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
1099
|
-
readonly modifiedTimestamp
|
|
1099
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
1100
1100
|
/** The party that originated the article, eg. 'FT'. */
|
|
1101
1101
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
1102
1102
|
/** The pinned article of this live blog package. */
|
|
@@ -1182,7 +1182,7 @@ export type LiveBlogPost = Content & {
|
|
|
1182
1182
|
/** An image object containing the url and the caption, to be displayed usually before the article content. */
|
|
1183
1183
|
readonly mainImage?: Maybe<Image>;
|
|
1184
1184
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
1185
|
-
readonly modifiedTimestamp
|
|
1185
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
1186
1186
|
/** The party that originated the article, eg. 'FT'. */
|
|
1187
1187
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
1188
1188
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
@@ -1420,7 +1420,7 @@ export type Placeholder = Content & {
|
|
|
1420
1420
|
/** An image object containing the url and the caption, to be displayed usually before the article content. */
|
|
1421
1421
|
readonly mainImage?: Maybe<Image>;
|
|
1422
1422
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
1423
|
-
readonly modifiedTimestamp
|
|
1423
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
1424
1424
|
/** The party that originated the article, eg. 'FT'. */
|
|
1425
1425
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
1426
1426
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
@@ -1783,7 +1783,7 @@ export type Video = Content & {
|
|
|
1783
1783
|
/** An image object containing the url and the caption, to be displayed usually before the article content. */
|
|
1784
1784
|
readonly mainImage?: Maybe<Image>;
|
|
1785
1785
|
/** The number of milliseconds since the unix epoch the article was last changed, eg '1712140552443'. */
|
|
1786
|
-
readonly modifiedTimestamp
|
|
1786
|
+
readonly modifiedTimestamp?: Maybe<Scalars['Float']['output']>;
|
|
1787
1787
|
/** The party that originated the article, eg. 'FT'. */
|
|
1788
1788
|
readonly originatingParty?: Maybe<Scalars['String']['output']>;
|
|
1789
1789
|
/** A string ID used to trace content publishes through the pipeline, e.g. 'republish_tid_JNhUrBjdXo'. */
|
|
@@ -2152,7 +2152,7 @@ export type ArticleResolvers<ContextType = QueryContext, ParentType extends Reso
|
|
|
2152
2152
|
id: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
2153
2153
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2154
2154
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2155
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
2155
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
2156
2156
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2157
2157
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2158
2158
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -2183,7 +2183,7 @@ export type AudioResolvers<ContextType = QueryContext, ParentType extends Resolv
|
|
|
2183
2183
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2184
2184
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2185
2185
|
media: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['Media']>>>, ParentType, ContextType>;
|
|
2186
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
2186
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
2187
2187
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2188
2188
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2189
2189
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -2342,7 +2342,7 @@ export type ContentResolvers<ContextType = QueryContext, ParentType extends Reso
|
|
|
2342
2342
|
id: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
2343
2343
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2344
2344
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2345
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
2345
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
2346
2346
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2347
2347
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2348
2348
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -2373,7 +2373,7 @@ export type ContentPackageResolvers<ContextType = QueryContext, ParentType exten
|
|
|
2373
2373
|
id: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
2374
2374
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2375
2375
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2376
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
2376
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
2377
2377
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2378
2378
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2379
2379
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -2698,7 +2698,7 @@ export type LiveBlogPackageResolvers<ContextType = QueryContext, ParentType exte
|
|
|
2698
2698
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2699
2699
|
liveBlogPosts: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['Content']>>>, ParentType, ContextType>;
|
|
2700
2700
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2701
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
2701
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
2702
2702
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2703
2703
|
pinnedPost: Resolver<Maybe<ResolversTypes['Content']>, ParentType, ContextType>;
|
|
2704
2704
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -2734,7 +2734,7 @@ export type LiveBlogPostResolvers<ContextType = QueryContext, ParentType extends
|
|
|
2734
2734
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2735
2735
|
isPinned: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
2736
2736
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2737
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
2737
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
2738
2738
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2739
2739
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2740
2740
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -2870,7 +2870,7 @@ export type PlaceholderResolvers<ContextType = QueryContext, ParentType extends
|
|
|
2870
2870
|
id: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
2871
2871
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2872
2872
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
2873
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
2873
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
2874
2874
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2875
2875
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2876
2876
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -3085,7 +3085,7 @@ export type VideoResolvers<ContextType = QueryContext, ParentType extends Resolv
|
|
|
3085
3085
|
id: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
|
3086
3086
|
instantAlertConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
3087
3087
|
mainImage: Resolver<Maybe<ResolversTypes['Image']>, ParentType, ContextType>;
|
|
3088
|
-
modifiedTimestamp: Resolver<ResolversTypes['Float']
|
|
3088
|
+
modifiedTimestamp: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>;
|
|
3089
3089
|
originatingParty: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
3090
3090
|
publishReference: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
3091
3091
|
publishedDate: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
@@ -263,11 +263,25 @@ export class CapiResponse {
|
|
|
263
263
|
async authors(): Promise<Person[]> {
|
|
264
264
|
const authors = this.annotations({ byPredicate: predicates.hasAuthor })
|
|
265
265
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
this.context.dataSources.capi.getPerson(author.uuid())
|
|
269
|
-
)
|
|
270
|
-
|
|
266
|
+
const getPerson = async (author: Concept) => {
|
|
267
|
+
try {
|
|
268
|
+
return await this.context.dataSources.capi.getPerson(author.uuid())
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (isError(error)) {
|
|
271
|
+
this.context.logger.warn({
|
|
272
|
+
event: 'RECOVERABLE_ERROR',
|
|
273
|
+
error: new OperationalError({
|
|
274
|
+
message: `Error fetching CAPI Person for author ${author.prefLabel()}`,
|
|
275
|
+
code: 'AUTHOR_PERSON_FETCH_ERROR',
|
|
276
|
+
cause: error,
|
|
277
|
+
}),
|
|
278
|
+
})
|
|
279
|
+
}
|
|
280
|
+
return Person.fromConcept(author, this.context)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return Promise.all(authors.map(getPerson))
|
|
271
285
|
}
|
|
272
286
|
|
|
273
287
|
async primaryAuthor(): Promise<Person | null> {
|
|
@@ -365,7 +379,9 @@ export class CapiResponse {
|
|
|
365
379
|
}
|
|
366
380
|
|
|
367
381
|
modifiedTimestamp() {
|
|
368
|
-
return
|
|
382
|
+
return this.capiData.lastModified
|
|
383
|
+
? new Date(this.capiData.lastModified).getTime()
|
|
384
|
+
: null
|
|
369
385
|
}
|
|
370
386
|
|
|
371
387
|
publishReference() {
|
package/src/model/Person.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { CapiPerson } from './schemas/capi/internal-content'
|
|
|
7
7
|
import flattenFormattedZodIssues from '../helpers/flatten-formatted-zod-errors'
|
|
8
8
|
import { PersonHeadshotArgs } from '../generated'
|
|
9
9
|
import { uuidFromUrl } from '../helpers/metadata'
|
|
10
|
+
import { Concept } from './Concept'
|
|
10
11
|
|
|
11
12
|
export class Person {
|
|
12
13
|
#systemCode: string
|
|
@@ -39,6 +40,24 @@ export class Person {
|
|
|
39
40
|
return new Person(data as CapiPerson, context)
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
// The concepts returned in the annotations contain *most* of the fields
|
|
44
|
+
// returned by the Person API so we can use one to create a simple Person.
|
|
45
|
+
// Useful as a fallback if the Person API fails.
|
|
46
|
+
static fromConcept(concept: Concept, context: QueryContext): Person {
|
|
47
|
+
const personShim: CapiPerson = {
|
|
48
|
+
id: concept.id(),
|
|
49
|
+
apiUrl: concept.apiUrl(),
|
|
50
|
+
prefLabel: concept.prefLabel(),
|
|
51
|
+
types: concept.types(),
|
|
52
|
+
directType: concept.directType(),
|
|
53
|
+
// there's no labels equivalent in Concept but we don't use this field
|
|
54
|
+
// anyway
|
|
55
|
+
labels: [],
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return new Person(personShim, context)
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
constructor(private person: CapiPerson, private context: QueryContext) {
|
|
43
62
|
this.#systemCode = context.systemCode ?? 'cp-content-pipeline'
|
|
44
63
|
}
|
package/src/model/Topper.ts
CHANGED
|
@@ -11,6 +11,8 @@ import imageServiceUrl from '../helpers/imageService'
|
|
|
11
11
|
import { LeadFlourish } from './LeadFlourish'
|
|
12
12
|
import type { TopperWithHeadshotHeadshotArgs } from '../generated'
|
|
13
13
|
import { predicates } from './Concept'
|
|
14
|
+
import { OperationalError } from '@dotcom-reliability-kit/errors'
|
|
15
|
+
import isError from '../helpers/isError'
|
|
14
16
|
|
|
15
17
|
type TopperType =
|
|
16
18
|
| 'DeepPortraitTopper'
|
|
@@ -346,7 +348,22 @@ export class Topper {
|
|
|
346
348
|
}
|
|
347
349
|
|
|
348
350
|
if (this.capiResponse.isOpinion()) {
|
|
349
|
-
|
|
351
|
+
try {
|
|
352
|
+
const primaryAuthor = await this.capiResponse.primaryAuthor()
|
|
353
|
+
return primaryAuthor?.headshot(args) ?? null
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (isError(error)) {
|
|
356
|
+
this.context.logger.warn({
|
|
357
|
+
event: 'RECOVERABLE_ERROR',
|
|
358
|
+
error: new OperationalError({
|
|
359
|
+
message: 'Error fetching CAPI Person for headshot URL for topper',
|
|
360
|
+
code: 'HEADSHOT_PERSON_FETCH_ERROR',
|
|
361
|
+
cause: error,
|
|
362
|
+
}),
|
|
363
|
+
})
|
|
364
|
+
}
|
|
365
|
+
return null
|
|
366
|
+
}
|
|
350
367
|
}
|
|
351
368
|
|
|
352
369
|
return null
|
|
@@ -195,7 +195,7 @@ export const baseMetadataSchema = z.object({
|
|
|
195
195
|
publishedDate: z.string(),
|
|
196
196
|
firstPublishedDate: z.string(),
|
|
197
197
|
publishReference: z.string().optional(),
|
|
198
|
-
lastModified: z.string(),
|
|
198
|
+
lastModified: z.string().optional(),
|
|
199
199
|
realtime: z.boolean(),
|
|
200
200
|
editorialDesk: z.string().optional(),
|
|
201
201
|
accessLevel: z
|