@financial-times/cp-content-pipeline-schema 2.9.2 → 2.10.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 +20 -0
- package/lib/datasources/capi.d.ts +1 -0
- package/lib/datasources/capi.js +10 -0
- package/lib/datasources/capi.js.map +1 -1
- package/lib/generated/index.d.ts +87 -2
- package/lib/model/CapiResponse.d.ts +6 -0
- package/lib/model/CapiResponse.js +5 -0
- package/lib/model/CapiResponse.js.map +1 -1
- package/lib/model/FlourishSource.d.ts +22 -0
- package/lib/model/FlourishSource.js +89 -0
- package/lib/model/FlourishSource.js.map +1 -0
- package/lib/model/FlourishSource.test.d.ts +1 -0
- package/lib/model/FlourishSource.test.js +67 -0
- package/lib/model/FlourishSource.test.js.map +1 -0
- package/lib/model/LeadFlourish.d.ts +13 -0
- package/lib/model/LeadFlourish.js +35 -0
- package/lib/model/LeadFlourish.js.map +1 -0
- package/lib/model/LeadFlourish.test.d.ts +1 -0
- package/lib/model/LeadFlourish.test.js +62 -0
- package/lib/model/LeadFlourish.test.js.map +1 -0
- package/lib/model/Topper.d.ts +4 -1
- package/lib/model/Topper.js +15 -0
- package/lib/model/Topper.js.map +1 -1
- package/lib/model/Topper.test.js +21 -0
- package/lib/model/Topper.test.js.map +1 -1
- package/lib/model/schemas/capi/article.d.ts +153 -60
- 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 +1111 -119
- package/lib/model/schemas/capi/base-schema.d.ts +166 -99
- package/lib/model/schemas/capi/base-schema.js +8 -1
- package/lib/model/schemas/capi/base-schema.js.map +1 -1
- package/lib/model/schemas/capi/content-package.d.ts +836 -46
- package/lib/model/schemas/capi/content-package.js +1 -1
- package/lib/model/schemas/capi/content-package.js.map +1 -1
- package/lib/model/schemas/capi/live-blog-package.d.ts +230 -65
- package/lib/model/schemas/capi/live-blog-package.js +0 -1
- package/lib/model/schemas/capi/live-blog-package.js.map +1 -1
- package/lib/model/schemas/capi/placeholder.d.ts +143 -60
- package/lib/resolvers/content-tree/references/Flourish.d.ts +8 -2
- package/lib/resolvers/content-tree/references/Flourish.js +15 -40
- package/lib/resolvers/content-tree/references/Flourish.js.map +1 -1
- package/lib/resolvers/content-tree/references/Flourish.test.js +0 -30
- package/lib/resolvers/content-tree/references/Flourish.test.js.map +1 -1
- package/lib/resolvers/index.d.ts +36 -9
- package/lib/resolvers/index.js +2 -0
- package/lib/resolvers/index.js.map +1 -1
- package/lib/resolvers/leadFlourish.d.ts +16 -0
- package/lib/resolvers/leadFlourish.js +28 -0
- package/lib/resolvers/leadFlourish.js.map +1 -0
- package/lib/resolvers/topper.d.ts +23 -9
- package/lib/resolvers/topper.js +6 -0
- package/lib/resolvers/topper.js.map +1 -1
- package/package.json +1 -1
- package/queries/article.graphql +26 -0
- package/src/datasources/capi.ts +14 -0
- package/src/generated/index.ts +93 -2
- package/src/model/CapiResponse.ts +6 -0
- package/src/model/FlourishSource.test.ts +93 -0
- package/src/model/FlourishSource.ts +103 -0
- package/src/model/LeadFlourish.test.ts +71 -0
- package/src/model/LeadFlourish.ts +30 -0
- package/src/model/Topper.test.ts +26 -0
- package/src/model/Topper.ts +18 -0
- package/src/model/schemas/capi/article.ts +1 -0
- package/src/model/schemas/capi/base-schema.ts +8 -0
- package/src/model/schemas/capi/content-package.ts +1 -1
- package/src/model/schemas/capi/live-blog-package.ts +0 -1
- package/src/resolvers/content-tree/references/Flourish.test.ts +2 -49
- package/src/resolvers/content-tree/references/Flourish.ts +15 -59
- package/src/resolvers/index.ts +2 -0
- package/src/resolvers/leadFlourish.ts +31 -0
- package/src/resolvers/topper.ts +10 -0
- package/src/types/internal-content.d.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/leadFlouish.graphql +29 -0
- package/typedefs/topper.graphql +35 -0
package/src/generated/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type { Clip as ClipModel } from '../model/Clip';
|
|
|
7
7
|
import type { Picture as PictureModel } from '../model/Picture';
|
|
8
8
|
import type { RichText as RichTextModel } from '../model/RichText';
|
|
9
9
|
import type { Topper as TopperModel } from '../model/Topper';
|
|
10
|
+
import type { LeadFlourish as LeadFlourishModel } from '../model/LeadFlourish';
|
|
11
|
+
import type { FlourishSource as FlourishSourceModel } from '../model/FlourishSource';
|
|
10
12
|
import type { ContentTree } from '@financial-times/content-tree';
|
|
11
13
|
import type { ReferenceWithCAPIData } from '../resolvers/content-tree/references';
|
|
12
14
|
import type { Video as VideoNode, ClipSet as ClipSetNode, OldClip as OldClipNode, RawImage as RawImageNode } from '../resolvers/content-tree/Workarounds';
|
|
@@ -549,6 +551,19 @@ export type FlourishFallback = {
|
|
|
549
551
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
550
552
|
};
|
|
551
553
|
|
|
554
|
+
export type FlourishSource = {
|
|
555
|
+
/** The format of the source, eg. 'standard'. */
|
|
556
|
+
readonly format?: Maybe<Scalars['String']['output']>;
|
|
557
|
+
/** The height of the source. */
|
|
558
|
+
readonly height?: Maybe<Scalars['Int']['output']>;
|
|
559
|
+
/** The type of the source, e.g. visualisation for leadFlourish */
|
|
560
|
+
readonly type?: Maybe<Scalars['String']['output']>;
|
|
561
|
+
/** The Origami image service url */
|
|
562
|
+
readonly url?: Maybe<Scalars['String']['output']>;
|
|
563
|
+
/** The width of the source. */
|
|
564
|
+
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
565
|
+
};
|
|
566
|
+
|
|
552
567
|
export type FullBleedTopper = Topper & TopperWithBrand & TopperWithImages & TopperWithTheme & {
|
|
553
568
|
/** Whether the topper should have a background box. */
|
|
554
569
|
readonly backgroundBox?: Maybe<Scalars['Boolean']['output']>;
|
|
@@ -909,6 +924,16 @@ export type LayoutImage = Reference & {
|
|
|
909
924
|
readonly type: Scalars['String']['output'];
|
|
910
925
|
};
|
|
911
926
|
|
|
927
|
+
export type LeadFlourish = {
|
|
928
|
+
/** The description of the Flourish chart. */
|
|
929
|
+
readonly description?: Maybe<Scalars['String']['output']>;
|
|
930
|
+
readonly fallbackImage: FlourishSource;
|
|
931
|
+
/** The id of the Flourish chart. */
|
|
932
|
+
readonly id?: Maybe<Scalars['String']['output']>;
|
|
933
|
+
/** The type of the chart, eg. 'visualisation'. */
|
|
934
|
+
readonly type?: Maybe<Scalars['String']['output']>;
|
|
935
|
+
};
|
|
936
|
+
|
|
912
937
|
export type LiveBlogPackage = Content & {
|
|
913
938
|
/** A scalar representing the access level of the article, eg. 'free'. */
|
|
914
939
|
readonly accessLevel?: Maybe<Scalars['AccessLevel']['output']>;
|
|
@@ -1462,6 +1487,31 @@ export type TopperWithBrand = {
|
|
|
1462
1487
|
readonly genreConcept?: Maybe<Concept>;
|
|
1463
1488
|
};
|
|
1464
1489
|
|
|
1490
|
+
export type TopperWithFlourish = Topper & {
|
|
1491
|
+
/** Whether the topper should have a background box. */
|
|
1492
|
+
readonly backgroundBox?: Maybe<Scalars['Boolean']['output']>;
|
|
1493
|
+
/** The background colour of the topper. */
|
|
1494
|
+
readonly backgroundColour?: Maybe<Scalars['TopperBackgroundColour']['output']>;
|
|
1495
|
+
/** The concept object to be displayed, eg. {'type': 'TOPIC', 'prefLabel': 'Non-dom tax status', ...}. */
|
|
1496
|
+
readonly displayConcept?: Maybe<Concept>;
|
|
1497
|
+
/** The variant of the follow button to be displayed on the topper. */
|
|
1498
|
+
readonly followButtonVariant?: Maybe<Scalars['FollowButtonVariant']['output']>;
|
|
1499
|
+
/** The concept object of the genre, eg. {'type': 'GENRE', 'prefLabel': 'Opinion', ...}. */
|
|
1500
|
+
readonly genreConcept?: Maybe<Concept>;
|
|
1501
|
+
/** The headline text of the topper. */
|
|
1502
|
+
readonly headline: Scalars['String']['output'];
|
|
1503
|
+
/** An abstract syntax tree of the introduction text. */
|
|
1504
|
+
readonly intro?: Maybe<RichText>;
|
|
1505
|
+
/** The layout type of the topper, eg. 'flourish'. */
|
|
1506
|
+
readonly layout?: Maybe<Scalars['String']['output']>;
|
|
1507
|
+
/** The layout width of the topper, eg. 'full-grid'. */
|
|
1508
|
+
readonly layoutWidth?: Maybe<Scalars['String']['output']>;
|
|
1509
|
+
/** The flourish object to be displayed, eg. {'type': 'visualisation', ...}. */
|
|
1510
|
+
readonly leadFlourish?: Maybe<LeadFlourish>;
|
|
1511
|
+
/** Whether the topper should have a text shadow. */
|
|
1512
|
+
readonly textShadow?: Maybe<Scalars['Boolean']['output']>;
|
|
1513
|
+
};
|
|
1514
|
+
|
|
1465
1515
|
export type TopperWithHeadshot = {
|
|
1466
1516
|
/** The url of the headshot image, eg. for columns with one key author. */
|
|
1467
1517
|
readonly headshot?: Maybe<Scalars['String']['output']>;
|
|
@@ -1650,7 +1700,7 @@ export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = R
|
|
|
1650
1700
|
Image: ( ImageModel ) | ( ImageModel ) | ( ImageModel ) | ( ImageModel ) | ( ImageModel ) | ( ImageModel ) | ( ImageModel ) | ( ImageModel ) | ( ImageModel );
|
|
1651
1701
|
Picture: ( PictureModel ) | ( PictureModel ) | ( PictureModel );
|
|
1652
1702
|
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> );
|
|
1653
|
-
Topper: ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel );
|
|
1703
|
+
Topper: ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel );
|
|
1654
1704
|
TopperWithBrand: ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel );
|
|
1655
1705
|
TopperWithHeadshot: ( TopperModel ) | ( TopperModel );
|
|
1656
1706
|
TopperWithImages: ( TopperModel ) | ( TopperModel ) | ( TopperModel ) | ( TopperModel );
|
|
@@ -1685,6 +1735,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
1685
1735
|
Float: ResolverTypeWrapper<Scalars['Float']['output']>;
|
|
1686
1736
|
Flourish: ResolverTypeWrapper<ReferenceWithCAPIData<ContentTree.Flourish>>;
|
|
1687
1737
|
FlourishFallback: ResolverTypeWrapper<FlourishFallback>;
|
|
1738
|
+
FlourishSource: ResolverTypeWrapper<FlourishSourceModel>;
|
|
1688
1739
|
FollowButtonVariant: ResolverTypeWrapper<Scalars['FollowButtonVariant']['output']>;
|
|
1689
1740
|
FullBleedTopper: ResolverTypeWrapper<TopperModel>;
|
|
1690
1741
|
ID: ResolverTypeWrapper<Scalars['ID']['output']>;
|
|
@@ -1706,6 +1757,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
1706
1757
|
Int: ResolverTypeWrapper<Scalars['Int']['output']>;
|
|
1707
1758
|
JSON: ResolverTypeWrapper<Scalars['JSON']['output']>;
|
|
1708
1759
|
LayoutImage: ResolverTypeWrapper<ReferenceWithCAPIData<ContentTree.LayoutImage>>;
|
|
1760
|
+
LeadFlourish: ResolverTypeWrapper<LeadFlourishModel>;
|
|
1709
1761
|
LiveBlogPackage: ResolverTypeWrapper<CapiResponse>;
|
|
1710
1762
|
LiveBlogPost: ResolverTypeWrapper<CapiResponse>;
|
|
1711
1763
|
MainImage: ResolverTypeWrapper<ReferenceWithCAPIData<ContentTree.ImageSet>>;
|
|
@@ -1736,6 +1788,7 @@ export type ResolversTypes = ResolversObject<{
|
|
|
1736
1788
|
Topper: ResolverTypeWrapper<TopperModel>;
|
|
1737
1789
|
TopperBackgroundColour: ResolverTypeWrapper<Scalars['TopperBackgroundColour']['output']>;
|
|
1738
1790
|
TopperWithBrand: ResolverTypeWrapper<TopperModel>;
|
|
1791
|
+
TopperWithFlourish: ResolverTypeWrapper<TopperModel>;
|
|
1739
1792
|
TopperWithHeadshot: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['TopperWithHeadshot']>;
|
|
1740
1793
|
TopperWithImages: ResolverTypeWrapper<TopperModel>;
|
|
1741
1794
|
TopperWithPackage: ResolverTypeWrapper<TopperModel>;
|
|
@@ -1772,6 +1825,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
1772
1825
|
Float: Scalars['Float']['output'];
|
|
1773
1826
|
Flourish: ReferenceWithCAPIData<ContentTree.Flourish>;
|
|
1774
1827
|
FlourishFallback: FlourishFallback;
|
|
1828
|
+
FlourishSource: FlourishSourceModel;
|
|
1775
1829
|
FollowButtonVariant: Scalars['FollowButtonVariant']['output'];
|
|
1776
1830
|
FullBleedTopper: TopperModel;
|
|
1777
1831
|
ID: Scalars['ID']['output'];
|
|
@@ -1793,6 +1847,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
1793
1847
|
Int: Scalars['Int']['output'];
|
|
1794
1848
|
JSON: Scalars['JSON']['output'];
|
|
1795
1849
|
LayoutImage: ReferenceWithCAPIData<ContentTree.LayoutImage>;
|
|
1850
|
+
LeadFlourish: LeadFlourishModel;
|
|
1796
1851
|
LiveBlogPackage: CapiResponse;
|
|
1797
1852
|
LiveBlogPost: CapiResponse;
|
|
1798
1853
|
MainImage: ReferenceWithCAPIData<ContentTree.ImageSet>;
|
|
@@ -1823,6 +1878,7 @@ export type ResolversParentTypes = ResolversObject<{
|
|
|
1823
1878
|
Topper: TopperModel;
|
|
1824
1879
|
TopperBackgroundColour: Scalars['TopperBackgroundColour']['output'];
|
|
1825
1880
|
TopperWithBrand: TopperModel;
|
|
1881
|
+
TopperWithFlourish: TopperModel;
|
|
1826
1882
|
TopperWithHeadshot: ResolversInterfaceTypes<ResolversParentTypes>['TopperWithHeadshot'];
|
|
1827
1883
|
TopperWithImages: TopperModel;
|
|
1828
1884
|
TopperWithPackage: TopperModel;
|
|
@@ -2121,6 +2177,15 @@ export type FlourishFallbackResolvers<ContextType = QueryContext, ParentType ext
|
|
|
2121
2177
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2122
2178
|
}>;
|
|
2123
2179
|
|
|
2180
|
+
export type FlourishSourceResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['FlourishSource'] = ResolversParentTypes['FlourishSource']> = ResolversObject<{
|
|
2181
|
+
format: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2182
|
+
height: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
2183
|
+
type: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2184
|
+
url: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2185
|
+
width: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
2186
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2187
|
+
}>;
|
|
2188
|
+
|
|
2124
2189
|
export interface FollowButtonVariantScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['FollowButtonVariant'], any> {
|
|
2125
2190
|
name: 'FollowButtonVariant';
|
|
2126
2191
|
}
|
|
@@ -2324,6 +2389,14 @@ export type LayoutImageResolvers<ContextType = QueryContext, ParentType extends
|
|
|
2324
2389
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2325
2390
|
}>;
|
|
2326
2391
|
|
|
2392
|
+
export type LeadFlourishResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['LeadFlourish'] = ResolversParentTypes['LeadFlourish']> = ResolversObject<{
|
|
2393
|
+
description: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2394
|
+
fallbackImage: Resolver<ResolversTypes['FlourishSource'], ParentType, ContextType>;
|
|
2395
|
+
id: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2396
|
+
type: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2397
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2398
|
+
}>;
|
|
2399
|
+
|
|
2327
2400
|
export type LiveBlogPackageResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['LiveBlogPackage'] = ResolversParentTypes['LiveBlogPackage']> = ResolversObject<{
|
|
2328
2401
|
accessLevel: Resolver<Maybe<ResolversTypes['AccessLevel']>, ParentType, ContextType>;
|
|
2329
2402
|
altStandfirst: Resolver<Maybe<ResolversTypes['AltStandfirst']>, ParentType, ContextType>;
|
|
@@ -2617,7 +2690,7 @@ export type TeaserResolvers<ContextType = QueryContext, ParentType extends Resol
|
|
|
2617
2690
|
}>;
|
|
2618
2691
|
|
|
2619
2692
|
export type TopperResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['Topper'] = ResolversParentTypes['Topper']> = ResolversObject<{
|
|
2620
|
-
__resolveType?: TypeResolveFn<'BasicTopper' | 'BrandedTopper' | 'DeepLandscapeTopper' | 'DeepPortraitTopper' | 'FullBleedTopper' | 'OpinionTopper' | 'PodcastTopper' | 'SplitTextTopper', ParentType, ContextType>;
|
|
2693
|
+
__resolveType?: TypeResolveFn<'BasicTopper' | 'BrandedTopper' | 'DeepLandscapeTopper' | 'DeepPortraitTopper' | 'FullBleedTopper' | 'OpinionTopper' | 'PodcastTopper' | 'SplitTextTopper' | 'TopperWithFlourish', ParentType, ContextType>;
|
|
2621
2694
|
backgroundBox: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
2622
2695
|
backgroundColour: Resolver<Maybe<ResolversTypes['TopperBackgroundColour']>, ParentType, ContextType>;
|
|
2623
2696
|
displayConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
@@ -2638,6 +2711,21 @@ export type TopperWithBrandResolvers<ContextType = QueryContext, ParentType exte
|
|
|
2638
2711
|
genreConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2639
2712
|
}>;
|
|
2640
2713
|
|
|
2714
|
+
export type TopperWithFlourishResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['TopperWithFlourish'] = ResolversParentTypes['TopperWithFlourish']> = ResolversObject<{
|
|
2715
|
+
backgroundBox: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
2716
|
+
backgroundColour: Resolver<Maybe<ResolversTypes['TopperBackgroundColour']>, ParentType, ContextType>;
|
|
2717
|
+
displayConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2718
|
+
followButtonVariant: Resolver<Maybe<ResolversTypes['FollowButtonVariant']>, ParentType, ContextType>;
|
|
2719
|
+
genreConcept: Resolver<Maybe<ResolversTypes['Concept']>, ParentType, ContextType>;
|
|
2720
|
+
headline: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2721
|
+
intro: Resolver<Maybe<ResolversTypes['RichText']>, ParentType, ContextType>;
|
|
2722
|
+
layout: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2723
|
+
layoutWidth: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2724
|
+
leadFlourish: Resolver<Maybe<ResolversTypes['LeadFlourish']>, ParentType, ContextType>;
|
|
2725
|
+
textShadow: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
|
2726
|
+
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2727
|
+
}>;
|
|
2728
|
+
|
|
2641
2729
|
export type TopperWithHeadshotResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['TopperWithHeadshot'] = ResolversParentTypes['TopperWithHeadshot']> = ResolversObject<{
|
|
2642
2730
|
__resolveType?: TypeResolveFn<'OpinionTopper' | 'PodcastTopper', ParentType, ContextType>;
|
|
2643
2731
|
headshot: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType, Partial<TopperWithHeadshotHeadshotArgs>>;
|
|
@@ -2725,6 +2813,7 @@ export type Resolvers<ContextType = QueryContext> = ResolversObject<{
|
|
|
2725
2813
|
Design: DesignResolvers<ContextType>;
|
|
2726
2814
|
Flourish: FlourishResolvers<ContextType>;
|
|
2727
2815
|
FlourishFallback: FlourishFallbackResolvers<ContextType>;
|
|
2816
|
+
FlourishSource: FlourishSourceResolvers<ContextType>;
|
|
2728
2817
|
FollowButtonVariant: GraphQLScalarType;
|
|
2729
2818
|
FullBleedTopper: FullBleedTopperResolvers<ContextType>;
|
|
2730
2819
|
Image: ImageResolvers<ContextType>;
|
|
@@ -2744,6 +2833,7 @@ export type Resolvers<ContextType = QueryContext> = ResolversObject<{
|
|
|
2744
2833
|
Indicators: IndicatorsResolvers<ContextType>;
|
|
2745
2834
|
JSON: GraphQLScalarType;
|
|
2746
2835
|
LayoutImage: LayoutImageResolvers<ContextType>;
|
|
2836
|
+
LeadFlourish: LeadFlourishResolvers<ContextType>;
|
|
2747
2837
|
LiveBlogPackage: LiveBlogPackageResolvers<ContextType>;
|
|
2748
2838
|
LiveBlogPost: LiveBlogPostResolvers<ContextType>;
|
|
2749
2839
|
MainImage: MainImageResolvers<ContextType>;
|
|
@@ -2773,6 +2863,7 @@ export type Resolvers<ContextType = QueryContext> = ResolversObject<{
|
|
|
2773
2863
|
Topper: TopperResolvers<ContextType>;
|
|
2774
2864
|
TopperBackgroundColour: GraphQLScalarType;
|
|
2775
2865
|
TopperWithBrand: TopperWithBrandResolvers<ContextType>;
|
|
2866
|
+
TopperWithFlourish: TopperWithFlourishResolvers<ContextType>;
|
|
2776
2867
|
TopperWithHeadshot: TopperWithHeadshotResolvers<ContextType>;
|
|
2777
2868
|
TopperWithImages: TopperWithImagesResolvers<ContextType>;
|
|
2778
2869
|
TopperWithPackage: TopperWithPackageResolvers<ContextType>;
|
|
@@ -236,6 +236,12 @@ export class CapiResponse {
|
|
|
236
236
|
return this.capiData.embeds
|
|
237
237
|
return []
|
|
238
238
|
}
|
|
239
|
+
|
|
240
|
+
leadFlourish() {
|
|
241
|
+
if ('leadFlourish' in this.capiData) return this.capiData.leadFlourish
|
|
242
|
+
return null
|
|
243
|
+
}
|
|
244
|
+
|
|
239
245
|
byline({ vanity }: Partial<ContentUrlArgs>) {
|
|
240
246
|
const bylineText = this.rawByline()
|
|
241
247
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { FlourishSource } from './FlourishSource'
|
|
2
|
+
import { QueryContext } from '..'
|
|
3
|
+
|
|
4
|
+
describe('FlourishSource', () => {
|
|
5
|
+
const mockFlourishData = {
|
|
6
|
+
id: '1234',
|
|
7
|
+
type: 'test-type',
|
|
8
|
+
description: 'Sample chart',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const origamiGetImageMetadataMock = jest.fn()
|
|
12
|
+
const context = {
|
|
13
|
+
systemCode: 'example-system',
|
|
14
|
+
dataSources: {
|
|
15
|
+
origami: {
|
|
16
|
+
getImageMetadata: origamiGetImageMetadataMock,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
} as unknown as QueryContext
|
|
20
|
+
|
|
21
|
+
let flourishSource: FlourishSource
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
const MOCKED_CURRENT_TIME = 1715158800000 // 8 May 2024 09:00:00
|
|
25
|
+
jest.spyOn(Date, 'now').mockImplementation(() => MOCKED_CURRENT_TIME)
|
|
26
|
+
|
|
27
|
+
flourishSource = new FlourishSource(mockFlourishData, context)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
jest.resetAllMocks()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should return the correct id', () => {
|
|
35
|
+
expect(flourishSource.id()).toBe('1234')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should return the correct type', () => {
|
|
39
|
+
expect(flourishSource.type()).toBe('test-type')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should return the correct format', () => {
|
|
43
|
+
expect(flourishSource.format()).toBe('standard')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should return the correct flourishUrl', () => {
|
|
47
|
+
expect(flourishSource.flourishUrl()).toBe(
|
|
48
|
+
'https://public.flourish.studio/test-type/1234/thumbnail?cacheBuster=5717196'
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should return the correct width', async () => {
|
|
53
|
+
const width = await flourishSource.width()
|
|
54
|
+
expect(width).toBe(2626)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should return the correct height', async () => {
|
|
58
|
+
const height = await flourishSource.height()
|
|
59
|
+
expect(height).toBe(1459)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should return the correct url', async () => {
|
|
63
|
+
const url = await flourishSource.url()
|
|
64
|
+
// Regex checks for flourish url whilst leaving spaces for arguments
|
|
65
|
+
// https://public.flourish.studio/{type}/{id}/thumbnail
|
|
66
|
+
const flourishImageUrlRegex = new RegExp(
|
|
67
|
+
/https:\/\/public.flourish.studio\/.*\/.*\/thumbnail/
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
expect(decodeURIComponent(url)).toMatch(flourishImageUrlRegex)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should bust the cache of the fallback image every five minutes', async () => {
|
|
74
|
+
const oldCacheBuster = '5717196'
|
|
75
|
+
const newCacheBuster = '5717197'
|
|
76
|
+
|
|
77
|
+
expect(flourishSource.flourishUrl()).toBe(
|
|
78
|
+
`https://public.flourish.studio/test-type/1234/thumbnail?cacheBuster=${oldCacheBuster}`
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
let newFlourishSource = new FlourishSource(mockFlourishData, context)
|
|
82
|
+
jest.spyOn(Date, 'now').mockImplementation(() => 1715159040000) // 8 May 2024 09:04:00
|
|
83
|
+
expect(newFlourishSource.flourishUrl()).toBe(
|
|
84
|
+
`https://public.flourish.studio/test-type/1234/thumbnail?cacheBuster=${oldCacheBuster}`
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
newFlourishSource = new FlourishSource(mockFlourishData, context)
|
|
88
|
+
jest.spyOn(Date, 'now').mockImplementation(() => 1715159100000) // 8 May 2024 09:05:00
|
|
89
|
+
expect(newFlourishSource.flourishUrl()).toBe(
|
|
90
|
+
`https://public.flourish.studio/test-type/1234/thumbnail?cacheBuster=${newCacheBuster}`
|
|
91
|
+
)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import imageServiceUrl from '../helpers/imageService'
|
|
2
|
+
import isError from '../helpers/isError'
|
|
3
|
+
import { OperationalError } from '@dotcom-reliability-kit/errors'
|
|
4
|
+
import type { QueryContext } from '..'
|
|
5
|
+
|
|
6
|
+
type ImageMetadata = {
|
|
7
|
+
width: number
|
|
8
|
+
height: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type FlourishData =
|
|
12
|
+
| {
|
|
13
|
+
id: string
|
|
14
|
+
type?: string
|
|
15
|
+
description?: string
|
|
16
|
+
}
|
|
17
|
+
| null
|
|
18
|
+
| undefined
|
|
19
|
+
|
|
20
|
+
export class FlourishSource {
|
|
21
|
+
#systemCode: string
|
|
22
|
+
private imageMetadata?: ImageMetadata
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
private flourishData: FlourishData,
|
|
26
|
+
private context: QueryContext
|
|
27
|
+
) {
|
|
28
|
+
this.#systemCode = context.systemCode ?? 'cp-content-pipeline'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
id() {
|
|
32
|
+
return this.flourishData?.id || ''
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type() {
|
|
36
|
+
return this.flourishData?.type || 'flourish'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
format() {
|
|
40
|
+
return 'standard'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
flourishUrl() {
|
|
44
|
+
// Timestamp rounded to the nearest 5 minutes to bust the cache every five minutes
|
|
45
|
+
const cacheBusterTimestamp = Math.floor(Date.now() / (5 * 60 * 1000))
|
|
46
|
+
|
|
47
|
+
return `https://public.flourish.studio/${this.type()}/${this.id()}/thumbnail?cacheBuster=${cacheBusterTimestamp}`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private async getImageMetadata(): Promise<ImageMetadata> {
|
|
51
|
+
const DEFAULT_WIDTH = 2626
|
|
52
|
+
const DEFAULT_HEIGHT = 1459
|
|
53
|
+
|
|
54
|
+
if (this.imageMetadata) {
|
|
55
|
+
return this.imageMetadata
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const metadata = await this.context.dataSources.origami.getImageMetadata(
|
|
60
|
+
this.flourishUrl()
|
|
61
|
+
)
|
|
62
|
+
this.imageMetadata = {
|
|
63
|
+
width: metadata?.width || DEFAULT_WIDTH,
|
|
64
|
+
height: metadata?.height || DEFAULT_HEIGHT,
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (isError(error)) {
|
|
68
|
+
this.context.logger.warn({
|
|
69
|
+
event: 'RECOVERABLE_ERROR',
|
|
70
|
+
error: new OperationalError({
|
|
71
|
+
code: 'FLOURISH_IMAGE_METADATA_ERROR',
|
|
72
|
+
message: `Error getting image dimensions for Flourish fallback image ${this.flourishUrl()}`,
|
|
73
|
+
cause: error,
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
this.imageMetadata = {
|
|
78
|
+
width: DEFAULT_WIDTH,
|
|
79
|
+
height: DEFAULT_HEIGHT,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return this.imageMetadata
|
|
83
|
+
}
|
|
84
|
+
async width() {
|
|
85
|
+
const imageMetadata = await this.getImageMetadata()
|
|
86
|
+
return imageMetadata.width
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async height() {
|
|
90
|
+
const imageMetadata = await this.getImageMetadata()
|
|
91
|
+
return imageMetadata.height
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async url() {
|
|
95
|
+
const width = await this.width()
|
|
96
|
+
const imageServiceWrappedUrl = imageServiceUrl({
|
|
97
|
+
url: this.flourishUrl(),
|
|
98
|
+
systemCode: 'cp-content-pipeline',
|
|
99
|
+
width,
|
|
100
|
+
})
|
|
101
|
+
return imageServiceWrappedUrl
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { LeadFlourish } from './LeadFlourish'
|
|
2
|
+
import { FlourishSource } from './FlourishSource'
|
|
3
|
+
import { CapiResponse } from './CapiResponse'
|
|
4
|
+
import { baseCapiObject } from '../fixtures/capiObject'
|
|
5
|
+
import type { QueryContext } from '..'
|
|
6
|
+
import cloneDeep from 'clone-deep'
|
|
7
|
+
|
|
8
|
+
const context = {} as unknown as QueryContext
|
|
9
|
+
|
|
10
|
+
jest.mock('./FlourishSource', () => {
|
|
11
|
+
return {
|
|
12
|
+
FlourishSource: jest.fn().mockImplementation(() => ({})),
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const leadFlourishData = {
|
|
17
|
+
id: 'test-id',
|
|
18
|
+
description: 'test-description',
|
|
19
|
+
type: 'test-type',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('LeadFlourish', () => {
|
|
23
|
+
let leadFlourish: LeadFlourish
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
const clonedBase = cloneDeep(baseCapiObject)
|
|
27
|
+
clonedBase.leadFlourish = leadFlourishData
|
|
28
|
+
clonedBase.topper = {
|
|
29
|
+
layout: 'flourish',
|
|
30
|
+
layoutWidth: 'full-width',
|
|
31
|
+
}
|
|
32
|
+
const capiResponse = new CapiResponse(clonedBase, context)
|
|
33
|
+
leadFlourish = new LeadFlourish(capiResponse, context)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe('fallbackImage', () => {
|
|
37
|
+
it('should instantiate a new FlourishSource', async () => {
|
|
38
|
+
await leadFlourish.fallbackImage()
|
|
39
|
+
|
|
40
|
+
expect(FlourishSource).toHaveBeenCalledWith(leadFlourishData, context)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe('id', () => {
|
|
45
|
+
it('should return the id', () => {
|
|
46
|
+
const result = leadFlourish.id()
|
|
47
|
+
|
|
48
|
+
expect(result).toBe('test-id')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('type', () => {
|
|
53
|
+
it('should return the type', () => {
|
|
54
|
+
const result = leadFlourish.type()
|
|
55
|
+
|
|
56
|
+
expect(result).toBe('test-type')
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('description', () => {
|
|
61
|
+
it('should return the description', () => {
|
|
62
|
+
const descriptionMock = jest.fn().mockReturnValue('flourish-description')
|
|
63
|
+
leadFlourish.description = descriptionMock
|
|
64
|
+
|
|
65
|
+
const result = leadFlourish.description()
|
|
66
|
+
|
|
67
|
+
expect(result).toBe('flourish-description')
|
|
68
|
+
expect(descriptionMock).toHaveBeenCalled()
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { CapiResponse } from './CapiResponse'
|
|
2
|
+
import type { QueryContext } from '..'
|
|
3
|
+
import { FlourishSource } from './FlourishSource'
|
|
4
|
+
|
|
5
|
+
export class LeadFlourish {
|
|
6
|
+
#systemCode: string
|
|
7
|
+
constructor(
|
|
8
|
+
private capiResponse: CapiResponse,
|
|
9
|
+
private context: QueryContext
|
|
10
|
+
) {
|
|
11
|
+
this.#systemCode = context.systemCode ?? 'cp-content-pipeline'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async fallbackImage() {
|
|
15
|
+
const flourishData = this.capiResponse.leadFlourish()
|
|
16
|
+
return new FlourishSource(flourishData, this.context)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
id() {
|
|
20
|
+
return this.capiResponse.leadFlourish()?.id || null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type() {
|
|
24
|
+
return this.capiResponse.leadFlourish()?.type || 'visualisation'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
description() {
|
|
28
|
+
return this.capiResponse.leadFlourish()?.description || ''
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/model/Topper.test.ts
CHANGED
|
@@ -151,6 +151,32 @@ describe('produces the correct types', () => {
|
|
|
151
151
|
expect(desiredColour).toEqual(actualColour)
|
|
152
152
|
})
|
|
153
153
|
|
|
154
|
+
it('flourish topper', () => {
|
|
155
|
+
const clonedBase = cloneDeep(baseCapiObject)
|
|
156
|
+
clonedBase.leadFlourish = {
|
|
157
|
+
id: 'test-id',
|
|
158
|
+
description: 'test-description',
|
|
159
|
+
type: 'test-type'
|
|
160
|
+
}
|
|
161
|
+
clonedBase.topper = {
|
|
162
|
+
layout: 'flourish',
|
|
163
|
+
layoutWidth: 'full-width'
|
|
164
|
+
}
|
|
165
|
+
const capiResponse = new CapiResponse(clonedBase, context)
|
|
166
|
+
const topper = new Topper(capiResponse, context)
|
|
167
|
+
|
|
168
|
+
const desired = 'TopperWithFlourish'
|
|
169
|
+
const actual = topper.type()
|
|
170
|
+
|
|
171
|
+
expect(actual).toEqual(desired)
|
|
172
|
+
|
|
173
|
+
const LeadFlourish = topper.leadFlourish()
|
|
174
|
+
|
|
175
|
+
expect(LeadFlourish.id()).toEqual('test-id')
|
|
176
|
+
expect(LeadFlourish.description()).toEqual('test-description')
|
|
177
|
+
expect(LeadFlourish.type()).toEqual('test-type')
|
|
178
|
+
})
|
|
179
|
+
|
|
154
180
|
it('live blog topper, is full bleed, large headline, paper background', () => {
|
|
155
181
|
const clonedBase = cloneDeep(baseCapiObject)
|
|
156
182
|
clonedBase.type = 'http://www.ft.com/ontology/content/LiveBlogPackage'
|
package/src/model/Topper.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { TopperBackgroundColour } from '../resolvers/scalars'
|
|
9
9
|
import { RichText } from './RichText'
|
|
10
10
|
import imageServiceUrl from '../helpers/imageService'
|
|
11
|
+
import { LeadFlourish } from './LeadFlourish'
|
|
11
12
|
import type { TopperWithHeadshotHeadshotArgs } from '../generated'
|
|
12
13
|
|
|
13
14
|
type TopperType =
|
|
@@ -19,6 +20,7 @@ type TopperType =
|
|
|
19
20
|
| 'OpinionTopper'
|
|
20
21
|
| 'BrandedTopper'
|
|
21
22
|
| 'BasicTopper'
|
|
23
|
+
| 'TopperWithFlourish'
|
|
22
24
|
|
|
23
25
|
type TopperBackgroundColourValues = LiteralUnionScalarValues<
|
|
24
26
|
typeof TopperBackgroundColour
|
|
@@ -77,6 +79,10 @@ export class Topper {
|
|
|
77
79
|
return 'SplitTextTopper'
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
if (this.capiResponse.topper()?.layout === 'flourish') {
|
|
83
|
+
return 'TopperWithFlourish'
|
|
84
|
+
}
|
|
85
|
+
|
|
80
86
|
if (
|
|
81
87
|
this.capiResponse.topper()?.layout?.startsWith('full-bleed') ||
|
|
82
88
|
this.capiResponse.type() === 'LiveBlogPackage'
|
|
@@ -138,6 +144,18 @@ export class Topper {
|
|
|
138
144
|
return this.capiResponse.topper()?.layout || 'branded'
|
|
139
145
|
}
|
|
140
146
|
|
|
147
|
+
layoutWidth() {
|
|
148
|
+
if (this.capiResponse.leadFlourish()) {
|
|
149
|
+
return this.capiResponse.topper()?.layoutWidth || 'full-grid'
|
|
150
|
+
} else {
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
leadFlourish() {
|
|
156
|
+
return new LeadFlourish(this.capiResponse, this.context)
|
|
157
|
+
}
|
|
158
|
+
|
|
141
159
|
backgroundColour(): TopperBackgroundColourValues {
|
|
142
160
|
if (
|
|
143
161
|
this.capiResponse.type() === 'ContentPackage' &&
|
|
@@ -21,6 +21,7 @@ const Topper = z.object({
|
|
|
21
21
|
backgroundBox: z.boolean().optional(),
|
|
22
22
|
textShadow: z.boolean().optional(),
|
|
23
23
|
layout: z.string(),
|
|
24
|
+
layoutWidth: z.string().optional(),
|
|
24
25
|
})
|
|
25
26
|
|
|
26
27
|
const BaseImage = z.object({
|
|
@@ -88,6 +89,12 @@ const AlternativeImage = z.object({
|
|
|
88
89
|
promotionalImage: BaseImage,
|
|
89
90
|
})
|
|
90
91
|
|
|
92
|
+
export const LeadFlourish = z.object({
|
|
93
|
+
id: z.string(),
|
|
94
|
+
type: z.string(),
|
|
95
|
+
description: z.string(),
|
|
96
|
+
})
|
|
97
|
+
|
|
91
98
|
const ClipSource = z.object({
|
|
92
99
|
audioCodec: z.string().optional(),
|
|
93
100
|
binaryUrl: z.string().optional(),
|
|
@@ -261,6 +268,7 @@ export const baseMediaSchema = z.object({
|
|
|
261
268
|
mainImage: MainImage.optional(),
|
|
262
269
|
leadImages: LeadImage.array().optional(),
|
|
263
270
|
alternativeImages: AlternativeImage.optional(),
|
|
271
|
+
leadFlourish: LeadFlourish.optional(),
|
|
264
272
|
embeds: z.array(z.union([ImageSet, ClipSet])).optional(),
|
|
265
273
|
dataSource: DataSource.array().optional(),
|
|
266
274
|
})
|
|
@@ -10,7 +10,6 @@ const contentPackageContentSchema = baseContentSchema
|
|
|
10
10
|
summary: true,
|
|
11
11
|
alternativeTitles: true,
|
|
12
12
|
contains: true,
|
|
13
|
-
tableOfContents: true,
|
|
14
13
|
design: true,
|
|
15
14
|
bodyXML: true,
|
|
16
15
|
})
|
|
@@ -44,6 +43,7 @@ const contentPackageMetadataSchema = baseMetadataSchema.pick({
|
|
|
44
43
|
const contentPackageMediaSchema = baseMediaSchema.pick({
|
|
45
44
|
mainImage: true,
|
|
46
45
|
leadImages: true,
|
|
46
|
+
leadFlourish: true,
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
export const contentPackageSchema = contentPackageContentSchema
|