@financial-times/cp-content-pipeline-schema 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/lib/generated/index.d.ts +2 -0
- package/lib/model/CapiResponse.d.ts +2 -0
- package/lib/model/CapiResponse.js +17 -3
- package/lib/model/CapiResponse.js.map +1 -1
- package/lib/model/Picture.d.ts +2 -1
- package/lib/model/Picture.js +4 -2
- package/lib/model/Picture.js.map +1 -1
- package/lib/model/Picture.test.js +3 -2
- package/lib/model/Picture.test.js.map +1 -1
- package/lib/model/schemas/capi/article.d.ts +29 -26
- package/lib/model/schemas/capi/article.js +1 -0
- package/lib/model/schemas/capi/article.js.map +1 -1
- package/lib/model/schemas/capi/audio.d.ts +9 -6
- package/lib/model/schemas/capi/audio.js +1 -0
- package/lib/model/schemas/capi/audio.js.map +1 -1
- package/lib/model/schemas/capi/base-schema.d.ts +32 -26
- package/lib/model/schemas/capi/base-schema.js +15 -7
- package/lib/model/schemas/capi/base-schema.js.map +1 -1
- package/lib/model/schemas/capi/content-package.d.ts +29 -26
- package/lib/model/schemas/capi/content-package.js +1 -0
- package/lib/model/schemas/capi/content-package.js.map +1 -1
- package/lib/model/schemas/capi/index.d.ts +1 -0
- package/lib/model/schemas/capi/live-blog-package.d.ts +32 -26
- package/lib/model/schemas/capi/live-blog-package.js +2 -0
- package/lib/model/schemas/capi/live-blog-package.js.map +1 -1
- package/lib/model/schemas/capi/placeholder.d.ts +29 -26
- package/lib/model/schemas/capi/placeholder.js +1 -0
- package/lib/model/schemas/capi/placeholder.js.map +1 -1
- package/lib/model/schemas/capi/video.d.ts +1 -0
- package/lib/resolvers/content-tree/references/ImageSet.js +2 -1
- package/lib/resolvers/content-tree/references/ImageSet.js.map +1 -1
- package/lib/resolvers/content-tree/references/LayoutImage.js +2 -1
- package/lib/resolvers/content-tree/references/LayoutImage.js.map +1 -1
- package/lib/resolvers/content-tree/references/ScrollyImage.js +2 -1
- package/lib/resolvers/content-tree/references/ScrollyImage.js.map +1 -1
- package/lib/resolvers/content.d.ts +1 -0
- package/lib/resolvers/content.js +1 -0
- package/lib/resolvers/content.js.map +1 -1
- package/lib/resolvers/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/generated/index.ts +2 -0
- package/src/model/CapiResponse.ts +25 -3
- package/src/model/Picture.test.ts +8 -2
- package/src/model/Picture.ts +9 -2
- package/src/model/schemas/capi/article.ts +1 -0
- package/src/model/schemas/capi/audio.ts +1 -0
- package/src/model/schemas/capi/base-schema.ts +23 -15
- package/src/model/schemas/capi/content-package.ts +1 -0
- package/src/model/schemas/capi/live-blog-package.ts +2 -0
- package/src/model/schemas/capi/placeholder.ts +1 -0
- package/src/resolvers/content-tree/references/ImageSet.ts +3 -1
- package/src/resolvers/content-tree/references/LayoutImage.ts +3 -0
- package/src/resolvers/content-tree/references/ScrollyImage.ts +3 -1
- package/src/resolvers/content.ts +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/content.graphql +1 -0
|
@@ -203,7 +203,11 @@ export class CapiResponse {
|
|
|
203
203
|
return this.capiData.types
|
|
204
204
|
}
|
|
205
205
|
async url(vanity?: boolean | null) {
|
|
206
|
-
const url =
|
|
206
|
+
const url =
|
|
207
|
+
this.capiData.webUrl ??
|
|
208
|
+
this.capiData.canonicalWebUrl ??
|
|
209
|
+
`https://www.ft.com/content/${this.id()}`
|
|
210
|
+
|
|
207
211
|
if (vanity) {
|
|
208
212
|
try {
|
|
209
213
|
const vanityUrl = await this.context.dataSources.vanityUrls.get(url)
|
|
@@ -231,7 +235,7 @@ export class CapiResponse {
|
|
|
231
235
|
return null
|
|
232
236
|
}
|
|
233
237
|
accessLevel(): LiteralUnionScalarValues<typeof AccessLevel> {
|
|
234
|
-
return this.capiData.accessLevel
|
|
238
|
+
return this.capiData.accessLevel ?? 'subscribed'
|
|
235
239
|
}
|
|
236
240
|
canBeSyndicated(): LiteralUnionScalarValues<typeof CanBeSyndicated> {
|
|
237
241
|
return this.capiData.canBeSyndicated
|
|
@@ -533,6 +537,23 @@ export class CapiResponse {
|
|
|
533
537
|
return []
|
|
534
538
|
}
|
|
535
539
|
|
|
540
|
+
async pinnedPost(): Promise<CapiResponse | null> {
|
|
541
|
+
if ('pinnedPosts' in this.capiData) {
|
|
542
|
+
const pinnedPosts = this.capiData.pinnedPosts || []
|
|
543
|
+
const liveBlogPosts = await this.liveBlogPosts()
|
|
544
|
+
|
|
545
|
+
const pinnedPostId = pinnedPosts[0]
|
|
546
|
+
const pinnedPostIndex = liveBlogPosts.findIndex(
|
|
547
|
+
(post) => post.id() === pinnedPostId
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
if (pinnedPostIndex !== -1) {
|
|
551
|
+
return liveBlogPosts.splice(pinnedPostIndex, 1)[0]
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return null
|
|
555
|
+
}
|
|
556
|
+
|
|
536
557
|
isContainedInPackage(): boolean {
|
|
537
558
|
return 'containedIn' in this.capiData
|
|
538
559
|
? (this.capiData.containedIn?.length ?? 0) > 0
|
|
@@ -551,8 +572,9 @@ export class CapiResponse {
|
|
|
551
572
|
|
|
552
573
|
commentsEnabled(): boolean {
|
|
553
574
|
if ('comments' in this.capiData) {
|
|
554
|
-
return this.capiData.comments
|
|
575
|
+
return this.capiData.comments?.enabled ?? false
|
|
555
576
|
}
|
|
577
|
+
|
|
556
578
|
return false
|
|
557
579
|
}
|
|
558
580
|
|
|
@@ -2,6 +2,8 @@ import { QueryContext } from '..'
|
|
|
2
2
|
import { ImageSet } from '../types/internal-content'
|
|
3
3
|
import { Picture } from './Picture'
|
|
4
4
|
|
|
5
|
+
const isLiveBlog = false
|
|
6
|
+
|
|
5
7
|
describe('Picture model', () => {
|
|
6
8
|
const mockImageSet = {
|
|
7
9
|
apiUrl: 'https://api.ft.com/content/image-set',
|
|
@@ -35,7 +37,7 @@ describe('Picture model', () => {
|
|
|
35
37
|
|
|
36
38
|
describe('finding the standard image', () => {
|
|
37
39
|
it('uses the StandardInline image if it exists', () => {
|
|
38
|
-
const picture = new Picture(mockImageSet, mockContext)
|
|
40
|
+
const picture = new Picture(mockImageSet, isLiveBlog, mockContext)
|
|
39
41
|
expect(picture.standard().url()).toEqual(
|
|
40
42
|
'https://cloudfront.com/standard-inline.jpg'
|
|
41
43
|
)
|
|
@@ -73,7 +75,11 @@ describe('Picture model', () => {
|
|
|
73
75
|
type: 'http://www.ft.com/ontology/content/ImageSet',
|
|
74
76
|
} as ImageSet
|
|
75
77
|
|
|
76
|
-
const picture = new Picture(
|
|
78
|
+
const picture = new Picture(
|
|
79
|
+
mockWithoutStandardInline,
|
|
80
|
+
isLiveBlog,
|
|
81
|
+
mockContext
|
|
82
|
+
)
|
|
77
83
|
expect(picture.standard().url()).toEqual(
|
|
78
84
|
'https://cloudfront.com/desktop.jpg'
|
|
79
85
|
)
|
package/src/model/Picture.ts
CHANGED
|
@@ -14,7 +14,11 @@ function assertDefined<T>(
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export class Picture {
|
|
17
|
-
constructor(
|
|
17
|
+
constructor(
|
|
18
|
+
private imageSet: ImageSet,
|
|
19
|
+
private isLiveBlog: boolean,
|
|
20
|
+
private context: QueryContext
|
|
21
|
+
) {}
|
|
18
22
|
|
|
19
23
|
images(): Image[] {
|
|
20
24
|
return this.imageSet.members.map(
|
|
@@ -36,7 +40,10 @@ export class Picture {
|
|
|
36
40
|
|
|
37
41
|
async layoutWidth(): Promise<LayoutWidth> {
|
|
38
42
|
//TODO: actually work out the types
|
|
39
|
-
|
|
43
|
+
|
|
44
|
+
if (!this.isLiveBlog && this.imageSet.members.length === 3) {
|
|
45
|
+
return 'full-grid'
|
|
46
|
+
}
|
|
40
47
|
|
|
41
48
|
const dimensions = await this.standard().dimensions()
|
|
42
49
|
|
|
@@ -120,23 +120,28 @@ export const CapiPerson = z.object({
|
|
|
120
120
|
export const baseMetadataSchema = z.object({
|
|
121
121
|
id: z.string(),
|
|
122
122
|
annotations: Annotation.array(),
|
|
123
|
-
webUrl: z.string(),
|
|
123
|
+
webUrl: z.string().optional(),
|
|
124
|
+
canonicalWebUrl: z.string().optional(),
|
|
124
125
|
type: z.string(),
|
|
125
126
|
types: z.string().array(),
|
|
126
|
-
standout: z
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
standout: z
|
|
128
|
+
.object({
|
|
129
|
+
editorsChoice: z.boolean(),
|
|
130
|
+
exclusive: z.boolean(),
|
|
131
|
+
scoop: z.boolean(),
|
|
132
|
+
})
|
|
133
|
+
.optional(),
|
|
131
134
|
publishedDate: z.string(),
|
|
132
135
|
firstPublishedDate: z.string(),
|
|
133
136
|
realtime: z.boolean(),
|
|
134
|
-
accessLevel: z
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
accessLevel: z
|
|
138
|
+
.union([
|
|
139
|
+
z.literal('premium'),
|
|
140
|
+
z.literal('subscribed'),
|
|
141
|
+
z.literal('registered'),
|
|
142
|
+
z.literal('free'),
|
|
143
|
+
])
|
|
144
|
+
.optional(),
|
|
140
145
|
canBeSyndicated: z.union([
|
|
141
146
|
z.literal('yes'),
|
|
142
147
|
z.literal('no'),
|
|
@@ -145,9 +150,11 @@ export const baseMetadataSchema = z.object({
|
|
|
145
150
|
z.literal('unknown'),
|
|
146
151
|
]),
|
|
147
152
|
topper: Topper.optional(),
|
|
148
|
-
comments: z
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
comments: z
|
|
154
|
+
.object({
|
|
155
|
+
enabled: z.boolean(),
|
|
156
|
+
})
|
|
157
|
+
.optional(),
|
|
151
158
|
containedIn: z
|
|
152
159
|
.object({ apiUrl: z.string(), id: z.string() })
|
|
153
160
|
.array()
|
|
@@ -175,6 +182,7 @@ export const baseContentSchema = z.object({
|
|
|
175
182
|
promotionalStandfirst: z.string().optional(),
|
|
176
183
|
})
|
|
177
184
|
.optional(),
|
|
185
|
+
pinnedPosts: z.string().array(),
|
|
178
186
|
contains: z
|
|
179
187
|
.object({
|
|
180
188
|
id: z.string(),
|
|
@@ -10,6 +10,7 @@ const liveBlogPackageContentSchema = baseContentSchema.pick({
|
|
|
10
10
|
summary: true,
|
|
11
11
|
alternativeTitles: true,
|
|
12
12
|
contains: true,
|
|
13
|
+
pinnedPosts: true,
|
|
13
14
|
realtime: true,
|
|
14
15
|
})
|
|
15
16
|
|
|
@@ -17,6 +18,7 @@ const liveBlogPackageMetadataSchema = baseMetadataSchema.pick({
|
|
|
17
18
|
id: true,
|
|
18
19
|
annotations: true,
|
|
19
20
|
webUrl: true,
|
|
21
|
+
canonicalWebUrl: true,
|
|
20
22
|
type: true,
|
|
21
23
|
types: true,
|
|
22
24
|
standout: true,
|
|
@@ -5,6 +5,8 @@ import { ImageSetResolvers } from '../../../generated'
|
|
|
5
5
|
|
|
6
6
|
export const ImageSet = {
|
|
7
7
|
picture(parent, _args, context) {
|
|
8
|
+
const isLiveBlog = parent.contentApiData?.type() === 'LiveBlogPost'
|
|
9
|
+
|
|
8
10
|
const imageSet = parent.contentApiData
|
|
9
11
|
?.embeds()
|
|
10
12
|
?.find(
|
|
@@ -12,6 +14,6 @@ export const ImageSet = {
|
|
|
12
14
|
uuidFromUrl(embed.id) === uuidFromUrl(parent.reference.id)
|
|
13
15
|
)
|
|
14
16
|
|
|
15
|
-
return imageSet ? new Picture(imageSet, context) : null
|
|
17
|
+
return imageSet ? new Picture(imageSet, isLiveBlog, context) : null
|
|
16
18
|
},
|
|
17
19
|
} satisfies ImageSetResolvers
|
|
@@ -3,6 +3,8 @@ import { LayoutImageResolvers } from '../../../generated'
|
|
|
3
3
|
|
|
4
4
|
export const LayoutImage = {
|
|
5
5
|
picture(parent, _args, context): Picture {
|
|
6
|
+
const isLiveBlog = parent.contentApiData?.type() === 'LiveBlogPost'
|
|
7
|
+
|
|
6
8
|
return new Picture(
|
|
7
9
|
{
|
|
8
10
|
id: 'layout-imageset',
|
|
@@ -21,6 +23,7 @@ export const LayoutImage = {
|
|
|
21
23
|
},
|
|
22
24
|
],
|
|
23
25
|
},
|
|
26
|
+
isLiveBlog,
|
|
24
27
|
context
|
|
25
28
|
)
|
|
26
29
|
},
|
|
@@ -5,6 +5,8 @@ import { ScrollyImageResolvers } from '../../../generated'
|
|
|
5
5
|
|
|
6
6
|
export const ScrollyImage = {
|
|
7
7
|
picture(parent, _args, context) {
|
|
8
|
+
const isLiveBlog = parent.contentApiData?.type() === 'LiveBlogPost'
|
|
9
|
+
|
|
8
10
|
const imageSet = parent.contentApiData
|
|
9
11
|
?.embeds()
|
|
10
12
|
?.find(
|
|
@@ -12,6 +14,6 @@ export const ScrollyImage = {
|
|
|
12
14
|
uuidFromUrl(embed.id) === uuidFromUrl(parent.reference.id)
|
|
13
15
|
)
|
|
14
16
|
|
|
15
|
-
return imageSet ? new Picture(imageSet, context) : null
|
|
17
|
+
return imageSet ? new Picture(imageSet, isLiveBlog, context) : null
|
|
16
18
|
},
|
|
17
19
|
} satisfies ScrollyImageResolvers
|