@financial-times/cp-content-pipeline-schema 1.4.4 → 1.5.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 +17 -0
- package/lib/fixtures/capiObject.js +1 -1
- package/lib/fixtures/capiObject.js.map +1 -1
- package/lib/fixtures/clipSet.d.ts +2 -0
- package/lib/fixtures/clipSet.js +70 -0
- package/lib/fixtures/clipSet.js.map +1 -0
- package/lib/generated/index.d.ts +120 -3
- package/lib/model/CapiResponse.d.ts +2 -2
- package/lib/model/CapiResponse.js +1 -1
- package/lib/model/CapiResponse.js.map +1 -1
- package/lib/model/Clip.d.ts +28 -0
- package/lib/model/Clip.js +51 -0
- package/lib/model/Clip.js.map +1 -0
- package/lib/model/schemas/capi/article.d.ts +602 -9
- package/lib/model/schemas/capi/audio.d.ts +31 -31
- package/lib/model/schemas/capi/base-schema.d.ts +1355 -14
- package/lib/model/schemas/capi/base-schema.js +41 -2
- 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/live-blog-package.d.ts +602 -9
- package/lib/model/schemas/capi/placeholder.d.ts +602 -9
- package/lib/resolvers/clip.d.ts +10 -0
- package/lib/resolvers/clip.js +13 -0
- package/lib/resolvers/clip.js.map +1 -0
- package/lib/resolvers/content-tree/Workarounds.d.ts +10 -2
- package/lib/resolvers/content-tree/nodePredicates.d.ts +3 -3
- package/lib/resolvers/content-tree/references/ClipSet.d.ts +27 -0
- package/lib/resolvers/content-tree/references/ClipSet.js +37 -0
- package/lib/resolvers/content-tree/references/ClipSet.js.map +1 -0
- package/lib/resolvers/content-tree/references/ImageSet.js +3 -2
- package/lib/resolvers/content-tree/references/ImageSet.js.map +1 -1
- package/lib/resolvers/content-tree/references/Reference.d.ts +1 -1
- package/lib/resolvers/content-tree/references/ScrollyImage.js +3 -2
- package/lib/resolvers/content-tree/references/ScrollyImage.js.map +1 -1
- package/lib/resolvers/content-tree/references/index.d.ts +4 -1
- package/lib/resolvers/content-tree/references/index.js +4 -0
- package/lib/resolvers/content-tree/references/index.js.map +1 -1
- package/lib/resolvers/content-tree/tagMappings.js +9 -0
- package/lib/resolvers/content-tree/tagMappings.js.map +1 -1
- package/lib/resolvers/index.d.ts +9 -0
- package/lib/resolvers/index.js +2 -0
- package/lib/resolvers/index.js.map +1 -1
- package/lib/resolvers/scalars.d.ts +2 -0
- package/lib/resolvers/scalars.js +6 -1
- package/lib/resolvers/scalars.js.map +1 -1
- package/package.json +1 -1
- package/src/fixtures/capiObject.ts +1 -1
- package/src/fixtures/clipSet.ts +72 -0
- package/src/generated/index.ts +128 -3
- package/src/model/CapiResponse.ts +3 -2
- package/src/model/Clip.ts +75 -0
- package/src/model/__snapshots__/RichText.test.ts.snap +25 -0
- package/src/model/schemas/capi/base-schema.ts +45 -1
- package/src/resolvers/clip.ts +13 -0
- package/src/resolvers/content-tree/Workarounds.ts +13 -2
- package/src/resolvers/content-tree/references/ClipSet.ts +49 -0
- package/src/resolvers/content-tree/references/ImageSet.ts +9 -5
- package/src/resolvers/content-tree/references/ScrollyImage.ts +9 -5
- package/src/resolvers/content-tree/references/index.ts +6 -0
- package/src/resolvers/content-tree/tagMappings.ts +10 -0
- package/src/resolvers/index.ts +2 -0
- package/src/resolvers/scalars.ts +8 -0
- package/src/types/internal-content.d.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/clip.graphql +28 -0
- package/typedefs/references/clipSet.graphql +19 -0
- package/typedefs/scalars.graphql +1 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ClipSet as ClipSetType } from '../types/internal-content'
|
|
2
|
+
|
|
3
|
+
export const ClipSet: ClipSetType = {
|
|
4
|
+
accessibility: {
|
|
5
|
+
captions: [
|
|
6
|
+
{
|
|
7
|
+
mediaType: 'text/vtt',
|
|
8
|
+
url: 'https://next-media-api.ft.com/captions/16862228218010.vtt',
|
|
9
|
+
},
|
|
10
|
+
],
|
|
11
|
+
transcript:
|
|
12
|
+
'<p>Hydrogen is this magic molecule which is the lightest, the most energetic and the most abundant in the universe. ... </p>',
|
|
13
|
+
},
|
|
14
|
+
description:
|
|
15
|
+
"The global push for net zero carbon emissions is one of humanity's greatest challenges. ...",
|
|
16
|
+
displayTitle: 'Ukranians fight back',
|
|
17
|
+
contentWarning: ['graphic violence', 'animal cruelty'],
|
|
18
|
+
id: 'http://www.ft.com/thing/f17fe25b-cdea-4d5f-a6af-40e56e33e888',
|
|
19
|
+
members: [
|
|
20
|
+
{
|
|
21
|
+
format: 'standardInline',
|
|
22
|
+
id: 'https://api.ft.com/content/4d000e07-d679-4e3b-bd6d-afa3461243a8',
|
|
23
|
+
type: 'http://www.ft.com/ontology/content/Clip',
|
|
24
|
+
dataSource: [
|
|
25
|
+
{
|
|
26
|
+
audioCodec: 'mp3',
|
|
27
|
+
binaryUrl:
|
|
28
|
+
'https://next-media-api.ft.com/renditions/16868569859480/0x0.mp3',
|
|
29
|
+
duration: 1057,
|
|
30
|
+
mediaType: 'audio/mpeg',
|
|
31
|
+
pixelHeight: 720,
|
|
32
|
+
pixelWidth: 1280,
|
|
33
|
+
videoCodec: 'h264',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
poster: {
|
|
37
|
+
id: 'https://api.ft.com/content/2f730c3c-1fb2-4131-9697-c534fe1ee488',
|
|
38
|
+
type: 'http://www.ft.com/ontology/content/ImageSet',
|
|
39
|
+
description: 'Picture for video',
|
|
40
|
+
members: [],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
format: 'mobile',
|
|
45
|
+
id: 'https://api.ft.com/content/b529ba2b-d424-4f85-a93f-8413636dac82',
|
|
46
|
+
type: 'http://www.ft.com/ontology/content/Clip',
|
|
47
|
+
dataSource: [
|
|
48
|
+
{
|
|
49
|
+
audioCodec: 'mp3',
|
|
50
|
+
binaryUrl:
|
|
51
|
+
'https://next-media-api.ft.com/renditions/16868569859480/0x0.mp3',
|
|
52
|
+
duration: 1057,
|
|
53
|
+
mediaType: 'audio/mpeg',
|
|
54
|
+
pixelHeight: 720,
|
|
55
|
+
pixelWidth: 1280,
|
|
56
|
+
videoCodec: 'h264',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
poster: {
|
|
60
|
+
id: 'https://api.ft.com/content/2f730c3c-1fb2-4131-9697-c534fe1ee488',
|
|
61
|
+
type: 'http://www.ft.com/ontology/content/ImageSet',
|
|
62
|
+
description: 'Picture for video',
|
|
63
|
+
members: [],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
noAudio: false,
|
|
68
|
+
publishedDate: '2023-06-14T03:59:47.543Z',
|
|
69
|
+
source: 'YouGov',
|
|
70
|
+
subtitle: 'Drone hits Moscow tower block',
|
|
71
|
+
type: 'http://www.ft.com/ontology/content/ClipSet',
|
|
72
|
+
}
|
package/src/generated/index.ts
CHANGED
|
@@ -2,12 +2,13 @@ import type { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } f
|
|
|
2
2
|
import type { Concept as ConceptModel } from '../model/Concept';
|
|
3
3
|
import type { CapiResponse } from '../model/CapiResponse';
|
|
4
4
|
import type { Image as ImageModel } from '../model/Image';
|
|
5
|
+
import type { Clip as ClipModel } from '../model/Clip';
|
|
5
6
|
import type { Picture as PictureModel } from '../model/Picture';
|
|
6
7
|
import type { RichText as RichTextModel } from '../model/RichText';
|
|
7
8
|
import type { Topper as TopperModel } from '../model/Topper';
|
|
8
9
|
import type { ContentTree } from '@financial-times/content-tree';
|
|
9
10
|
import type { ReferenceWithCAPIData } from '../resolvers/content-tree/references';
|
|
10
|
-
import type { Video as VideoNode, RawImage as RawImageNode } from '../resolvers/content-tree/Workarounds';
|
|
11
|
+
import type { Video as VideoNode, ClipSet as ClipSetNode, OldClip as OldClipNode, RawImage as RawImageNode } from '../resolvers/content-tree/Workarounds';
|
|
11
12
|
import type { QueryContext } from '../';
|
|
12
13
|
export type Maybe<T> = T | null;
|
|
13
14
|
export type InputMaybe<T> = Maybe<T>;
|
|
@@ -27,6 +28,7 @@ export type Scalars = {
|
|
|
27
28
|
Float: { input: number; output: number; }
|
|
28
29
|
AccessLevel: { input: 'premium' | 'subscribed' | 'registered' | 'free'; output: 'premium' | 'subscribed' | 'registered' | 'free'; }
|
|
29
30
|
CanBeSyndicated: { input: 'yes' | 'no' | 'verify' | 'withContributorPayment' | 'unknown'; output: 'yes' | 'no' | 'verify' | 'withContributorPayment' | 'unknown'; }
|
|
31
|
+
ClipFormat: { input: 'standard-inline' | 'mobile'; output: 'standard-inline' | 'mobile'; }
|
|
30
32
|
ContentType: { input: 'Article' | 'Placeholder' | 'Video' | 'Audio' | 'LiveBlogPackage' | 'LiveBlogPost' | 'ContentPackage' | 'Content' | 'MediaResource'; output: 'Article' | 'Placeholder' | 'Video' | 'Audio' | 'LiveBlogPackage' | 'LiveBlogPost' | 'ContentPackage' | 'Content' | 'MediaResource'; }
|
|
31
33
|
FollowButtonVariant: { input: 'standard' | 'inverse' | 'opinion' | 'alphaville' | 'monochrome' | 'inverse-monochrome'; output: 'standard' | 'inverse' | 'opinion' | 'alphaville' | 'monochrome' | 'inverse-monochrome'; }
|
|
32
34
|
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'; }
|
|
@@ -37,6 +39,11 @@ export type Scalars = {
|
|
|
37
39
|
TopperBackgroundColour: { input: 'paper' | 'wheat' | 'white' | 'black' | 'claret' | 'oxford' | 'slate' | 'crimson' | 'sky' | 'matisse'; output: 'paper' | 'wheat' | 'white' | 'black' | 'claret' | 'oxford' | 'slate' | 'crimson' | 'sky' | 'matisse'; }
|
|
38
40
|
};
|
|
39
41
|
|
|
42
|
+
export type Accessibility = {
|
|
43
|
+
readonly captions?: Maybe<ReadonlyArray<Maybe<Caption>>>;
|
|
44
|
+
readonly transcript?: Maybe<Scalars['String']['output']>;
|
|
45
|
+
};
|
|
46
|
+
|
|
40
47
|
export type AltStandfirst = {
|
|
41
48
|
readonly promotionalStandfirst?: Maybe<Scalars['String']['output']>;
|
|
42
49
|
};
|
|
@@ -142,6 +149,49 @@ export type BrandedTopper = Topper & TopperWithBrand & TopperWithTheme & {
|
|
|
142
149
|
readonly textShadow?: Maybe<Scalars['Boolean']['output']>;
|
|
143
150
|
};
|
|
144
151
|
|
|
152
|
+
export type Caption = {
|
|
153
|
+
readonly mediaType?: Maybe<Scalars['String']['output']>;
|
|
154
|
+
readonly url?: Maybe<Scalars['String']['output']>;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export type Clip = {
|
|
158
|
+
readonly dataSource: ReadonlyArray<ClipSource>;
|
|
159
|
+
readonly format?: Maybe<Scalars['ClipFormat']['output']>;
|
|
160
|
+
readonly id: Scalars['String']['output'];
|
|
161
|
+
readonly poster?: Maybe<Scalars['String']['output']>;
|
|
162
|
+
readonly type?: Maybe<Scalars['String']['output']>;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export type ClipSet = Reference & {
|
|
166
|
+
readonly accessibility?: Maybe<Accessibility>;
|
|
167
|
+
readonly autoplay?: Maybe<Scalars['Boolean']['output']>;
|
|
168
|
+
readonly caption?: Maybe<Scalars['String']['output']>;
|
|
169
|
+
readonly clips?: Maybe<ReadonlyArray<Maybe<Clip>>>;
|
|
170
|
+
readonly contentWarning?: Maybe<ReadonlyArray<Maybe<Scalars['String']['output']>>>;
|
|
171
|
+
readonly credits?: Maybe<Scalars['String']['output']>;
|
|
172
|
+
readonly dataLayout?: Maybe<Scalars['String']['output']>;
|
|
173
|
+
readonly description?: Maybe<Scalars['String']['output']>;
|
|
174
|
+
readonly displayTitle?: Maybe<Scalars['String']['output']>;
|
|
175
|
+
readonly id: Scalars['String']['output'];
|
|
176
|
+
readonly loop?: Maybe<Scalars['Boolean']['output']>;
|
|
177
|
+
readonly muted?: Maybe<Scalars['Boolean']['output']>;
|
|
178
|
+
readonly noAudio?: Maybe<Scalars['Boolean']['output']>;
|
|
179
|
+
readonly publishedDate?: Maybe<Scalars['String']['output']>;
|
|
180
|
+
readonly source?: Maybe<Scalars['String']['output']>;
|
|
181
|
+
readonly subtitle?: Maybe<Scalars['String']['output']>;
|
|
182
|
+
readonly type: Scalars['String']['output'];
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
export type ClipSource = {
|
|
186
|
+
readonly audioCodec?: Maybe<Scalars['String']['output']>;
|
|
187
|
+
readonly binaryUrl: Scalars['String']['output'];
|
|
188
|
+
readonly duration?: Maybe<Scalars['Int']['output']>;
|
|
189
|
+
readonly mediaType: Scalars['String']['output'];
|
|
190
|
+
readonly pixelHeight?: Maybe<Scalars['Int']['output']>;
|
|
191
|
+
readonly pixelWidth?: Maybe<Scalars['Int']['output']>;
|
|
192
|
+
readonly videoCodec?: Maybe<Scalars['String']['output']>;
|
|
193
|
+
};
|
|
194
|
+
|
|
145
195
|
export type Concept = {
|
|
146
196
|
readonly apiUrl?: Maybe<Scalars['String']['output']>;
|
|
147
197
|
readonly directType?: Maybe<Scalars['String']['output']>;
|
|
@@ -990,7 +1040,7 @@ export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = R
|
|
|
990
1040
|
Content: ( CapiResponse ) | ( CapiResponse ) | ( CapiResponse ) | ( CapiResponse ) | ( CapiResponse ) | ( CapiResponse ) | ( CapiResponse );
|
|
991
1041
|
Image: ( ImageDesktop ) | ( ImageLandscape ) | ( ImageMobile ) | ( ImagePortrait ) | ( ImageSquare ) | ( ImageSquareFtEdit ) | ( ImageStandard ) | ( ImageStandardInline ) | ( ImageWide );
|
|
992
1042
|
Picture: ( Omit<PictureFullBleed, 'fallbackImage' | 'images'> & { fallbackImage: RefType['Image'], images: ReadonlyArray<RefType['Image']> } ) | ( Omit<PictureInline, 'fallbackImage' | 'images'> & { fallbackImage: RefType['Image'], images: ReadonlyArray<RefType['Image']> } ) | ( Omit<PictureStandard, 'fallbackImage' | 'images'> & { fallbackImage: RefType['Image'], images: ReadonlyArray<RefType['Image']> } );
|
|
993
|
-
Reference: ( ReferenceWithCAPIData<ContentTree.Flourish> ) | ( ReferenceWithCAPIData<ContentTree.ImageSet> ) | ( ReferenceWithCAPIData<ContentTree.LayoutImage> ) | ( ReferenceWithCAPIData<ContentTree.ImageSet> ) | ( ReferenceWithCAPIData<RawImageNode> ) | ( ReferenceWithCAPIData<ContentTree.Recommended> ) | ( ReferenceWithCAPIData<ContentTree.ScrollyImage> ) | ( ReferenceWithCAPIData<ContentTree.Tweet> ) | ( ReferenceWithCAPIData<VideoNode> );
|
|
1043
|
+
Reference: ( ReferenceWithCAPIData<ClipSetNode|OldClipNode> ) | ( ReferenceWithCAPIData<ContentTree.Flourish> ) | ( ReferenceWithCAPIData<ContentTree.ImageSet> ) | ( ReferenceWithCAPIData<ContentTree.LayoutImage> ) | ( ReferenceWithCAPIData<ContentTree.ImageSet> ) | ( ReferenceWithCAPIData<RawImageNode> ) | ( ReferenceWithCAPIData<ContentTree.Recommended> ) | ( ReferenceWithCAPIData<ContentTree.ScrollyImage> ) | ( ReferenceWithCAPIData<ContentTree.Tweet> ) | ( ReferenceWithCAPIData<VideoNode> );
|
|
994
1044
|
Topper: ( Omit<BasicTopper, 'displayConcept' | 'genreConcept' | 'intro'> & { displayConcept?: Maybe<RefType['Concept']>, genreConcept?: Maybe<RefType['Concept']>, intro?: Maybe<RefType['RichText']> } ) | ( Omit<BrandedTopper, 'brandConcept' | 'displayConcept' | 'genreConcept' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, genreConcept?: Maybe<RefType['Concept']>, intro?: Maybe<RefType['RichText']> } ) | ( Omit<DeepLandscapeTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } ) | ( Omit<DeepPortraitTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } ) | ( Omit<FullBleedTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } ) | ( TopperModel ) | ( TopperModel ) | ( Omit<SplitTextTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } );
|
|
995
1045
|
TopperWithBrand: ( Omit<BrandedTopper, 'brandConcept' | 'displayConcept' | 'genreConcept' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, genreConcept?: Maybe<RefType['Concept']>, intro?: Maybe<RefType['RichText']> } ) | ( Omit<DeepLandscapeTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } ) | ( Omit<DeepPortraitTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } ) | ( Omit<FullBleedTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } ) | ( TopperModel ) | ( Omit<SplitTextTopper, 'brandConcept' | 'displayConcept' | 'fallbackImage' | 'genreConcept' | 'images' | 'intro'> & { brandConcept?: Maybe<RefType['Concept']>, displayConcept?: Maybe<RefType['Concept']>, fallbackImage?: Maybe<RefType['Image']>, genreConcept?: Maybe<RefType['Concept']>, images: ReadonlyArray<RefType['Image']>, intro?: Maybe<RefType['RichText']> } );
|
|
996
1046
|
TopperWithHeadshot: ( TopperModel ) | ( TopperModel );
|
|
@@ -1002,6 +1052,7 @@ export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = R
|
|
|
1002
1052
|
/** Mapping between all available schema types and the resolvers types */
|
|
1003
1053
|
export type ResolversTypes = ResolversObject<{
|
|
1004
1054
|
AccessLevel: ResolverTypeWrapper<Scalars['AccessLevel']['output']>;
|
|
1055
|
+
Accessibility: ResolverTypeWrapper<Accessibility>;
|
|
1005
1056
|
AltStandfirst: ResolverTypeWrapper<AltStandfirst>;
|
|
1006
1057
|
AltTitle: ResolverTypeWrapper<AltTitle>;
|
|
1007
1058
|
Article: ResolverTypeWrapper<CapiResponse>;
|
|
@@ -1010,6 +1061,11 @@ export type ResolversTypes = ResolversObject<{
|
|
|
1010
1061
|
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
|
|
1011
1062
|
BrandedTopper: ResolverTypeWrapper<Omit<BrandedTopper, 'brandConcept' | 'displayConcept' | 'genreConcept' | 'intro'> & { brandConcept?: Maybe<ResolversTypes['Concept']>, displayConcept?: Maybe<ResolversTypes['Concept']>, genreConcept?: Maybe<ResolversTypes['Concept']>, intro?: Maybe<ResolversTypes['RichText']> }>;
|
|
1012
1063
|
CanBeSyndicated: ResolverTypeWrapper<Scalars['CanBeSyndicated']['output']>;
|
|
1064
|
+
Caption: ResolverTypeWrapper<Caption>;
|
|
1065
|
+
Clip: ResolverTypeWrapper<ClipModel>;
|
|
1066
|
+
ClipFormat: ResolverTypeWrapper<Scalars['ClipFormat']['output']>;
|
|
1067
|
+
ClipSet: ResolverTypeWrapper<ReferenceWithCAPIData<ClipSetNode|OldClipNode>>;
|
|
1068
|
+
ClipSource: ResolverTypeWrapper<ClipSource>;
|
|
1013
1069
|
Concept: ResolverTypeWrapper<ConceptModel>;
|
|
1014
1070
|
Content: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['Content']>;
|
|
1015
1071
|
ContentPackage: ResolverTypeWrapper<CapiResponse>;
|
|
@@ -1081,6 +1137,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
1081
1137
|
/** Mapping between all available schema types and the resolvers parents */
|
|
1082
1138
|
export type ResolversParentTypes = ResolversObject<{
|
|
1083
1139
|
AccessLevel: Scalars['AccessLevel']['output'];
|
|
1140
|
+
Accessibility: Accessibility;
|
|
1084
1141
|
AltStandfirst: AltStandfirst;
|
|
1085
1142
|
AltTitle: AltTitle;
|
|
1086
1143
|
Article: CapiResponse;
|
|
@@ -1089,6 +1146,11 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
1089
1146
|
Boolean: Scalars['Boolean']['output'];
|
|
1090
1147
|
BrandedTopper: Omit<BrandedTopper, 'brandConcept' | 'displayConcept' | 'genreConcept' | 'intro'> & { brandConcept?: Maybe<ResolversParentTypes['Concept']>, displayConcept?: Maybe<ResolversParentTypes['Concept']>, genreConcept?: Maybe<ResolversParentTypes['Concept']>, intro?: Maybe<ResolversParentTypes['RichText']> };
|
|
1091
1148
|
CanBeSyndicated: Scalars['CanBeSyndicated']['output'];
|
|
1149
|
+
Caption: Caption;
|
|
1150
|
+
Clip: ClipModel;
|
|
1151
|
+
ClipFormat: Scalars['ClipFormat']['output'];
|
|
1152
|
+
ClipSet: ReferenceWithCAPIData<ClipSetNode|OldClipNode>;
|
|
1153
|
+
ClipSource: ClipSource;
|
|
1092
1154
|
Concept: ConceptModel;
|
|
1093
1155
|
Content: ResolversInterfaceTypes<ResolversParentTypes>['Content'];
|
|
1094
1156
|
ContentPackage: CapiResponse;
|
|
@@ -1161,6 +1223,12 @@ export interface AccessLevelScalarConfig extends GraphQLScalarTypeConfig<Resolve
|
|
|
1161
1223
|
name: 'AccessLevel';
|
|
1162
1224
|
}
|
|
1163
1225
|
|
|
1226
|
+
export type AccessibilityResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Accessibility'] = ResolversParentTypes['Accessibility']> = ResolversObject<{
|
|
1227
|
+
captions?: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['Caption']>>>, ParentType, ContextType>;
|
|
1228
|
+
transcript?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1229
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
1230
|
+
}>;
|
|
1231
|
+
|
|
1164
1232
|
export type AltStandfirstResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['AltStandfirst'] = ResolversParentTypes['AltStandfirst']> = ResolversObject<{
|
|
1165
1233
|
promotionalStandfirst?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1166
1234
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
@@ -1254,6 +1322,57 @@ export interface CanBeSyndicatedScalarConfig extends GraphQLScalarTypeConfig<Res
|
|
|
1254
1322
|
name: 'CanBeSyndicated';
|
|
1255
1323
|
}
|
|
1256
1324
|
|
|
1325
|
+
export type CaptionResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Caption'] = ResolversParentTypes['Caption']> = ResolversObject<{
|
|
1326
|
+
mediaType?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1327
|
+
url?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1328
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
1329
|
+
}>;
|
|
1330
|
+
|
|
1331
|
+
export type ClipResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Clip'] = ResolversParentTypes['Clip']> = ResolversObject<{
|
|
1332
|
+
dataSource?: Resolver<ReadonlyArray<ResolversTypes['ClipSource']>, ParentType, ContextType>;
|
|
1333
|
+
format?: Resolver<Maybe<ResolversTypes['ClipFormat']>, ParentType, ContextType>;
|
|
1334
|
+
id?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1335
|
+
poster?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1336
|
+
type?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1337
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
1338
|
+
}>;
|
|
1339
|
+
|
|
1340
|
+
export interface ClipFormatScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['ClipFormat'], any> {
|
|
1341
|
+
name: 'ClipFormat';
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
export type ClipSetResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['ClipSet'] = ResolversParentTypes['ClipSet']> = ResolversObject<{
|
|
1345
|
+
accessibility?: Resolver<Maybe<ResolversTypes['Accessibility']>, ParentType, ContextType>;
|
|
1346
|
+
autoplay?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
1347
|
+
caption?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1348
|
+
clips?: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['Clip']>>>, ParentType, ContextType>;
|
|
1349
|
+
contentWarning?: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['String']>>>, ParentType, ContextType>;
|
|
1350
|
+
credits?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1351
|
+
dataLayout?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1352
|
+
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1353
|
+
displayTitle?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1354
|
+
id?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1355
|
+
loop?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
1356
|
+
muted?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
1357
|
+
noAudio?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
1358
|
+
publishedDate?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1359
|
+
source?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1360
|
+
subtitle?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1361
|
+
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1362
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
1363
|
+
}>;
|
|
1364
|
+
|
|
1365
|
+
export type ClipSourceResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['ClipSource'] = ResolversParentTypes['ClipSource']> = ResolversObject<{
|
|
1366
|
+
audioCodec?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1367
|
+
binaryUrl?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1368
|
+
duration?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
1369
|
+
mediaType?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1370
|
+
pixelHeight?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
1371
|
+
pixelWidth?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
1372
|
+
videoCodec?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1373
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
1374
|
+
}>;
|
|
1375
|
+
|
|
1257
1376
|
export type ConceptResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Concept'] = ResolversParentTypes['Concept']> = ResolversObject<{
|
|
1258
1377
|
apiUrl?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
1259
1378
|
directType?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
@@ -1782,7 +1901,7 @@ export type RecommendedResolvers<ContextType = QueryContext, ParentType extends
|
|
|
1782
1901
|
}>;
|
|
1783
1902
|
|
|
1784
1903
|
export type ReferenceResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Reference'] = ResolversParentTypes['Reference']> = ResolversObject<{
|
|
1785
|
-
__resolveType?: TypeResolveFn<'Flourish' | 'ImageSet' | 'LayoutImage' | 'MainImage' | 'RawImage' | 'Recommended' | 'ScrollyImage' | 'Tweet' | 'VideoReference', ParentType, ContextType>;
|
|
1904
|
+
__resolveType?: TypeResolveFn<'ClipSet' | 'Flourish' | 'ImageSet' | 'LayoutImage' | 'MainImage' | 'RawImage' | 'Recommended' | 'ScrollyImage' | 'Tweet' | 'VideoReference', ParentType, ContextType>;
|
|
1786
1905
|
type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
1787
1906
|
}>;
|
|
1788
1907
|
|
|
@@ -1936,6 +2055,7 @@ export type VideoReferenceResolvers<ContextType = QueryContext, ParentType exten
|
|
|
1936
2055
|
|
|
1937
2056
|
export type Resolvers<ContextType = QueryContext> = ResolversObject<{
|
|
1938
2057
|
AccessLevel?: GraphQLScalarType;
|
|
2058
|
+
Accessibility?: AccessibilityResolvers<ContextType>;
|
|
1939
2059
|
AltStandfirst?: AltStandfirstResolvers<ContextType>;
|
|
1940
2060
|
AltTitle?: AltTitleResolvers<ContextType>;
|
|
1941
2061
|
Article?: ArticleResolvers<ContextType>;
|
|
@@ -1943,6 +2063,11 @@ export type Resolvers<ContextType = QueryContext> = ResolversObject<{
|
|
|
1943
2063
|
BasicTopper?: BasicTopperResolvers<ContextType>;
|
|
1944
2064
|
BrandedTopper?: BrandedTopperResolvers<ContextType>;
|
|
1945
2065
|
CanBeSyndicated?: GraphQLScalarType;
|
|
2066
|
+
Caption?: CaptionResolvers<ContextType>;
|
|
2067
|
+
Clip?: ClipResolvers<ContextType>;
|
|
2068
|
+
ClipFormat?: GraphQLScalarType;
|
|
2069
|
+
ClipSet?: ClipSetResolvers<ContextType>;
|
|
2070
|
+
ClipSource?: ClipSourceResolvers<ContextType>;
|
|
1946
2071
|
Concept?: ConceptResolvers<ContextType>;
|
|
1947
2072
|
Content?: ContentResolvers<ContextType>;
|
|
1948
2073
|
ContentPackage?: ContentPackageResolvers<ContextType>;
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
ImageSet,
|
|
3
3
|
ContentTypeSchemas,
|
|
4
4
|
MainImage,
|
|
5
|
+
ClipSet,
|
|
5
6
|
} from '../types/internal-content'
|
|
6
7
|
import conceptIds from '@financial-times/n-concept-ids'
|
|
7
8
|
import metadata from '@financial-times/n-display-metadata'
|
|
@@ -125,7 +126,7 @@ export class CapiResponse {
|
|
|
125
126
|
/*
|
|
126
127
|
* Check if the incoming data matches the types defined in our data source schema
|
|
127
128
|
* Our data source schema should handle all possible response from CAPI
|
|
128
|
-
* As there is no agreed schema provided by CAPI there is a chance it will out of date / incorrect at times
|
|
129
|
+
* As there is no agreed schema provided by CAPI there is a chance it will be out of date / incorrect at times
|
|
129
130
|
* Manual updates will be required at times to keep it in sync with the responses we receive
|
|
130
131
|
*/
|
|
131
132
|
const schemaResponse = model.schema().safeParse(content)
|
|
@@ -177,7 +178,7 @@ export class CapiResponse {
|
|
|
177
178
|
}
|
|
178
179
|
return null
|
|
179
180
|
}
|
|
180
|
-
embeds(): ImageSet[] {
|
|
181
|
+
embeds(): (ImageSet | ClipSet)[] {
|
|
181
182
|
if ('embeds' in this.capiData && this.capiData.embeds)
|
|
182
183
|
return this.capiData.embeds
|
|
183
184
|
return []
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { uuidFromUrl } from '../helpers/metadata'
|
|
2
|
+
import { Clip as ClipMetadata } from '../types/internal-content'
|
|
3
|
+
import {
|
|
4
|
+
LiteralUnionScalarValues,
|
|
5
|
+
validLiteralUnionValue,
|
|
6
|
+
} from '../resolvers/literal-union'
|
|
7
|
+
import { ClipFormat } from '../resolvers/scalars'
|
|
8
|
+
|
|
9
|
+
export type ClipSource = {
|
|
10
|
+
binaryUrl: string
|
|
11
|
+
mediaType: string
|
|
12
|
+
audioCodec?: string
|
|
13
|
+
duration?: number
|
|
14
|
+
pixelHeight?: number
|
|
15
|
+
pixelWidth?: number
|
|
16
|
+
videoCodec?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ClipVideo {
|
|
20
|
+
id(): string
|
|
21
|
+
type(): string
|
|
22
|
+
format(): LiteralUnionScalarValues<typeof ClipFormat>
|
|
23
|
+
poster(): string
|
|
24
|
+
dataSource(): ClipSource[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class Clip implements ClipVideo {
|
|
28
|
+
constructor(private clip: ClipMetadata) {}
|
|
29
|
+
|
|
30
|
+
type() {
|
|
31
|
+
return 'clip'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
id() {
|
|
35
|
+
const uuid = uuidFromUrl(this.clip.id)
|
|
36
|
+
if (!uuid) {
|
|
37
|
+
throw new Error(`${this.clip.id} is not a valid Content API Clip ID`)
|
|
38
|
+
}
|
|
39
|
+
return uuid
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
format(): LiteralUnionScalarValues<typeof ClipFormat> {
|
|
43
|
+
if (this.clip.format) {
|
|
44
|
+
const capiFormat = this.clip.format
|
|
45
|
+
|
|
46
|
+
if (capiFormat === 'standardInline') {
|
|
47
|
+
return 'standard-inline'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (validLiteralUnionValue(this.clip.format, ClipFormat.values)) {
|
|
51
|
+
return capiFormat
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return 'standard-inline'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
dataSource() {
|
|
59
|
+
// Order clip dataSource to have an order of video formats first then audio/mpeg, as the browser will first check if it can play the MIME type of the first source
|
|
60
|
+
const dataSource = this.clip.dataSource.slice().sort((a, b) => {
|
|
61
|
+
if (a.mediaType === 'video/mp4' && b.mediaType !== 'video/mp4') {
|
|
62
|
+
return -1
|
|
63
|
+
}
|
|
64
|
+
if (a.mediaType !== 'video/mp4' && b.mediaType === 'video/mp4') {
|
|
65
|
+
return 1
|
|
66
|
+
}
|
|
67
|
+
return 0
|
|
68
|
+
})
|
|
69
|
+
return dataSource as ClipSource[]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
poster() {
|
|
73
|
+
return this.clip.poster?.members[0].binaryUrl ?? ''
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -96,6 +96,20 @@ Object {
|
|
|
96
96
|
"type": "image-set",
|
|
97
97
|
},
|
|
98
98
|
},
|
|
99
|
+
Object {
|
|
100
|
+
"contentApiData": undefined,
|
|
101
|
+
"reference": Object {
|
|
102
|
+
"autoplay": true,
|
|
103
|
+
"data": Object {
|
|
104
|
+
"referenceIndex": 9,
|
|
105
|
+
},
|
|
106
|
+
"dataLayout": "in-line",
|
|
107
|
+
"id": "http://api-t.ft.com/content/f17fe25b-cdea-4d5f-a6af-40e56e33e888",
|
|
108
|
+
"loop": true,
|
|
109
|
+
"muted": true,
|
|
110
|
+
"type": "clip-set",
|
|
111
|
+
},
|
|
112
|
+
},
|
|
99
113
|
],
|
|
100
114
|
"text": "Eliot After The Waste Land (Eliot Biographies, 2)by Robert Crawford, Jonathan Cape £25
|
|
101
115
|
|
|
@@ -905,6 +919,17 @@ Join our online book group on Facebook at FT Books Café",
|
|
|
905
919
|
],
|
|
906
920
|
"type": "paragraph",
|
|
907
921
|
},
|
|
922
|
+
Object {
|
|
923
|
+
"autoplay": true,
|
|
924
|
+
"data": Object {
|
|
925
|
+
"referenceIndex": 9,
|
|
926
|
+
},
|
|
927
|
+
"dataLayout": "in-line",
|
|
928
|
+
"id": "http://api-t.ft.com/content/f17fe25b-cdea-4d5f-a6af-40e56e33e888",
|
|
929
|
+
"loop": true,
|
|
930
|
+
"muted": true,
|
|
931
|
+
"type": "clip-set",
|
|
932
|
+
},
|
|
908
933
|
Object {
|
|
909
934
|
"children": Array [
|
|
910
935
|
Object {
|
|
@@ -88,6 +88,50 @@ export const AlternativeImage = z.object({
|
|
|
88
88
|
promotionalImage: BaseImage,
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
+
const ClipSource = z.object({
|
|
92
|
+
audioCodec: z.string().optional(),
|
|
93
|
+
binaryUrl: z.string().optional(),
|
|
94
|
+
duration: z.number().optional(),
|
|
95
|
+
mediaType: z.string().optional(),
|
|
96
|
+
pixelHeight: z.number().optional(),
|
|
97
|
+
pixelWidth: z.number().optional(),
|
|
98
|
+
videoCodec: z.string().optional(),
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
export const Clip = z.object({
|
|
102
|
+
id: z.string(),
|
|
103
|
+
type: z.literal('http://www.ft.com/ontology/content/Clip'),
|
|
104
|
+
format: z
|
|
105
|
+
.union([z.literal('standardInline'), z.literal('mobile')])
|
|
106
|
+
.optional(),
|
|
107
|
+
dataSource: ClipSource.array(),
|
|
108
|
+
poster: ImageSet.optional(),
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
export const ClipSet = z.object({
|
|
112
|
+
id: z.string(),
|
|
113
|
+
type: z.literal('http://www.ft.com/ontology/content/ClipSet'),
|
|
114
|
+
members: Clip.array(),
|
|
115
|
+
caption: z.string().optional(),
|
|
116
|
+
dataCopyright: z.string().optional(),
|
|
117
|
+
description: z.string().optional(),
|
|
118
|
+
displayTitle: z.string().optional(),
|
|
119
|
+
contentWarning: z.string().array().optional(),
|
|
120
|
+
noAudio: z.boolean().optional(),
|
|
121
|
+
source: z.string().optional(),
|
|
122
|
+
subtitle: z.string().optional(),
|
|
123
|
+
publishedDate: z.string().optional(),
|
|
124
|
+
accessibility: z.object({
|
|
125
|
+
captions: z.array(
|
|
126
|
+
z.object({
|
|
127
|
+
mediaType: z.string(),
|
|
128
|
+
url: z.string(),
|
|
129
|
+
})
|
|
130
|
+
),
|
|
131
|
+
transcript: z.string().optional(),
|
|
132
|
+
}),
|
|
133
|
+
})
|
|
134
|
+
|
|
91
135
|
export const DataSource = z.object({
|
|
92
136
|
binaryUrl: z.string(),
|
|
93
137
|
duration: z.number(),
|
|
@@ -213,6 +257,6 @@ export const baseMediaSchema = z.object({
|
|
|
213
257
|
mainImage: MainImage.optional(),
|
|
214
258
|
leadImages: LeadImage.array().optional(),
|
|
215
259
|
alternativeImages: AlternativeImage.optional(),
|
|
216
|
-
embeds:
|
|
260
|
+
embeds: z.array(z.union([ImageSet, ClipSet])).optional(),
|
|
217
261
|
dataSource: DataSource.array().optional(),
|
|
218
262
|
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ClipResolvers } from '../generated'
|
|
2
|
+
|
|
3
|
+
const resolvers = {
|
|
4
|
+
Clip: {
|
|
5
|
+
__resolveType: () => 'Clip',
|
|
6
|
+
dataSource: (clip) => clip.dataSource(),
|
|
7
|
+
type: (clip) => clip.type(),
|
|
8
|
+
format: (clip) => clip.format(),
|
|
9
|
+
poster: (clip) => clip.poster(),
|
|
10
|
+
},
|
|
11
|
+
} satisfies { Clip: ClipResolvers }
|
|
12
|
+
|
|
13
|
+
export default resolvers
|
|
@@ -68,7 +68,7 @@ export interface YoutubeVideo extends ContentTree.Node {
|
|
|
68
68
|
url: string
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export interface
|
|
71
|
+
export interface OldClip extends ContentTree.Node {
|
|
72
72
|
type: 'clip'
|
|
73
73
|
url: string
|
|
74
74
|
autoplay: boolean
|
|
@@ -83,6 +83,16 @@ export interface Clip extends ContentTree.Node {
|
|
|
83
83
|
credits: string
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
// this type is used for bodyXML transformation ONLY in tagMappings
|
|
87
|
+
export interface ClipSet extends ContentTree.Node {
|
|
88
|
+
type: 'clip-set'
|
|
89
|
+
id: string
|
|
90
|
+
autoplay: boolean
|
|
91
|
+
loop: boolean
|
|
92
|
+
muted: boolean
|
|
93
|
+
dataLayout: 'in-line' | 'mid-grid' | 'full-grid'
|
|
94
|
+
}
|
|
95
|
+
|
|
86
96
|
export type TableColumnSettings = {
|
|
87
97
|
hideOnMobile: boolean
|
|
88
98
|
sortable: boolean
|
|
@@ -148,6 +158,8 @@ export type AnyNode =
|
|
|
148
158
|
| ContentTree.Blockquote
|
|
149
159
|
| ContentTree.Pullquote
|
|
150
160
|
| ContentTree.ImageSet
|
|
161
|
+
| ClipSet
|
|
162
|
+
| OldClip
|
|
151
163
|
| ContentTree.Recommended
|
|
152
164
|
| ContentTree.Tweet
|
|
153
165
|
| ContentTree.Flourish
|
|
@@ -168,7 +180,6 @@ export type AnyNode =
|
|
|
168
180
|
| TableCell
|
|
169
181
|
| Video
|
|
170
182
|
| YoutubeVideo
|
|
171
|
-
| Clip
|
|
172
183
|
| MainImage
|
|
173
184
|
| MainImageRaw
|
|
174
185
|
| RawImage
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ClipSet as CAPIClipSet } from '../../../types/internal-content'
|
|
2
|
+
import type { ClipSet as ClipSetWorkaroundContentTree, OldClip as OldClipContentTree } from '../Workarounds'
|
|
3
|
+
import { uuidFromUrl } from '../../../helpers/metadata'
|
|
4
|
+
import { Clip } from '../../../model/Clip'
|
|
5
|
+
import { ClipSetResolvers } from '../../../generated'
|
|
6
|
+
import { ReferenceWithCAPIData } from '.'
|
|
7
|
+
|
|
8
|
+
function getClipSet(
|
|
9
|
+
parent: ReferenceWithCAPIData<ClipSetWorkaroundContentTree|OldClipContentTree>
|
|
10
|
+
) {
|
|
11
|
+
const clipSets = parent.contentApiData
|
|
12
|
+
?.embeds()
|
|
13
|
+
?.filter((embedded) => embedded.type.includes('ClipSet')) as
|
|
14
|
+
| CAPIClipSet[]
|
|
15
|
+
| []
|
|
16
|
+
|
|
17
|
+
const clipSet = clipSets?.find(
|
|
18
|
+
(embed: CAPIClipSet) =>
|
|
19
|
+
uuidFromUrl(embed.id) === uuidFromUrl((parent.reference as ClipSetWorkaroundContentTree).id)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return clipSet as CAPIClipSet
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const ClipSet = {
|
|
26
|
+
// amend id, caption, description, credits when v1 is no longer supported
|
|
27
|
+
id: (parent) => ((parent.reference as ClipSetWorkaroundContentTree).id ? uuidFromUrl((parent.reference as ClipSetWorkaroundContentTree).id) : ''),
|
|
28
|
+
autoplay: (parent) => parent.reference.autoplay,
|
|
29
|
+
noAudio: (parent) => getClipSet(parent)?.noAudio || null,
|
|
30
|
+
loop: (parent) => parent.reference.loop,
|
|
31
|
+
muted: (parent) => parent.reference.muted,
|
|
32
|
+
dataLayout: (parent) => parent.reference.dataLayout,
|
|
33
|
+
caption: (parent) => ((parent.reference as OldClipContentTree).caption ?? getClipSet(parent)?.caption),
|
|
34
|
+
accessibility: (parent) => getClipSet(parent)?.accessibility || null,
|
|
35
|
+
description: (parent) => ((parent.reference as OldClipContentTree).description ?? getClipSet(parent)?.description),
|
|
36
|
+
credits: (parent) => ((parent.reference as OldClipContentTree).credits ?? getClipSet(parent)?.dataCopyright),
|
|
37
|
+
displayTitle: (parent) => getClipSet(parent)?.displayTitle || null,
|
|
38
|
+
contentWarning: (parent) => getClipSet(parent)?.contentWarning || null,
|
|
39
|
+
source: (parent) => getClipSet(parent)?.source || null,
|
|
40
|
+
subtitle: (parent) => getClipSet(parent)?.subtitle || null,
|
|
41
|
+
publishedDate: (parent) => getClipSet(parent)?.publishedDate || null,
|
|
42
|
+
async clips(parent) {
|
|
43
|
+
const clipSet = getClipSet(parent)
|
|
44
|
+
|
|
45
|
+
return clipSet && clipSet.members && clipSet.members.length > 0
|
|
46
|
+
? clipSet.members.map((clip) => new Clip(clip))
|
|
47
|
+
: null
|
|
48
|
+
},
|
|
49
|
+
} satisfies ClipSetResolvers
|
|
@@ -7,12 +7,16 @@ export const ImageSet = {
|
|
|
7
7
|
async picture(parent, _args, context) {
|
|
8
8
|
const isLiveBlog = parent.contentApiData?.type() === 'LiveBlogPost'
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const imageSets = parent.contentApiData
|
|
11
11
|
?.embeds()
|
|
12
|
-
?.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
?.filter((embedded) => embedded.type.includes('ImageSet')) as
|
|
13
|
+
| CAPIImageSet[]
|
|
14
|
+
| []
|
|
15
|
+
|
|
16
|
+
const imageSet = imageSets?.find(
|
|
17
|
+
(embed: CAPIImageSet) =>
|
|
18
|
+
uuidFromUrl(embed.id) === uuidFromUrl(parent.reference.id)
|
|
19
|
+
)
|
|
16
20
|
|
|
17
21
|
return imageSet && imageSet.members && imageSet.members.length > 0
|
|
18
22
|
? new Picture(imageSet, isLiveBlog, context)
|