@financial-times/cp-content-pipeline-schema 1.8.5 → 2.0.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.
@@ -0,0 +1,615 @@
1
+ fragment Concept on Concept {
2
+ id
3
+ prefLabel
4
+ types
5
+ type
6
+ directType
7
+ isPackageBrand
8
+ predicate
9
+ url(vanity: $useVanities)
10
+ relativeUrl: url(relative: true, vanity: $useVanities)
11
+ }
12
+
13
+ fragment StructuredTree on StructuredContent {
14
+ tree
15
+ }
16
+
17
+ fragment MetaLink on MetaLink {
18
+ ... on Concept {
19
+ id
20
+ url
21
+ relativeUrl: url(relative: true)
22
+ prefLabel
23
+ }
24
+ ... on ContentPackage {
25
+ id
26
+ url
27
+ relativeUrl: url(relative: true)
28
+ prefLabel: title
29
+ }
30
+ }
31
+
32
+ fragment Teaser on Teaser {
33
+ title
34
+ id
35
+ url
36
+ relativeUrl: url(relative: true)
37
+ type
38
+ publishedDate
39
+ firstPublishedDate
40
+ metaPrefixText
41
+ metaSuffixText
42
+ metaLink {
43
+ ...MetaLink
44
+ }
45
+ metaAltLink {
46
+ ...MetaLink
47
+ }
48
+ image {
49
+ url
50
+ width
51
+ height
52
+ altText
53
+ }
54
+ indicators {
55
+ accessLevel
56
+ isOpinion
57
+ isColumn
58
+ isPodcast
59
+ isEditorsChoice
60
+ isExclusive
61
+ isScoop
62
+ }
63
+ }
64
+
65
+ fragment ImageSource on ImageSource {
66
+ dpr
67
+ width
68
+ url
69
+ }
70
+
71
+ fragment Intro on RichText {
72
+ source
73
+ structured {
74
+ ...StructuredTree
75
+ }
76
+ }
77
+
78
+ fragment Topper on Topper {
79
+ __typename
80
+ headline
81
+ backgroundColour
82
+ backgroundBox
83
+ textShadow
84
+ followButtonVariant
85
+
86
+ intro {
87
+ ...Intro
88
+ }
89
+
90
+ displayConcept {
91
+ ...Concept
92
+ }
93
+
94
+ genreConcept {
95
+ ...Concept
96
+ }
97
+
98
+ ... on TopperWithBrand {
99
+ brandConcept {
100
+ ...Concept
101
+ }
102
+ }
103
+
104
+ ... on TopperWithImages {
105
+ fallbackImage {
106
+ sourceSet(width: 1440) {
107
+ ...ImageSource
108
+ }
109
+ ...Image
110
+ }
111
+
112
+ images {
113
+ ...Image
114
+ }
115
+ }
116
+
117
+ ... on SplitTextTopper {
118
+ images {
119
+ ... on ImageSquare {
120
+ sourceSet(width: 490) {
121
+ ...ImageSource
122
+ }
123
+ }
124
+ ... on ImageStandard {
125
+ sourceSet(width: 800) {
126
+ ...ImageSource
127
+ }
128
+ }
129
+ ... on ImageWide {
130
+ normal: sourceSet(width: 1220) {
131
+ ...ImageSource
132
+ }
133
+ widest: sourceSet(width: 1440) {
134
+ ...ImageSource
135
+ }
136
+ }
137
+ }
138
+ }
139
+ ... on FullBleedTopper {
140
+ images {
141
+ ... on ImageSquare {
142
+ sourceSet(width: 490) {
143
+ ...ImageSource
144
+ }
145
+ }
146
+ ... on ImageStandard {
147
+ sourceSet(width: 800) {
148
+ ...ImageSource
149
+ }
150
+ }
151
+ ... on ImageWide {
152
+ normal: sourceSet(width: 1220) {
153
+ ...ImageSource
154
+ }
155
+ wide: sourceSet(width: 1440) {
156
+ ...ImageSource
157
+ }
158
+ wideL: sourceSet(width: 1920) {
159
+ ...ImageSource
160
+ }
161
+ wideXL: sourceSet(width: 3840) {
162
+ ...ImageSource
163
+ }
164
+ }
165
+ }
166
+ }
167
+ ... on OpinionTopper {
168
+ headshot(dpr: 2, width: 150)
169
+ columnist {
170
+ ...Concept
171
+ }
172
+ }
173
+ ... on PodcastTopper {
174
+ headshot(dpr: 2, width: 150)
175
+ }
176
+ ... on TopperWithTheme {
177
+ isLargeHeadline
178
+ layout
179
+ }
180
+ ... on TopperWithPackage {
181
+ design
182
+ }
183
+ ... on DeepPortraitTopper {
184
+ images {
185
+ ... on ImagePortrait {
186
+ small: sourceSet(width: 490) {
187
+ ...ImageSource
188
+ }
189
+ normal: sourceSet(width: 740) {
190
+ ...ImageSource
191
+ }
192
+ wide: sourceSet(width: 1067) {
193
+ ...ImageSource
194
+ }
195
+ }
196
+ }
197
+ }
198
+ ... on DeepLandscapeTopper {
199
+ images {
200
+ ... on ImageLandscape {
201
+ normal: sourceSet(width: 1440) {
202
+ ...ImageSource
203
+ }
204
+ wide: sourceSet(width: 1920) {
205
+ ...ImageSource
206
+ }
207
+ }
208
+ ... on ImagePortrait {
209
+ small: sourceSet(width: 490) {
210
+ ...ImageSource
211
+ }
212
+ tablets: sourceSet(width: 980) {
213
+ ...ImageSource
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ fragment Image on Image {
221
+ __typename
222
+ height
223
+ width
224
+ caption
225
+ credit
226
+ format
227
+ url
228
+ id
229
+ }
230
+
231
+ fragment PictureFields on Picture {
232
+ __typename
233
+ alt
234
+ caption
235
+ credit
236
+ imageType
237
+ layoutWidth
238
+
239
+ fallbackImage {
240
+ ...Image
241
+ }
242
+ }
243
+
244
+ fragment Picture on Picture {
245
+ ...PictureFields
246
+
247
+ images {
248
+ ...Image
249
+ }
250
+
251
+ ... on PictureStandard {
252
+ images {
253
+ ... on ImageStandard {
254
+ sourceSet(width: 700) {
255
+ ...ImageSource
256
+ }
257
+ }
258
+ ... on ImageStandardInline {
259
+ sourceSet(width: 700) {
260
+ ...ImageSource
261
+ }
262
+ }
263
+ ... on ImageMobile {
264
+ sourceSet(width: 490) {
265
+ ...ImageSource
266
+ }
267
+ }
268
+ }
269
+ }
270
+ ... on PictureInline {
271
+ images {
272
+ ... on ImageStandardInline {
273
+ sourceSet(width: 350) {
274
+ ...ImageSource
275
+ }
276
+ }
277
+ }
278
+ }
279
+ ... on PictureFullBleed {
280
+ images {
281
+ ... on ImageMobile {
282
+ sourceSet(width: 490) {
283
+ ...ImageSource
284
+ }
285
+ }
286
+ ... on ImageDesktop {
287
+ sourceSet(width: 1200) {
288
+ ...ImageSource
289
+ }
290
+ }
291
+ }
292
+ }
293
+ }
294
+
295
+ fragment MainImage on MainImage {
296
+ picture {
297
+ ...Picture
298
+ }
299
+ }
300
+
301
+ fragment ImageSet on ImageSet {
302
+ picture {
303
+ ...Picture
304
+ }
305
+ }
306
+
307
+ fragment Clip on Clip {
308
+ __typename
309
+ format
310
+ dataSource {
311
+ audioCodec
312
+ binaryUrl
313
+ duration
314
+ mediaType
315
+ pixelHeight
316
+ pixelWidth
317
+ videoCodec
318
+ }
319
+ poster
320
+ }
321
+
322
+ fragment Transcript on RichText {
323
+ source
324
+ structured {
325
+ ...StructuredTree
326
+ }
327
+ }
328
+
329
+ fragment ClipSet on ClipSet {
330
+ type
331
+ autoplay
332
+ loop
333
+ muted
334
+ dataLayout
335
+ id
336
+ noAudio
337
+ caption
338
+ credits
339
+ description
340
+ displayTitle
341
+ contentWarning
342
+ source
343
+ subtitle
344
+ publishedDate
345
+ clips {
346
+ ...Clip
347
+ }
348
+ accessibility {
349
+ captions {
350
+ url
351
+ mediaType
352
+ }
353
+ transcript {
354
+ ...Transcript
355
+ }
356
+ }
357
+ }
358
+
359
+ fragment LayoutImage on LayoutImage {
360
+ picture {
361
+ ...Picture
362
+ }
363
+ }
364
+
365
+ fragment Recommended on Recommended {
366
+ teaser {
367
+ ...Teaser
368
+ }
369
+ }
370
+
371
+ fragment Tweet on Tweet {
372
+ html
373
+ }
374
+
375
+ fragment Video on VideoReference {
376
+ title
377
+ id
378
+ }
379
+
380
+ fragment Flourish on Flourish {
381
+ fallbackImage {
382
+ url
383
+ height
384
+ width
385
+ }
386
+ }
387
+
388
+ fragment RawImage on RawImage {
389
+ image {
390
+ ...Image
391
+ }
392
+ }
393
+
394
+ fragment ScrollyImage on ScrollyImage {
395
+ picture {
396
+ ...PictureFields
397
+
398
+ images {
399
+ ...Image
400
+
401
+ ... on ImageDesktop {
402
+ s: sourceSet(width: 740) {
403
+ ...ImageSource
404
+ }
405
+ m: sourceSet(width: 980) {
406
+ ...ImageSource
407
+ }
408
+ l: sourceSet(width: 1220) {
409
+ ...ImageSource
410
+ }
411
+ xl: sourceSet(width: 1920) {
412
+ ...ImageSource
413
+ }
414
+ xxl: sourceSet(width: 3840) {
415
+ ...ImageSource
416
+ }
417
+ }
418
+
419
+ ... on ImageMobile {
420
+ s: sourceSet(width: 490) {
421
+ ...ImageSource
422
+ }
423
+ m: sourceSet(width: 600) {
424
+ ...ImageSource
425
+ }
426
+ l: sourceSet(width: 800) {
427
+ ...ImageSource
428
+ }
429
+ xl: sourceSet(width: 1200) {
430
+ ...ImageSource
431
+ }
432
+ }
433
+ }
434
+ }
435
+ }
436
+
437
+ fragment Design on Design {
438
+ theme
439
+ layout
440
+ }
441
+
442
+ fragment Media on Media {
443
+ url
444
+ mediaType
445
+ }
446
+
447
+ fragment PinnedPost on Content {
448
+ __typename
449
+ id
450
+ title
451
+ publishedDate
452
+ body {
453
+ structured {
454
+ ...StructuredContent
455
+ }
456
+ }
457
+ publishedDate
458
+ mainImage {
459
+ ...Image
460
+ }
461
+ altTitle {
462
+ promotionalTitle
463
+ }
464
+ byline(vanity: $useVanities) {
465
+ tree
466
+ }
467
+ accessLevel
468
+ canBeSyndicated
469
+ commentsEnabled
470
+ }
471
+
472
+ fragment ArticleReferences on Reference {
473
+ type
474
+ ...Recommended
475
+ ...ImageSet
476
+ ...ClipSet
477
+ ...LayoutImage
478
+ ...Tweet
479
+ ...Video
480
+ ...Flourish
481
+ ...RawImage
482
+ ...MainImage
483
+ ...ScrollyImage
484
+ }
485
+
486
+ fragment StructuredContent on StructuredContent {
487
+ ...StructuredTree
488
+
489
+ references {
490
+ ...ArticleReferences
491
+ }
492
+ text
493
+ }
494
+
495
+ fragment Content on Content {
496
+ __typename
497
+ id
498
+ title
499
+ publishedDate
500
+ firstPublishedDate
501
+ standfirst
502
+ topper {
503
+ ...Topper
504
+ }
505
+ body {
506
+ structured {
507
+ ...StructuredContent
508
+ }
509
+ }
510
+ publishedDate
511
+ mainImage {
512
+ ...Image
513
+ }
514
+ altTitle {
515
+ promotionalTitle
516
+ }
517
+ byline(vanity: $useVanities) {
518
+ tree
519
+ }
520
+ annotations {
521
+ ...Concept
522
+ }
523
+ accessLevel
524
+ canBeSyndicated
525
+ instantAlertConcept {
526
+ id
527
+ prefLabel
528
+ }
529
+ originatingParty
530
+ commentsEnabled
531
+ editorialDesk
532
+ design {
533
+ ...Design
534
+ }
535
+ }
536
+
537
+ fragment PackageContainer on Content {
538
+ ...Content
539
+ url(vanity: $useVanities)
540
+ topper {
541
+ ... on TopperWithBrand {
542
+ genreConcept {
543
+ ...Concept
544
+ }
545
+ }
546
+ }
547
+ ... on ContentPackage {
548
+ containsLength
549
+ contains(fromId: $uuid, surroundingArticles: 7) {
550
+ ...Teaser
551
+ standfirst
552
+ }
553
+ tableOfContents {
554
+ labelType
555
+ sequence
556
+ }
557
+ }
558
+ }
559
+
560
+ fragment ArticleFields on Content {
561
+ ...Content
562
+ url
563
+ ... on Article {
564
+ containedIn {
565
+ ...PackageContainer
566
+ }
567
+ }
568
+ ... on Placeholder {
569
+ containedIn {
570
+ ...PackageContainer
571
+ }
572
+ }
573
+ ... on LiveBlogPost {
574
+ containedIn {
575
+ ...PackageContainer
576
+ }
577
+ }
578
+ ... on LiveBlogPackage {
579
+ liveBlogPosts(includePinned: false) {
580
+ ...Content
581
+ url
582
+ }
583
+ pinnedPost {
584
+ ...PinnedPost
585
+ }
586
+ realtime
587
+ }
588
+ ... on ContentPackage {
589
+ contains {
590
+ ...Teaser
591
+ standfirst
592
+ }
593
+ }
594
+ ... on Audio {
595
+ media {
596
+ ...Media
597
+ }
598
+ }
599
+ }
600
+
601
+ query Article($uuid: String!, $useVanities: Boolean!) {
602
+ content(uuid: $uuid) {
603
+ ...ArticleFields
604
+ }
605
+ }
606
+
607
+ query ArticleFromJSON(
608
+ $content: JSON!
609
+ $useVanities: Boolean!
610
+ $uuid: String = null
611
+ ) {
612
+ contentFromJSON(content: $content) {
613
+ ...ArticleFields
614
+ }
615
+ }
@@ -35,13 +35,13 @@ export type Scalars = {
35
35
  ImageType: { input: 'image' | 'graphic'; output: 'image' | 'graphic'; }
36
36
  JSON: { input: any; output: any; }
37
37
  PackageDesign: { input: 'special-report' | 'extra' | 'basic' | 'extra-wide'; output: 'special-report' | 'extra' | 'basic' | 'extra-wide'; }
38
- RichTextSource: { input: 'standfirst' | 'summary' | 'bodyXML'; output: 'standfirst' | 'summary' | 'bodyXML'; }
38
+ RichTextSource: { input: 'standfirst' | 'summary' | 'bodyXML' | 'transcript'; output: 'standfirst' | 'summary' | 'bodyXML' | 'transcript'; }
39
39
  TopperBackgroundColour: { input: 'paper' | 'wheat' | 'white' | 'black' | 'claret' | 'oxford' | 'slate' | 'crimson' | 'sky' | 'matisse'; output: 'paper' | 'wheat' | 'white' | 'black' | 'claret' | 'oxford' | 'slate' | 'crimson' | 'sky' | 'matisse'; }
40
40
  };
41
41
 
42
42
  export type Accessibility = {
43
43
  readonly captions?: Maybe<ReadonlyArray<Maybe<Caption>>>;
44
- readonly transcript?: Maybe<Scalars['String']['output']>;
44
+ readonly transcript?: Maybe<RichText>;
45
45
  };
46
46
 
47
47
  export type AltStandfirst = {
@@ -1080,7 +1080,7 @@ export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = R
1080
1080
  /** Mapping between all available schema types and the resolvers types */
1081
1081
  export type ResolversTypes = ResolversObject<{
1082
1082
  AccessLevel: ResolverTypeWrapper<Scalars['AccessLevel']['output']>;
1083
- Accessibility: ResolverTypeWrapper<Accessibility>;
1083
+ Accessibility: ResolverTypeWrapper<Omit<Accessibility, 'transcript'> & { transcript?: Maybe<ResolversTypes['RichText']> }>;
1084
1084
  AltStandfirst: ResolverTypeWrapper<AltStandfirst>;
1085
1085
  AltTitle: ResolverTypeWrapper<AltTitle>;
1086
1086
  Article: ResolverTypeWrapper<CapiResponse>;
@@ -1165,7 +1165,7 @@ export type ResolversTypes = ResolversObject<{
1165
1165
  /** Mapping between all available schema types and the resolvers parents */
1166
1166
  export type ResolversParentTypes = ResolversObject<{
1167
1167
  AccessLevel: Scalars['AccessLevel']['output'];
1168
- Accessibility: Accessibility;
1168
+ Accessibility: Omit<Accessibility, 'transcript'> & { transcript?: Maybe<ResolversParentTypes['RichText']> };
1169
1169
  AltStandfirst: AltStandfirst;
1170
1170
  AltTitle: AltTitle;
1171
1171
  Article: CapiResponse;
@@ -1253,7 +1253,7 @@ export interface AccessLevelScalarConfig extends GraphQLScalarTypeConfig<Resolve
1253
1253
 
1254
1254
  export type AccessibilityResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Accessibility'] = ResolversParentTypes['Accessibility']> = ResolversObject<{
1255
1255
  captions?: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['Caption']>>>, ParentType, ContextType>;
1256
- transcript?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
1256
+ transcript?: Resolver<Maybe<ResolversTypes['RichText']>, ParentType, ContextType>;
1257
1257
  __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
1258
1258
  }>;
1259
1259
 
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import type { Logger } from '@dotcom-reliability-kit/logger'
4
4
  import { loadSchemaSync } from '@graphql-tools/load'
5
5
  import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'
6
6
  import * as path from 'path'
7
+ import { readFileSync } from 'fs'
7
8
 
8
9
  interface Metrics {
9
10
  count(metric: string | string[], count?: number): void
@@ -23,6 +24,11 @@ export interface QueryContext {
23
24
  }
24
25
  }
25
26
 
27
+ export const articleDocumentQuery = readFileSync(
28
+ require.resolve('../queries/article.graphql'),
29
+ 'utf-8'
30
+ )
31
+
26
32
  export const typeDefs = loadSchemaSync(
27
33
  path.join(__dirname, '../typedefs/**/*.graphql'),
28
34
  {
@@ -227,15 +227,6 @@ export class Topper {
227
227
 
228
228
  const brandConcept = this.brandConcept()
229
229
  const displayConcept = this.capiResponse.getDisplayConcept()
230
-
231
- if (this.capiResponse.isOpinion()) {
232
- return brandConcept ?? displayConcept ?? null
233
- }
234
-
235
- if (displayConcept) {
236
- return displayConcept
237
- }
238
-
239
230
  const aboutAnnotation = this.capiResponse
240
231
  .annotations()
241
232
  ?.find(
@@ -244,6 +235,14 @@ export class Topper {
244
235
  'http://www.ft.com/ontology/annotation/about'
245
236
  )
246
237
 
238
+ if (this.capiResponse.isOpinion() && brandConcept) {
239
+ return brandConcept
240
+ }
241
+
242
+ if (displayConcept) {
243
+ return displayConcept
244
+ }
245
+
247
246
  return aboutAnnotation ?? null
248
247
  }
249
248
 
@@ -5,6 +5,7 @@ import type {
5
5
  } from '../Workarounds'
6
6
  import { uuidFromUrl } from '../../../helpers/metadata'
7
7
  import { Clip } from '../../../model/Clip'
8
+ import { RichText } from '../../../model/RichText'
8
9
  import { ClipSetResolvers } from '../../../generated'
9
10
  import { ReferenceWithCAPIData } from '.'
10
11
 
@@ -45,7 +46,18 @@ export const ClipSet = {
45
46
  caption: (parent) =>
46
47
  (parent.reference as OldClipContentTree).caption ??
47
48
  getClipSet(parent)?.caption,
48
- accessibility: (parent) => getClipSet(parent)?.accessibility || null,
49
+ accessibility: (parent) => {
50
+ const accessibility = getClipSet(parent)?.accessibility
51
+ if (accessibility) {
52
+ return {
53
+ ...accessibility,
54
+ transcript: accessibility.transcript
55
+ ? new RichText('transcript', accessibility.transcript)
56
+ : null,
57
+ }
58
+ }
59
+ return null
60
+ },
49
61
  description: (parent) =>
50
62
  (parent.reference as OldClipContentTree).description ??
51
63
  getClipSet(parent)?.description,