@financial-times/cp-content-pipeline-schema 0.5.0 → 0.6.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.
Files changed (190) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +45 -11
  3. package/lib/content-tree/bodyXMLToTree.d.ts +6 -0
  4. package/lib/content-tree/bodyXMLToTree.js +57 -0
  5. package/lib/content-tree/bodyXMLToTree.js.map +1 -0
  6. package/lib/{tags/Tweet.test.d.ts → content-tree/bodyXMLToTree.test.d.ts} +0 -0
  7. package/lib/content-tree/bodyXMLToTree.test.js +73 -0
  8. package/lib/content-tree/bodyXMLToTree.test.js.map +1 -0
  9. package/lib/content-tree/extractReferencesFromTree.d.ts +7 -0
  10. package/lib/content-tree/extractReferencesFromTree.js +31 -0
  11. package/lib/content-tree/extractReferencesFromTree.js.map +1 -0
  12. package/lib/content-tree/references/Flourish.d.ts +15 -0
  13. package/lib/content-tree/references/Flourish.js +45 -0
  14. package/lib/content-tree/references/Flourish.js.map +1 -0
  15. package/lib/{tags/model → content-tree/references}/Flourish.test.d.ts +0 -0
  16. package/lib/content-tree/references/Flourish.test.js +93 -0
  17. package/lib/content-tree/references/Flourish.test.js.map +1 -0
  18. package/lib/content-tree/references/ImageSet.d.ts +3 -0
  19. package/lib/content-tree/references/ImageSet.js +29 -0
  20. package/lib/content-tree/references/ImageSet.js.map +1 -0
  21. package/lib/content-tree/references/LayoutImage.d.ts +3 -0
  22. package/lib/content-tree/references/LayoutImage.js +37 -0
  23. package/lib/content-tree/references/LayoutImage.js.map +1 -0
  24. package/lib/content-tree/references/RawImage.d.ts +4 -0
  25. package/lib/content-tree/references/RawImage.js +89 -0
  26. package/lib/content-tree/references/RawImage.js.map +1 -0
  27. package/lib/content-tree/references/Recommended.d.ts +3 -0
  28. package/lib/content-tree/references/Recommended.js +25 -0
  29. package/lib/content-tree/references/Recommended.js.map +1 -0
  30. package/lib/content-tree/references/Reference.d.ts +4 -0
  31. package/lib/content-tree/references/Reference.js +22 -0
  32. package/lib/content-tree/references/Reference.js.map +1 -0
  33. package/lib/content-tree/references/Tweet.d.ts +3 -0
  34. package/lib/content-tree/references/Tweet.js +36 -0
  35. package/lib/content-tree/references/Tweet.js.map +1 -0
  36. package/lib/content-tree/references/index.d.ts +25 -0
  37. package/lib/content-tree/references/index.js +22 -0
  38. package/lib/content-tree/references/index.js.map +1 -0
  39. package/lib/content-tree/tagMappings.d.ts +3 -0
  40. package/lib/content-tree/tagMappings.js +110 -0
  41. package/lib/content-tree/tagMappings.js.map +1 -0
  42. package/lib/content.js +4 -0
  43. package/lib/content.js.map +1 -1
  44. package/lib/datasources/capi.d.ts +2 -0
  45. package/lib/datasources/capi.js +2 -3
  46. package/lib/datasources/capi.js.map +1 -1
  47. package/lib/datasources/instrumented.d.ts +1 -0
  48. package/lib/datasources/instrumented.js +3 -5
  49. package/lib/datasources/instrumented.js.map +1 -1
  50. package/lib/datasources/origami-image.d.ts +2 -0
  51. package/lib/datasources/twitter.d.ts +2 -0
  52. package/lib/helpers/kebabCaseToPascalCase.d.ts +1 -0
  53. package/lib/helpers/kebabCaseToPascalCase.js +4 -0
  54. package/lib/helpers/kebabCaseToPascalCase.js.map +1 -0
  55. package/lib/image.js +81 -27
  56. package/lib/image.js.map +1 -1
  57. package/lib/index.js +3 -9
  58. package/lib/index.js.map +1 -1
  59. package/lib/model/Byline.d.ts +3 -18
  60. package/lib/model/Byline.js +2 -3
  61. package/lib/model/Byline.js.map +1 -1
  62. package/lib/model/CapiResponse.d.ts +83 -2
  63. package/lib/model/CapiResponse.js +12 -15
  64. package/lib/model/CapiResponse.js.map +1 -1
  65. package/lib/model/Concept.js +4 -4
  66. package/lib/model/Concept.js.map +1 -1
  67. package/lib/model/Image.d.ts +30 -14
  68. package/lib/model/Image.js +32 -33
  69. package/lib/model/Image.js.map +1 -1
  70. package/lib/model/Image.test.js +73 -82
  71. package/lib/model/Image.test.js.map +1 -1
  72. package/lib/model/Picture.d.ts +5 -13
  73. package/lib/model/Picture.js +16 -46
  74. package/lib/model/Picture.js.map +1 -1
  75. package/lib/model/Picture.test.d.ts +1 -0
  76. package/lib/model/Picture.test.js +72 -0
  77. package/lib/model/Picture.test.js.map +1 -0
  78. package/lib/model/Topper.d.ts +2 -3
  79. package/lib/model/Topper.js +14 -19
  80. package/lib/model/Topper.js.map +1 -1
  81. package/lib/model/Topper.test.js +42 -10
  82. package/lib/model/Topper.test.js.map +1 -1
  83. package/lib/model/schemas/capi/article.d.ts +40 -1
  84. package/lib/model/schemas/capi/article.js +1 -0
  85. package/lib/model/schemas/capi/article.js.map +1 -1
  86. package/lib/model/schemas/capi/base-schema.d.ts +60 -0
  87. package/lib/model/schemas/capi/base-schema.js +13 -0
  88. package/lib/model/schemas/capi/base-schema.js.map +1 -1
  89. package/lib/model/schemas/capi/index.d.ts +80 -2
  90. package/lib/model/schemas/capi/live-blog-package.d.ts +40 -1
  91. package/lib/model/schemas/capi/live-blog-package.js +1 -0
  92. package/lib/model/schemas/capi/live-blog-package.js.map +1 -1
  93. package/lib/picture.d.ts +8 -9
  94. package/lib/picture.js +25 -13
  95. package/lib/picture.js.map +1 -1
  96. package/lib/richText.d.ts +4 -1
  97. package/lib/richText.js +8 -30
  98. package/lib/richText.js.map +1 -1
  99. package/lib/richText.test.js +4 -4
  100. package/lib/richText.test.js.map +1 -1
  101. package/lib/topper.js +4 -10
  102. package/lib/topper.js.map +1 -1
  103. package/package.json +7 -7
  104. package/src/__snapshots__/richText.test.ts.snap +405 -2901
  105. package/src/content-tree/Workarounds.d.ts +54 -0
  106. package/src/content-tree/bodyXMLToTree.test.ts +80 -0
  107. package/src/content-tree/bodyXMLToTree.ts +78 -0
  108. package/src/content-tree/extractReferencesFromTree.ts +43 -0
  109. package/src/content-tree/references/Flourish.test.ts +145 -0
  110. package/src/content-tree/references/Flourish.ts +61 -0
  111. package/src/content-tree/references/ImageSet.ts +41 -0
  112. package/src/content-tree/references/LayoutImage.ts +48 -0
  113. package/src/content-tree/references/RawImage.ts +111 -0
  114. package/src/content-tree/references/Recommended.ts +35 -0
  115. package/src/content-tree/references/Reference.ts +25 -0
  116. package/src/content-tree/references/Tweet.ts +44 -0
  117. package/src/content-tree/references/index.ts +47 -0
  118. package/src/content-tree/tagMappings.ts +118 -0
  119. package/src/content.ts +4 -0
  120. package/src/helpers/kebabCaseToPascalCase.ts +5 -0
  121. package/src/image.ts +96 -30
  122. package/src/index.ts +6 -13
  123. package/src/model/Byline.ts +6 -4
  124. package/src/model/CapiResponse.ts +5 -1
  125. package/src/model/Image.test.ts +100 -88
  126. package/src/model/Image.ts +50 -42
  127. package/src/model/Picture.test.ts +82 -0
  128. package/src/model/Picture.ts +27 -50
  129. package/src/model/Topper.test.ts +41 -3
  130. package/src/model/Topper.ts +11 -14
  131. package/src/model/__snapshots__/Byline.test.ts.snap +10 -25
  132. package/src/model/schemas/capi/article.ts +1 -0
  133. package/src/model/schemas/capi/base-schema.ts +13 -0
  134. package/src/model/schemas/capi/live-blog-package.ts +1 -0
  135. package/src/picture.ts +25 -13
  136. package/src/richText.test.ts +7 -5
  137. package/src/richText.ts +10 -36
  138. package/src/topper.ts +3 -8
  139. package/src/types/internal-content.d.ts +2 -0
  140. package/tsconfig.tsbuildinfo +1 -1
  141. package/lib/tags/Flourish.d.ts +0 -24
  142. package/lib/tags/Flourish.js +0 -37
  143. package/lib/tags/Flourish.js.map +0 -1
  144. package/lib/tags/ImageSet.d.ts +0 -3
  145. package/lib/tags/ImageSet.js +0 -40
  146. package/lib/tags/ImageSet.js.map +0 -1
  147. package/lib/tags/LayoutImage.d.ts +0 -2
  148. package/lib/tags/LayoutImage.js +0 -43
  149. package/lib/tags/LayoutImage.js.map +0 -1
  150. package/lib/tags/Link.d.ts +0 -2
  151. package/lib/tags/Link.js +0 -21
  152. package/lib/tags/Link.js.map +0 -1
  153. package/lib/tags/PullQuote.d.ts +0 -2
  154. package/lib/tags/PullQuote.js +0 -25
  155. package/lib/tags/PullQuote.js.map +0 -1
  156. package/lib/tags/Recommended.d.ts +0 -2
  157. package/lib/tags/Recommended.js +0 -39
  158. package/lib/tags/Recommended.js.map +0 -1
  159. package/lib/tags/Tweet.d.ts +0 -17
  160. package/lib/tags/Tweet.js +0 -50
  161. package/lib/tags/Tweet.js.map +0 -1
  162. package/lib/tags/Tweet.test.js +0 -71
  163. package/lib/tags/Tweet.test.js.map +0 -1
  164. package/lib/tags/index.d.ts +0 -17
  165. package/lib/tags/index.js +0 -52
  166. package/lib/tags/index.js.map +0 -1
  167. package/lib/tags/model/Flourish.d.ts +0 -16
  168. package/lib/tags/model/Flourish.js +0 -54
  169. package/lib/tags/model/Flourish.js.map +0 -1
  170. package/lib/tags/model/Flourish.test.js +0 -208
  171. package/lib/tags/model/Flourish.test.js.map +0 -1
  172. package/lib/unified-plugins/extract-references.d.ts +0 -7
  173. package/lib/unified-plugins/extract-references.js +0 -36
  174. package/lib/unified-plugins/extract-references.js.map +0 -1
  175. package/lib/unified-plugins/map-to-abstract-types.d.ts +0 -4
  176. package/lib/unified-plugins/map-to-abstract-types.js +0 -18
  177. package/lib/unified-plugins/map-to-abstract-types.js.map +0 -1
  178. package/src/tags/Flourish.ts +0 -51
  179. package/src/tags/ImageSet.ts +0 -55
  180. package/src/tags/LayoutImage.ts +0 -56
  181. package/src/tags/Link.ts +0 -23
  182. package/src/tags/PullQuote.ts +0 -32
  183. package/src/tags/Recommended.ts +0 -49
  184. package/src/tags/Tweet.test.ts +0 -82
  185. package/src/tags/Tweet.ts +0 -59
  186. package/src/tags/index.ts +0 -90
  187. package/src/tags/model/Flourish.test.ts +0 -257
  188. package/src/tags/model/Flourish.ts +0 -61
  189. package/src/unified-plugins/extract-references.ts +0 -51
  190. package/src/unified-plugins/map-to-abstract-types.ts +0 -24
package/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v0.5.0...cp-content-pipeline-schema-v0.6.0) (2023-01-24)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * update to latest content-tree ImageSet changes
9
+ * update references to use External type
10
+ * use contenttree for structured tree
11
+ * remove now-unused min/maxDisplayWidth fields
12
+ * return an array for picture images
13
+ * rename sources on Image to sourceSet
14
+
15
+ ### Features
16
+
17
+ * add Flourish to content-tree ([7816a6f](https://github.com/Financial-Times/cp-content-pipeline/commit/7816a6f5b4d9f599ebd4afd9284de5b18979f278))
18
+ * add imageset to references ([28157c0](https://github.com/Financial-Times/cp-content-pipeline/commit/28157c0e170a4d954ff8ab97decd89221b1a0545))
19
+ * add layout nodes to content tree ([b6f5327](https://github.com/Financial-Times/cp-content-pipeline/commit/b6f5327422521437ae18f88f6100e1fcfa1f8cb4))
20
+ * add recommended link to content tree ([082450a](https://github.com/Financial-Times/cp-content-pipeline/commit/082450acf36e3ce2bd51893e3ebfd8c62fe20f0b))
21
+ * add some more tags ([0bb0b6f](https://github.com/Financial-Times/cp-content-pipeline/commit/0bb0b6fd2dae1d9ffaa2d19028a49231c8a89217))
22
+ * add tweet reference to content tree ([d0d0550](https://github.com/Financial-Times/cp-content-pipeline/commit/d0d05504ad0cc105507d6c8f9145dee23afc8f28))
23
+ * reinstate fallback image in picture schema ([e61d375](https://github.com/Financial-Times/cp-content-pipeline/commit/e61d37569aee82f6d2ac1bfac17f3192f02334d4))
24
+ * rename sources on Image to sourceSet ([c6ddf7f](https://github.com/Financial-Times/cp-content-pipeline/commit/c6ddf7f0cac47b0cbf4060e91c62a1c398665058))
25
+ * return an array for picture images ([e555bf8](https://github.com/Financial-Times/cp-content-pipeline/commit/e555bf8e33e4c2f23957aa2ed463395bae21e8a6))
26
+ * return an image for raw images ([d05a6cd](https://github.com/Financial-Times/cp-content-pipeline/commit/d05a6cd93defbf222b968943f7365def841b0e64))
27
+ * return array for topper images ([826e6cf](https://github.com/Financial-Times/cp-content-pipeline/commit/826e6cfc47b2782e5a7a7dfe4a12f81d21d08fcb))
28
+ * return image format ([3f0ea41](https://github.com/Financial-Times/cp-content-pipeline/commit/3f0ea410f2b19114f81ebcf42c9add5c32b0f453))
29
+ * split image into interfaces for distinguishing formats ([0078f09](https://github.com/Financial-Times/cp-content-pipeline/commit/0078f09e6a343112e1a1494a38272f8cf430ecc9))
30
+ * support mainImage formats (which are the topper formats it turns out) ([c15c63f](https://github.com/Financial-Times/cp-content-pipeline/commit/c15c63ff6aead55f9502651dd184135f834f677a))
31
+ * use contenttree for structured tree ([0bb0b6f](https://github.com/Financial-Times/cp-content-pipeline/commit/0bb0b6fd2dae1d9ffaa2d19028a49231c8a89217))
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * broken tests and image format bug ([82909a8](https://github.com/Financial-Times/cp-content-pipeline/commit/82909a88cca976eb2bf1c8114ca4c93eb0d9da8d))
37
+ * get imageset working with latest image changes ([e1eceb0](https://github.com/Financial-Times/cp-content-pipeline/commit/e1eceb0a5b8018d91aadd1699240425a29505e58))
38
+ * handle content-tree nodes that have no children ([1ffdf17](https://github.com/Financial-Times/cp-content-pipeline/commit/1ffdf1729b3fbb3472127a6d62e52a89cecc2df8))
39
+ * handle empty rich text value ([2fb542f](https://github.com/Financial-Times/cp-content-pipeline/commit/2fb542f28e78373e00d6136f4597c9cef27d4a57))
40
+ * include children of unknown tags in tree ([1ffdf17](https://github.com/Financial-Times/cp-content-pipeline/commit/1ffdf1729b3fbb3472127a6d62e52a89cecc2df8))
41
+ * loosen image format restrictions to handle edge cases ([27ada9b](https://github.com/Financial-Times/cp-content-pipeline/commit/27ada9b62405c9036e484e041337a3f590faa2f8))
42
+ * missing credit on images ([62a10d3](https://github.com/Financial-Times/cp-content-pipeline/commit/62a10d3146d1af49371262e247b6c0cb2140d432))
43
+ * reimplement mainImage workaround ([5e30dca](https://github.com/Financial-Times/cp-content-pipeline/commit/5e30dca04c28eee87c44d31c4d69075c14fa6c53))
44
+ * reinstate twitter error handling ([6ba321c](https://github.com/Financial-Times/cp-content-pipeline/commit/6ba321cf6a3d66f58cd1c0f3df40eaf9b8076054))
45
+ * render byline links as a custom author-link node ([9a4dfc0](https://github.com/Financial-Times/cp-content-pipeline/commit/9a4dfc02810744054d0137f8cb4e640d9cd8e7f5))
46
+ * style headings usign n-content-body styles ([f1d8b0f](https://github.com/Financial-Times/cp-content-pipeline/commit/f1d8b0f65225295d6324ce65ab2ca2b962c7a0ff))
47
+ * switch tag order to LayoutImage overrides RawImage where it matches ([ad3f596](https://github.com/Financial-Times/cp-content-pipeline/commit/ad3f596a6eb79884e0ca7197d906a9b9710ea698))
48
+ * typo and update tests after rebase ([37b54ae](https://github.com/Financial-Times/cp-content-pipeline/commit/37b54aeff28cbbc696abb4acd431ae75a5a2abda))
49
+ * update layout-width to match new names ([b6f5327](https://github.com/Financial-Times/cp-content-pipeline/commit/b6f5327422521437ae18f88f6100e1fcfa1f8cb4))
50
+ * update layoutWidth definitions ([b6fbe8e](https://github.com/Financial-Times/cp-content-pipeline/commit/b6fbe8ef9cf471725dd8c2a73d8947559c7146f8))
51
+
52
+
53
+ ### Miscellaneous Chores
54
+
55
+ * remove now-unused min/maxDisplayWidth fields ([e892d2f](https://github.com/Financial-Times/cp-content-pipeline/commit/e892d2f7b8a8b10f63a720f3dca974cd29678e70))
56
+
57
+
58
+ ### Code Refactoring
59
+
60
+ * update references to use External type ([6ba321c](https://github.com/Financial-Times/cp-content-pipeline/commit/6ba321cf6a3d66f58cd1c0f3df40eaf9b8076054))
61
+ * update to latest content-tree ImageSet changes ([6d33d38](https://github.com/Financial-Times/cp-content-pipeline/commit/6d33d383586841e83e08685ce93ca87964825297))
62
+
3
63
  ## [0.5.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-schema-v0.4.7...cp-content-pipeline-schema-v0.5.0) (2022-12-14)
4
64
 
5
65
 
package/README.md CHANGED
@@ -10,15 +10,15 @@ The GraphQL schema, data sources, and business logic for the content pipeline AP
10
10
  ```typescript
11
11
  import { ApolloServer } from 'apollo-server'
12
12
  import {
13
- resolvers,
14
- typeDefs,
15
- dataSources,
13
+ resolvers,
14
+ typeDefs,
15
+ dataSources,
16
16
  } from '@financial-times/cp-content-pipeline-schema'
17
17
 
18
18
  const server = new ApolloServer({
19
- typeDefs,
20
- resolvers,
21
- dataSources,
19
+ typeDefs,
20
+ resolvers,
21
+ dataSources,
22
22
  })
23
23
  ```
24
24
 
@@ -30,11 +30,11 @@ This package's exports are shown below. They are consumed by a GraphQL server fr
30
30
 
31
31
  ```typescript
32
32
  import {
33
- resolvers,
34
- typeDefs,
35
- dataSources,
36
- DataSources,
37
- QueryContext,
33
+ resolvers,
34
+ typeDefs,
35
+ dataSources,
36
+ DataSources,
37
+ QueryContext,
38
38
  } from '@financial-times/cp-content-pipeline-schema'
39
39
  ```
40
40
 
@@ -55,3 +55,37 @@ The `QueryContext` type defines the context object sent to each resolver functio
55
55
 
56
56
  The model contains the business logic gluing the data sources and resolvers together. It is defined in the
57
57
  [model](src/model) directory and consumed by the resolvers.
58
+
59
+ ## Content Tree
60
+
61
+ ### What is it?
62
+
63
+ [content-tree](https://github.com/Financial-Times/content-tree/) is a specification for representing Financial Times article content as an abstract tree. It implements the unist spec.
64
+
65
+ It is shared across Spark, Content & Metadata and Customer Products.
66
+
67
+ ### What is it used for?
68
+
69
+ As of January 2023, the `content-tree` spec defines everything that can appear in the body of an article. In the future, this may extend to other fields (e.g. Topper)
70
+
71
+ ### How do we create the tree?
72
+
73
+ As of January 2023, the `bodyTree` property is not yet being published to the Content API, so we must convert the `bodyXML` field to a valid content tree.
74
+
75
+ This is done by the [bodyXMLtoTree](src/content-tree/bodyXMLToTree.ts) function. This function uses [cheerio](https://cheerio.js.org/) to parse the XML, and then traverses through the nodes, converting each one to a `content-tree` node.
76
+
77
+ In order to know how to convert the XML nodes to a `content-tree` one, we provide this function some [tagMappings](src/content-tree/tagMappings.ts).
78
+
79
+ ### What are references?
80
+
81
+ For some `content-tree` nodes, the CMS will not be able to provide all of the information required to render. For example, to render a tweet, we need to fetch the embed code from the Twitter API.
82
+
83
+ In the `content-tree` spec, this additional information will be marked as optional, as it may not be there when the tree is produced.
84
+
85
+ We provide this additional data to the tree by using an array of `references` - objects containing the extra information needed. These references can be queried using GraphQL, so we can make use of dataSources to fetch data, and other resolvers (e.g. Picture) to share transformation logic.
86
+
87
+ For nodes that require additional information, a file should be created in the [content-tree/references](src/content-tree/references/) folder, containing a GraphQL typeDef and resolver object.
88
+
89
+ ### How do I add a new "body" component?
90
+
91
+ // TODO: agree a process with Spark/CP Platforms/Storytelling and flesh this out.
@@ -0,0 +1,6 @@
1
+ /// <reference types="cheerio" />
2
+ import type { ContentTree } from '@financial-times/content-tree';
3
+ declare type ContentTreeTransform = ($el: cheerio.Cheerio) => ContentTree.Node;
4
+ export declare type TagMappings = Record<string, ContentTreeTransform>;
5
+ export default function bodyXMLToTree(xml: string, tagMappings: TagMappings): ContentTree.Body;
6
+ export {};
@@ -0,0 +1,57 @@
1
+ import cheerio from 'cheerio';
2
+ function isTagElement(el) {
3
+ return el.type === 'tag';
4
+ }
5
+ function isParent(node) {
6
+ return 'children' in node;
7
+ }
8
+ function isNode(node) {
9
+ return Boolean(node && 'type' in node);
10
+ }
11
+ /*
12
+ Function that converts an XML string to a Body node from
13
+ the Content Tree spec - https://github.com/Financial-Times/content-tree#Body
14
+ */
15
+ export default function bodyXMLToTree(xml, tagMappings) {
16
+ const $ = cheerio.load(xml);
17
+ const bodyEl = $('body')[0];
18
+ if (!isTagElement(bodyEl)) {
19
+ throw new Error('Body is invalid');
20
+ }
21
+ function traverse(element) {
22
+ let contentTreeNode;
23
+ if (element.type === 'text' && element.data) {
24
+ contentTreeNode = {
25
+ type: 'text',
26
+ value: element.data,
27
+ };
28
+ }
29
+ else if (isTagElement(element)) {
30
+ const matchedSelector = Object.keys(tagMappings).find((selector) => element.name === selector || $(element).is(selector));
31
+ if (matchedSelector) {
32
+ const contentTreeTransform = tagMappings[matchedSelector];
33
+ contentTreeNode = contentTreeTransform($(element));
34
+ }
35
+ if (element.children.length) {
36
+ const children = element.children
37
+ .map(traverse)
38
+ .flat()
39
+ .filter(isNode);
40
+ if (contentTreeNode && isParent(contentTreeNode)) {
41
+ contentTreeNode.children = children;
42
+ }
43
+ else if (!contentTreeNode) {
44
+ return children;
45
+ }
46
+ }
47
+ }
48
+ return contentTreeNode;
49
+ }
50
+ const children = bodyEl.children.map(traverse).flat().filter(isNode);
51
+ return {
52
+ type: 'body',
53
+ version: 1,
54
+ children,
55
+ };
56
+ }
57
+ //# sourceMappingURL=bodyXMLToTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bodyXMLToTree.js","sourceRoot":"","sources":["../../src/content-tree/bodyXMLToTree.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAI7B,SAAS,YAAY,CAAC,EAAmB;IACvC,OAAO,EAAE,CAAC,IAAI,KAAK,KAAK,CAAA;AAC1B,CAAC;AAED,SAAS,QAAQ,CAAC,IAAsB;IACtC,OAAO,UAAU,IAAI,IAAI,CAAA;AAC3B,CAAC;AAED,SAAS,MAAM,CAAC,IAAkC;IAChD,OAAO,OAAO,CAAC,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,CAAA;AACxC,CAAC;AAMD;;;EAGE;AACF,MAAM,CAAC,OAAO,UAAU,aAAa,CACnC,GAAW,EACX,WAAwB;IAExB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAE3B,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAE3B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;KACnC;IAED,SAAS,QAAQ,CAAC,OAAwB;QACxC,IAAI,eAAe,CAAA;QAEnB,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,EAAE;YAC3C,eAAe,GAAG;gBAChB,IAAI,EAAE,MAAM;gBACZ,KAAK,EAAE,OAAO,CAAC,IAAI;aACpB,CAAA;SACF;aAAM,IAAI,YAAY,CAAC,OAAO,CAAC,EAAE;YAChC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CACnD,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CACnE,CAAA;YAED,IAAI,eAAe,EAAE;gBACnB,MAAM,oBAAoB,GAAG,WAAW,CAAC,eAAe,CAAC,CAAA;gBACzD,eAAe,GAAG,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;aACnD;YAED,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE;gBAC3B,MAAM,QAAQ,GAAuB,OAAO,CAAC,QAAQ;qBAClD,GAAG,CAAC,QAAQ,CAAC;qBACb,IAAI,EAAE;qBACN,MAAM,CAAC,MAAM,CAAC,CAAA;gBACjB,IAAI,eAAe,IAAI,QAAQ,CAAC,eAAe,CAAC,EAAE;oBAChD,eAAe,CAAC,QAAQ,GAAG,QAAQ,CAAA;iBACpC;qBAAM,IAAI,CAAC,eAAe,EAAE;oBAC3B,OAAO,QAAQ,CAAA;iBAChB;aACF;SACF;QAED,OAAO,eAAe,CAAA;IACxB,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAEpE,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,CAAC;QACV,QAAQ;KACT,CAAA;AACH,CAAC"}
@@ -0,0 +1,73 @@
1
+ import bodyXMLToTree from './bodyXMLToTree.js';
2
+ import tags from './tagMappings.js';
3
+ describe('bodyXMLToTree', () => {
4
+ it('converts XML to tree', () => {
5
+ const xml = `<body><p>Hello world</p></body>`;
6
+ const expectedTree = {
7
+ type: 'body',
8
+ version: 1,
9
+ children: [
10
+ {
11
+ type: 'paragraph',
12
+ children: [{ type: 'text', value: 'Hello world' }],
13
+ },
14
+ ],
15
+ };
16
+ expect(bodyXMLToTree(xml, tags)).toEqual(expectedTree);
17
+ });
18
+ it('supports nested selectors', () => {
19
+ const xml = `<body><img src="regular-image"/><div class="n-content-layout"><img src="layout-image" /></div></body>`;
20
+ const tags = {
21
+ 'body > img': ($el) => ({ type: 'regular-image' }),
22
+ '.n-content-layout': ($el) => ({ type: 'layout', children: [] }),
23
+ '.n-content-layout > img': ($el) => ({ type: 'layout-image' }),
24
+ };
25
+ const expectedTree = {
26
+ type: 'body',
27
+ version: 1,
28
+ children: [
29
+ {
30
+ type: 'regular-image',
31
+ },
32
+ {
33
+ type: 'layout',
34
+ children: [{ type: 'layout-image' }],
35
+ },
36
+ ],
37
+ };
38
+ expect(bodyXMLToTree(xml, tags)).toEqual(expectedTree);
39
+ });
40
+ it('includes known children of unknown tags', () => {
41
+ const xml = `<body><p>P1</p><unknown><p>P2</p></unknown></body>`;
42
+ const expectedTree = {
43
+ type: 'body',
44
+ version: 1,
45
+ children: [
46
+ {
47
+ type: 'paragraph',
48
+ children: [{ type: 'text', value: 'P1' }],
49
+ },
50
+ {
51
+ type: 'paragraph',
52
+ children: [{ type: 'text', value: 'P2' }],
53
+ },
54
+ ],
55
+ };
56
+ expect(bodyXMLToTree(xml, tags)).toEqual(expectedTree);
57
+ });
58
+ it("maps tags that aren't expected to have any children (e.g. tweet reference)", () => {
59
+ const xml = '<body><a data-asset-type="tweet" data-embedded="true" href="https://twitter.com/EllliotttB/status/1610358432577748992?s=20&amp;t=pcwJG65_c10H3snOQWzI4w">https://twitter.com/EllliotttB/status/1610358432577748992?s=20&amp;t=pcwJG65_c10H3snOQWzI4w</a></body>';
60
+ const expectedTree = {
61
+ type: 'body',
62
+ version: 1,
63
+ children: [
64
+ {
65
+ type: 'tweet',
66
+ id: 'https://twitter.com/EllliotttB/status/1610358432577748992?s=20&t=pcwJG65_c10H3snOQWzI4w',
67
+ },
68
+ ],
69
+ };
70
+ expect(bodyXMLToTree(xml, tags)).toEqual(expectedTree);
71
+ });
72
+ });
73
+ //# sourceMappingURL=bodyXMLToTree.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bodyXMLToTree.test.js","sourceRoot":"","sources":["../../src/content-tree/bodyXMLToTree.test.ts"],"names":[],"mappings":"AACA,OAAO,aAA8B,MAAM,oBAAoB,CAAA;AAC/D,OAAO,IAAI,MAAM,kBAAkB,CAAA;AACnC,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,GAAG,GAAG,iCAAiC,CAAA;QAC7C,MAAM,YAAY,GAAG;YACnB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,WAAW;oBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;iBACnD;aACF;SACF,CAAA;QACD,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,uGAAuG,CAAA;QAEnH,MAAM,IAAI,GAAgB;YACxB,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;YAClD,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAChE,yBAAyB,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;SAC/D,CAAA;QACD,MAAM,YAAY,GAAG;YACnB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,eAAe;iBACtB;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;iBACrC;aACF;SACF,CAAA;QACD,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,GAAG,GAAG,oDAAoD,CAAA;QAEhE,MAAM,YAAY,GAAG;YACnB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,WAAW;oBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;iBAC1C;gBACD;oBACE,IAAI,EAAE,WAAW;oBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;iBAC1C;aACF;SACF,CAAA;QACD,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QACpF,MAAM,GAAG,GACP,iQAAiQ,CAAA;QAEnQ,MAAM,YAAY,GAAG;YACnB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,CAAC;YACV,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,OAAO;oBACb,EAAE,EAAE,yFAAyF;iBAC9F;aACF;SACF,CAAA;QACD,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { ContentTree } from '@financial-times/content-tree';
2
+ import { CapiResponse } from '../model/CapiResponse.js';
3
+ import { ReferenceWithCAPIData } from './references/index.js';
4
+ export default function extractReferencesFromTree(tree: ContentTree.Body, contentApiData?: CapiResponse): {
5
+ tree: ContentTree.Body;
6
+ references: ReferenceWithCAPIData[];
7
+ };
@@ -0,0 +1,31 @@
1
+ import kebabCaseToPascalCase from '../helpers/kebabCaseToPascalCase.js';
2
+ import { graphQlReferences } from './references/index.js';
3
+ import cloneDeep from 'clone-deep';
4
+ const nodeTypesWithReferences = Object.keys(graphQlReferences);
5
+ function isParent(node) {
6
+ return 'children' in node;
7
+ }
8
+ export default function extractReferencesFromTree(tree, contentApiData) {
9
+ const treeWithReferences = cloneDeep(tree);
10
+ const references = [];
11
+ let index = 0;
12
+ function traverse(node) {
13
+ if (nodeTypesWithReferences.includes(kebabCaseToPascalCase(node.type))) {
14
+ references.push({
15
+ reference: node,
16
+ contentApiData,
17
+ });
18
+ node.data = node.data || {};
19
+ node.data.referenceIndex = index++;
20
+ }
21
+ if (isParent(node)) {
22
+ node.children.forEach(traverse);
23
+ }
24
+ }
25
+ traverse(treeWithReferences);
26
+ return {
27
+ tree: treeWithReferences,
28
+ references,
29
+ };
30
+ }
31
+ //# sourceMappingURL=extractReferencesFromTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractReferencesFromTree.js","sourceRoot":"","sources":["../../src/content-tree/extractReferencesFromTree.ts"],"names":[],"mappings":"AACA,OAAO,qBAAqB,MAAM,qCAAqC,CAAA;AAEvE,OAAO,EAAyB,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAChF,OAAO,SAAS,MAAM,YAAY,CAAA;AAElC,MAAM,uBAAuB,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;AAC9D,SAAS,QAAQ,CAAC,IAAsB;IACtC,OAAO,UAAU,IAAI,IAAI,CAAA;AAC3B,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAC/C,IAAsB,EACtB,cAA6B;IAK7B,MAAM,kBAAkB,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,UAAU,GAA4B,EAAE,CAAA;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAA;IAEb,SAAS,QAAQ,CAAC,IAAsB;QACtC,IAAI,uBAAuB,CAAC,QAAQ,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE;YACtE,UAAU,CAAC,IAAI,CAAC;gBACd,SAAS,EAAE,IAAI;gBACf,cAAc;aACf,CAAC,CAAA;YACF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAA;YAC3B,IAAI,CAAC,IAAI,CAAC,cAAc,GAAG,KAAK,EAAE,CAAA;SACnC;QACD,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE;YAClB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;SAChC;IACH,CAAC;IAED,QAAQ,CAAC,kBAAkB,CAAC,CAAA;IAE5B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,UAAU;KACX,CAAA;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { GraphQLReference, ReferenceWithCAPIData } from './index.js';
2
+ import { QueryContext } from '../../index.js';
3
+ import { ContentTree } from '@financial-times/content-tree';
4
+ export declare class FlourishResolver {
5
+ fallbackImage: (parent: ReferenceWithCAPIData<ContentTree.Flourish>, _args: unknown, context: QueryContext) => Promise<{
6
+ type: string;
7
+ url: string;
8
+ sourceSet: never[];
9
+ format: string;
10
+ width: number;
11
+ height: number;
12
+ }>;
13
+ }
14
+ declare const Flourish: GraphQLReference;
15
+ export default Flourish;
@@ -0,0 +1,45 @@
1
+ import { gql } from 'graphql-tag';
2
+ import imageServiceUrl from '../../helpers/imageService.js';
3
+ import { ReferenceFields } from './Reference.js';
4
+ export class FlourishResolver {
5
+ constructor() {
6
+ this.fallbackImage = async (parent, _args, context) => {
7
+ const DEFAULT_WIDTH = 2626;
8
+ const DEFAULT_HEIGHT = 1459;
9
+ const type = parent.reference.flourishType;
10
+ const timestamp = typeof parent.reference.timestamp === 'string'
11
+ ? parent.reference.timestamp
12
+ : false;
13
+ const flourishUrl = `https://public.flourish.studio/${type}/${parent.reference.id}/thumbnail${timestamp ? '?cacheBuster=' + timestamp : ''}`;
14
+ const imageMetadata = await context.dataSources.origami.getImageMetadata(flourishUrl);
15
+ const imageServiceWrappedUrl = imageServiceUrl({
16
+ url: flourishUrl,
17
+ systemCode: context.systemCode,
18
+ width: imageMetadata?.width || DEFAULT_WIDTH,
19
+ });
20
+ return {
21
+ type: 'image',
22
+ url: imageServiceWrappedUrl,
23
+ sourceSet: [],
24
+ format: 'standard',
25
+ width: imageMetadata?.width ? imageMetadata?.width : DEFAULT_WIDTH,
26
+ height: imageMetadata?.height ? imageMetadata?.height : DEFAULT_HEIGHT,
27
+ };
28
+ };
29
+ }
30
+ }
31
+ const Flourish = {
32
+ typeDef: gql `
33
+ type FlourishFallback {
34
+ url: String
35
+ width: Int
36
+ height: Int
37
+ }
38
+ type Flourish implements Reference {
39
+ ${ReferenceFields}
40
+ fallbackImage: FlourishFallback
41
+ }`,
42
+ resolve: new FlourishResolver(),
43
+ };
44
+ export default Flourish;
45
+ //# sourceMappingURL=Flourish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Flourish.js","sourceRoot":"","sources":["../../../src/content-tree/references/Flourish.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAGjC,OAAO,eAAe,MAAM,+BAA+B,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAEhD,MAAM,OAAO,gBAAgB;IAA7B;QACE,kBAAa,GAAG,KAAK,EACnB,MAAmD,EACnD,KAAc,EACd,OAAqB,EACrB,EAAE;YACF,MAAM,aAAa,GAAG,IAAI,CAAA;YAC1B,MAAM,cAAc,GAAG,IAAI,CAAA;YAE3B,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,YAAY,CAAA;YAC1C,MAAM,SAAS,GACb,OAAO,MAAM,CAAC,SAAS,CAAC,SAAS,KAAK,QAAQ;gBAC5C,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS;gBAC5B,CAAC,CAAC,KAAK,CAAA;YAEX,MAAM,WAAW,GAAG,kCAAkC,IAAI,IACxD,MAAM,CAAC,SAAS,CAAC,EACnB,aAAa,SAAS,CAAC,CAAC,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;YAC3D,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,gBAAgB,CACtE,WAAW,CACZ,CAAA;YAED,MAAM,sBAAsB,GAAG,eAAe,CAAC;gBAC7C,GAAG,EAAE,WAAW;gBAChB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,aAAa;aAC7C,CAAC,CAAA;YAEF,OAAO;gBACL,IAAI,EAAE,OAAO;gBACb,GAAG,EAAE,sBAAsB;gBAC3B,SAAS,EAAE,EAAE;gBACb,MAAM,EAAE,UAAU;gBAClB,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,aAAa;gBAClE,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,cAAc;aACvE,CAAA;QACH,CAAC,CAAA;IACH,CAAC;CAAA;AAED,MAAM,QAAQ,GAAqB;IACjC,OAAO,EAAE,GAAG,CAAA;;;;;;;QAON,eAAe;;MAEjB;IACJ,OAAO,EAAE,IAAI,gBAAgB,EAAE;CAChC,CAAA;AAED,eAAe,QAAQ,CAAA"}
@@ -0,0 +1,93 @@
1
+ import { jest } from '@jest/globals';
2
+ import { FlourishResolver } from './Flourish.js';
3
+ describe('Flourish Model', () => {
4
+ const origamiGetImageMetadataMock = jest.fn();
5
+ const resolvers = new FlourishResolver();
6
+ const context = {
7
+ systemCode: 'example-system',
8
+ dataSources: {
9
+ origami: {
10
+ getImageMetadata: origamiGetImageMetadataMock,
11
+ },
12
+ },
13
+ };
14
+ afterEach(() => {
15
+ jest.resetAllMocks();
16
+ });
17
+ describe('.fallbackImage()', () => {
18
+ describe('url', () => {
19
+ it('The Flourish base image url is used', async () => {
20
+ const reference = {
21
+ type: 'flourish',
22
+ id: 'example-id',
23
+ flourishType: 'example-type',
24
+ layoutWidth: 'grid',
25
+ timestamp: '2022-01-06T14:41:01.102Z',
26
+ description: 'Example description',
27
+ };
28
+ const fallbackResponse = await resolvers.fallbackImage({ reference }, undefined, context);
29
+ // Regex checks for flourish url whilst leaving spaces for arguments
30
+ // https://public.flourish.studio/{type}/{id}/thumbnail
31
+ const flourishImageUrlRegex = new RegExp(/https:\/\/public.flourish.studio\/.*\/.*\/thumbnail/);
32
+ expect(decodeURIComponent(fallbackResponse.url)).toMatch(flourishImageUrlRegex);
33
+ });
34
+ it('The Flourish id is included in the flourish image url', async () => {
35
+ const reference = {
36
+ type: 'flourish',
37
+ id: 'example-id',
38
+ flourishType: 'example-type',
39
+ layoutWidth: 'grid',
40
+ timestamp: '2022-01-06T14:41:01.102Z',
41
+ description: 'Example description',
42
+ };
43
+ const fallbackResponse = await resolvers.fallbackImage({ reference }, null, context);
44
+ const flourishImageUrlRegex = new RegExp(/https:\/\/public.flourish.studio\/.*\/example-id/);
45
+ expect(decodeURIComponent(fallbackResponse.url)).toMatch(flourishImageUrlRegex);
46
+ });
47
+ it('The Flourish graphic type is included in the flourish image url', async () => {
48
+ const reference = {
49
+ type: 'flourish',
50
+ id: 'example-id',
51
+ flourishType: 'example-type',
52
+ layoutWidth: 'grid',
53
+ timestamp: '2022-01-06T14:41:01.102Z',
54
+ description: 'Example description',
55
+ };
56
+ const fallbackResponse = await resolvers.fallbackImage({ reference }, null, context);
57
+ const flourishImageUrl = 'https://public.flourish.studio/example-type';
58
+ expect(fallbackResponse.url).toContain(encodeURIComponent(flourishImageUrl));
59
+ });
60
+ describe('the flourish timestamp is present', () => {
61
+ it('The Flourish cache buster is appended to the flourish image url', async () => {
62
+ const reference = {
63
+ type: 'flourish',
64
+ id: 'example-id',
65
+ flourishType: 'example-type',
66
+ layoutWidth: 'grid',
67
+ timestamp: '2022-01-06T14:41:01.102Z',
68
+ description: 'Example description',
69
+ };
70
+ const fallbackResponse = await resolvers.fallbackImage({ reference }, null, context);
71
+ const flourishImageCacheBuster = '?cacheBuster=2022-01-06T14:41:01.102Z';
72
+ expect(fallbackResponse.url).toContain(encodeURIComponent(flourishImageCacheBuster));
73
+ });
74
+ });
75
+ describe('the flourish timestamp is missing', () => {
76
+ it('The Flourish cache buster is not appended to the flourish image url', async () => {
77
+ const reference = {
78
+ type: 'flourish',
79
+ id: 'example-id',
80
+ flourishType: 'example-type',
81
+ layoutWidth: 'grid',
82
+ timestamp: '',
83
+ description: 'Example description',
84
+ };
85
+ const fallbackResponse = await resolvers.fallbackImage({ reference }, null, context);
86
+ const flourishImageCacheBuster = '?cacheBuster=';
87
+ expect(fallbackResponse.url).not.toContain(encodeURIComponent(flourishImageCacheBuster));
88
+ });
89
+ });
90
+ });
91
+ });
92
+ });
93
+ //# sourceMappingURL=Flourish.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Flourish.test.js","sourceRoot":"","sources":["../../../src/content-tree/references/Flourish.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { GraphQLReference } from './index.js';
2
+ declare const ImageSet: GraphQLReference;
3
+ export default ImageSet;
@@ -0,0 +1,29 @@
1
+ import { gql } from 'graphql-tag';
2
+ import { uuidFromUrl } from '../../helpers/metadata.js';
3
+ import { ReferenceFields } from './Reference.js';
4
+ import { Picture } from '../../model/Picture.js';
5
+ class ImageSetResolver {
6
+ constructor() {
7
+ this.picture = (parent, _args, context) => {
8
+ const imageSet = parent.contentApiData
9
+ ?.embeds()
10
+ ?.find((embed) => uuidFromUrl(embed.id) === uuidFromUrl(parent.reference.id));
11
+ return imageSet ? new Picture(imageSet, context) : null;
12
+ };
13
+ }
14
+ }
15
+ const ImageSet = {
16
+ typeDef: gql `
17
+ type ImageSet implements Reference {
18
+ ${ReferenceFields}
19
+ picture: Picture!
20
+ }
21
+ type MainImage implements Reference {
22
+ ${ReferenceFields}
23
+ picture: Picture!
24
+ }
25
+ `,
26
+ resolve: new ImageSetResolver(),
27
+ };
28
+ export default ImageSet;
29
+ //# sourceMappingURL=ImageSet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageSet.js","sourceRoot":"","sources":["../../../src/content-tree/references/ImageSet.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAEhD,MAAM,gBAAgB;IAAtB;QACE,YAAO,GAAG,CACR,MAAmD,EACnD,KAAc,EACd,OAAqB,EACrB,EAAE;YACF,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc;gBACpC,EAAE,MAAM,EAAE;gBACV,EAAE,IAAI,CACJ,CAAC,KAAmB,EAAE,EAAE,CACtB,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAC7D,CAAA;YAEH,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACzD,CAAC,CAAA;IACH,CAAC;CAAA;AACD,MAAM,QAAQ,GAAqB;IACjC,OAAO,EAAE,GAAG,CAAA;;QAEN,eAAe;;;;QAIf,eAAe;;;GAGpB;IACD,OAAO,EAAE,IAAI,gBAAgB,EAAE;CAChC,CAAA;AAED,eAAe,QAAQ,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { GraphQLReference } from './index.js';
2
+ declare const LayoutImage: GraphQLReference;
3
+ export default LayoutImage;
@@ -0,0 +1,37 @@
1
+ import { gql } from 'graphql-tag';
2
+ import { ReferenceFields } from './Reference.js';
3
+ import { Picture } from '../../model/Picture.js';
4
+ class LayoutImageResolver {
5
+ constructor() {
6
+ this.picture = (parent, _args, context) => {
7
+ return new Picture({
8
+ id: 'layout-imageset',
9
+ description: parent.reference.alt,
10
+ type: 'http://www.ft.com/ontology/content/ImageSet',
11
+ members: [
12
+ {
13
+ id: parent.reference.id,
14
+ description: parent.reference.alt,
15
+ title: parent.reference.caption,
16
+ binaryUrl: parent.reference.id,
17
+ copyright: {
18
+ notice: parent.reference.credit,
19
+ },
20
+ type: 'http://www.ft.com/ontology/content/Image',
21
+ },
22
+ ],
23
+ }, context);
24
+ };
25
+ }
26
+ }
27
+ const LayoutImage = {
28
+ typeDef: gql `
29
+ type LayoutImage implements Reference {
30
+ ${ReferenceFields}
31
+ picture: Picture!
32
+ }
33
+ `,
34
+ resolve: new LayoutImageResolver(),
35
+ };
36
+ export default LayoutImage;
37
+ //# sourceMappingURL=LayoutImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutImage.js","sourceRoot":"","sources":["../../../src/content-tree/references/LayoutImage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAGjC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAEhD,MAAM,mBAAmB;IAAzB;QACE,YAAO,GAAG,CACR,MAAsD,EACtD,KAAU,EACV,OAAqB,EACZ,EAAE;YACX,OAAO,IAAI,OAAO,CAChB;gBACE,EAAE,EAAE,iBAAiB;gBACrB,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG;gBACjC,IAAI,EAAE,6CAA6C;gBACnD,OAAO,EAAE;oBACP;wBACE,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE;wBACvB,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG;wBACjC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO;wBAC/B,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE;wBAC9B,SAAS,EAAE;4BACT,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM;yBAChC;wBACD,IAAI,EAAE,0CAA0C;qBACjD;iBACF;aACF,EACD,OAAO,CACR,CAAA;QACH,CAAC,CAAA;IACH,CAAC;CAAA;AACD,MAAM,WAAW,GAAqB;IACpC,OAAO,EAAE,GAAG,CAAA;;QAEN,eAAe;;;GAGpB;IACD,OAAO,EAAE,IAAI,mBAAmB,EAAE;CACnC,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { GraphQLReference } from './index.js';
2
+ import { RawImage } from '../Workarounds.js';
3
+ declare const RawImage: GraphQLReference;
4
+ export default RawImage;