@financial-times/cp-content-pipeline-schema 3.12.2 → 3.14.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 +32 -0
- package/lib/datasources/origami-image.js +3 -3
- package/lib/datasources/origami-image.js.map +1 -1
- package/lib/datasources/url-management.js +45 -7
- package/lib/datasources/url-management.js.map +1 -1
- package/lib/datasources/url-management.test.js +97 -0
- package/lib/datasources/url-management.test.js.map +1 -1
- package/lib/generated/index.d.ts +11 -11
- package/lib/helpers/imageService.js +1 -1
- package/lib/helpers/imageService.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/model/Clip.d.ts +3 -1
- package/lib/model/Clip.js +4 -2
- package/lib/model/Clip.js.map +1 -1
- package/lib/model/Clip.test.js +26 -37
- package/lib/model/Clip.test.js.map +1 -1
- package/lib/model/Concept.js +8 -2
- package/lib/model/Concept.js.map +1 -1
- package/lib/model/Content.js +6 -1
- package/lib/model/Content.js.map +1 -1
- package/lib/model/FlourishSource.js +2 -2
- package/lib/model/FlourishSource.js.map +1 -1
- package/lib/model/Image.js +1 -1
- package/lib/model/Image.js.map +1 -1
- package/lib/model/Image.test.js +8 -8
- package/lib/model/Image.test.js.map +1 -1
- package/lib/model/LeadFlourish.js +1 -1
- package/lib/model/LeadFlourish.js.map +1 -1
- package/lib/model/Person.js +1 -1
- package/lib/model/Person.js.map +1 -1
- package/lib/model/Person.test.js +2 -2
- package/lib/model/Person.test.js.map +1 -1
- package/lib/model/Picture.test.js +4 -2
- package/lib/model/Picture.test.js.map +1 -1
- package/lib/model/RichText.test.js +9 -9
- package/lib/model/Topper.js +1 -1
- package/lib/model/Topper.js.map +1 -1
- package/lib/model/Topper.test.js +3 -2
- package/lib/model/Topper.test.js.map +1 -1
- package/lib/resolvers/content-tree/bodyXMLToTree.test.js +176 -176
- package/lib/resolvers/content-tree/references/ClipSet.d.ts +2 -1
- package/lib/resolvers/content-tree/references/ClipSet.js +2 -2
- package/lib/resolvers/content-tree/references/ClipSet.js.map +1 -1
- package/lib/resolvers/content-tree/references/RawImage.js +1 -1
- package/lib/resolvers/content-tree/references/RawImage.js.map +1 -1
- package/package.json +1 -1
- package/src/datasources/origami-image.ts +3 -3
- package/src/datasources/url-management.test.ts +126 -0
- package/src/datasources/url-management.ts +48 -8
- package/src/generated/index.ts +11 -11
- package/src/helpers/imageService.ts +1 -1
- package/src/index.ts +1 -1
- package/src/model/Clip.test.ts +164 -145
- package/src/model/Clip.ts +7 -3
- package/src/model/Concept.ts +8 -2
- package/src/model/Content.ts +6 -1
- package/src/model/FlourishSource.ts +3 -6
- package/src/model/Image.test.ts +8 -10
- package/src/model/Image.ts +1 -1
- package/src/model/LeadFlourish.ts +1 -1
- package/src/model/Person.test.ts +2 -2
- package/src/model/Person.ts +1 -1
- package/src/model/Picture.test.ts +4 -2
- package/src/model/RichText.test.ts +9 -9
- package/src/model/Topper.test.ts +4 -2
- package/src/model/Topper.ts +1 -1
- package/src/model/__snapshots__/Byline.test.ts.snap +99 -99
- package/src/model/__snapshots__/RichText.test.ts.snap +305 -305
- package/src/resolvers/content-tree/bodyXMLToTree.test.ts +176 -176
- package/src/resolvers/content-tree/references/ClipSet.ts +3 -2
- package/src/resolvers/content-tree/references/RawImage.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/image.graphql +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipSet.js","sourceRoot":"","sources":["../../../../src/resolvers/content-tree/references/ClipSet.ts"],"names":[],"mappings":";;;AAIA,wDAAuD;AACvD,8CAA0C;AAC1C,sDAAkD;
|
|
1
|
+
{"version":3,"file":"ClipSet.js","sourceRoot":"","sources":["../../../../src/resolvers/content-tree/references/ClipSet.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RawImage.js","sourceRoot":"","sources":["../../../../src/resolvers/content-tree/references/RawImage.ts"],"names":[],"mappings":";;;;;;AAEA,iFAA2D;AAG3D,uEAA8C;AAC9C,2DAAiE;AAEjE,MAAM,aAAa;IACG;IAAgC;IAApD,YAAoB,QAAsB,EAAU,OAAqB;QAArD,aAAQ,GAAR,QAAQ,CAAc;QAAU,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAE7E,IAAI;QACF,OAAO,OAAgB,CAAA;IACzB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAA;IAC9B,CAAC;IAED,GAAG;QACD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC1D,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC7B,CAAC;IAED,GAAG;QACD,OAAO,IAAA,sBAAe,EAAC;YACrB,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG;YACtB,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU
|
|
1
|
+
{"version":3,"file":"RawImage.js","sourceRoot":"","sources":["../../../../src/resolvers/content-tree/references/RawImage.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -6,9 +6,9 @@ const REQUEST_TIMEOUT = process.env.ORIGAMI_DATASOURCE_REQUEST_TIMEOUT
|
|
|
6
6
|
: 200
|
|
7
7
|
|
|
8
8
|
export class OrigamiImageDataSource extends InstrumentedRESTDataSource {
|
|
9
|
-
baseURL = 'https://
|
|
9
|
+
baseURL = 'https://images.ft.com/v3/image/'
|
|
10
10
|
get backendSystemCode() {
|
|
11
|
-
return '
|
|
11
|
+
return 'image-service'
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
imageMetadataCacheTTL = process.env.IMAGE_METADATA_CACHE_TTL
|
|
@@ -29,7 +29,7 @@ export class OrigamiImageDataSource extends InstrumentedRESTDataSource {
|
|
|
29
29
|
url: string
|
|
30
30
|
): Promise<{ width: number; height: number }> {
|
|
31
31
|
const imageMetadata = await this.get(
|
|
32
|
-
`
|
|
32
|
+
`metadata/${encodeURIComponent(url)}?source=cp-content-pipeline-api`
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
this.calls.push(url)
|
|
@@ -9,6 +9,7 @@ import nUrlManagementApiReadClient from '@financial-times/n-url-management-api-r
|
|
|
9
9
|
import type { QueryContext } from '..'
|
|
10
10
|
import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache'
|
|
11
11
|
import { jest } from '@jest/globals'
|
|
12
|
+
import { BaseError, HttpError } from '@dotcom-reliability-kit/errors'
|
|
12
13
|
|
|
13
14
|
const getMock = nUrlManagementApiReadClient.get as jest.Mock<
|
|
14
15
|
typeof import('@financial-times/n-url-management-api-read-client')['get']
|
|
@@ -95,4 +96,129 @@ describe('URL management data source', () => {
|
|
|
95
96
|
expect(different).toEqual('different.url.vanity')
|
|
96
97
|
expect(nUrlManagementApiReadClient.get).toHaveBeenCalledTimes(2)
|
|
97
98
|
})
|
|
99
|
+
|
|
100
|
+
describe('error handling', () => {
|
|
101
|
+
it('wraps 4xx errors in HttpError', async () => {
|
|
102
|
+
getMock.mockRejectedValueOnce(
|
|
103
|
+
new HttpError({
|
|
104
|
+
statusCode: 400,
|
|
105
|
+
code: 'UnrecognisedClientException',
|
|
106
|
+
})
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
const datasource = new URLManagementDataSource({
|
|
110
|
+
context,
|
|
111
|
+
cache: new InMemoryLRUCache(),
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
await expect(datasource.get('original.url')).rejects
|
|
115
|
+
.toMatchInlineSnapshot(`
|
|
116
|
+
HttpError: "Internal Server Error" {
|
|
117
|
+
"cause": "HttpError: "Bad Request" {
|
|
118
|
+
"cause": null,
|
|
119
|
+
"code": "UNRECOGNISEDCLIENTEXCEPTION",
|
|
120
|
+
"data": {},
|
|
121
|
+
}",
|
|
122
|
+
"code": "URL_MANAGEMENT_CLIENT_ERROR",
|
|
123
|
+
"data": {
|
|
124
|
+
"upstreamStatusCode": 400,
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
`)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('wraps 5xx errors in UpstreamError', async () => {
|
|
131
|
+
getMock.mockRejectedValueOnce(
|
|
132
|
+
new HttpError({
|
|
133
|
+
statusCode: 500,
|
|
134
|
+
code: 'InternalServerException',
|
|
135
|
+
})
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
const datasource = new URLManagementDataSource({
|
|
139
|
+
context,
|
|
140
|
+
cache: new InMemoryLRUCache(),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
await expect(datasource.get('original.url')).rejects
|
|
144
|
+
.toMatchInlineSnapshot(`
|
|
145
|
+
UpstreamServiceError: "Bad Gateway" {
|
|
146
|
+
"cause": "HttpError: "Internal Server Error" {
|
|
147
|
+
"cause": null,
|
|
148
|
+
"code": "INTERNALSERVEREXCEPTION",
|
|
149
|
+
"data": {},
|
|
150
|
+
}",
|
|
151
|
+
"code": "URL_MANAGEMENT_SERVER_ERROR",
|
|
152
|
+
"data": {
|
|
153
|
+
"upstreamStatusCode": 500,
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
`)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('wraps other HTTP errors in HttpError', async () => {
|
|
160
|
+
const error = new BaseError({
|
|
161
|
+
code: 'SomethingElseException',
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
Object.assign(error, {
|
|
165
|
+
// HttpError normalises status codes to the 400-599 range
|
|
166
|
+
statusCode: 137,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
getMock.mockRejectedValueOnce(error)
|
|
170
|
+
|
|
171
|
+
const datasource = new URLManagementDataSource({
|
|
172
|
+
context,
|
|
173
|
+
cache: new InMemoryLRUCache(),
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
await expect(datasource.get('original.url')).rejects
|
|
177
|
+
.toMatchInlineSnapshot(`
|
|
178
|
+
HttpError: "Internal Server Error" {
|
|
179
|
+
"cause": "BaseError: "An error occurred" {
|
|
180
|
+
"cause": null,
|
|
181
|
+
"code": "SOMETHINGELSEEXCEPTION",
|
|
182
|
+
"data": {},
|
|
183
|
+
}",
|
|
184
|
+
"code": "URL_MANAGEMENT_UNKNOWN_ERROR",
|
|
185
|
+
"data": {
|
|
186
|
+
"upstreamStatusCode": 137,
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
`)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('rethrows unknown errors', async () => {
|
|
193
|
+
getMock.mockRejectedValueOnce('not an error')
|
|
194
|
+
|
|
195
|
+
const datasource = new URLManagementDataSource({
|
|
196
|
+
context,
|
|
197
|
+
cache: new InMemoryLRUCache(),
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
await expect(
|
|
201
|
+
datasource.get('original.url')
|
|
202
|
+
).rejects.toMatchInlineSnapshot(`"not an error"`)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it("doesn't cache error responses", async () => {
|
|
206
|
+
getMock.mockRejectedValueOnce(new Error())
|
|
207
|
+
|
|
208
|
+
const datasource = new URLManagementDataSource({
|
|
209
|
+
context,
|
|
210
|
+
cache: new InMemoryLRUCache(),
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
await expect(datasource.get('original.url')).rejects.toThrow()
|
|
214
|
+
|
|
215
|
+
getMock.mockImplementation((fromURL) =>
|
|
216
|
+
Promise.resolve({ fromURL, toURL: `${fromURL}.vanity`, code: 0 })
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
await expect(datasource.get('original.url')).resolves.toEqual(
|
|
220
|
+
`original.url.vanity`
|
|
221
|
+
)
|
|
222
|
+
})
|
|
223
|
+
})
|
|
98
224
|
})
|
|
@@ -7,6 +7,8 @@ import nUrlManagementApiReadClient from '@financial-times/n-url-management-api-r
|
|
|
7
7
|
import type { ResolvedVanityURL } from '@financial-times/n-url-management-api-read-client'
|
|
8
8
|
import { QueryContext } from '..'
|
|
9
9
|
import { BaseDataSource, BaseDataSourceOptions } from './base'
|
|
10
|
+
import { HttpError, UpstreamServiceError } from '@dotcom-reliability-kit/errors'
|
|
11
|
+
import isError from '../helpers/isError'
|
|
10
12
|
|
|
11
13
|
const CACHE_PREFIX = 'nurlmgmtapi:'
|
|
12
14
|
const DEFAULT_TTL_IN_SECONDS = 60 * 60 // one hour
|
|
@@ -53,16 +55,54 @@ export class URLManagementDataSource implements BaseDataSource {
|
|
|
53
55
|
return fromDynamoDB.toURL
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
try {
|
|
59
|
+
let promise = this.memoizedResults.get(fromURL)
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
if (promise) {
|
|
62
|
+
return await promise
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
promise = getVanity()
|
|
66
|
+
this.memoizedResults.set(fromURL, promise)
|
|
67
|
+
|
|
68
|
+
this.calls.push(fromURL)
|
|
69
|
+
return await promise
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.memoizedResults.delete(fromURL)
|
|
72
|
+
await this.cache.delete(fromURL)
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
if (
|
|
75
|
+
isError(error) &&
|
|
76
|
+
'statusCode' in error &&
|
|
77
|
+
typeof error.statusCode === 'number'
|
|
78
|
+
) {
|
|
79
|
+
const baseErrorOptions = {
|
|
80
|
+
cause: error,
|
|
81
|
+
relatesToSystems: ['next-url-management-db'],
|
|
82
|
+
upstreamStatusCode: error.statusCode,
|
|
83
|
+
}
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
if (error.statusCode >= 400 && error.statusCode < 500) {
|
|
86
|
+
throw new HttpError({
|
|
87
|
+
code: 'URL_MANAGEMENT_CLIENT_ERROR',
|
|
88
|
+
statusCode: 500,
|
|
89
|
+
...baseErrorOptions,
|
|
90
|
+
})
|
|
91
|
+
} else if (error.statusCode >= 500 && error.statusCode < 600) {
|
|
92
|
+
throw new UpstreamServiceError({
|
|
93
|
+
code: 'URL_MANAGEMENT_SERVER_ERROR',
|
|
94
|
+
...baseErrorOptions,
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw new HttpError({
|
|
99
|
+
code: 'URL_MANAGEMENT_UNKNOWN_ERROR',
|
|
100
|
+
statusCode: 500,
|
|
101
|
+
...baseErrorOptions,
|
|
102
|
+
})
|
|
103
|
+
} else {
|
|
104
|
+
throw error
|
|
105
|
+
}
|
|
106
|
+
}
|
|
67
107
|
}
|
|
68
108
|
}
|
package/src/generated/index.ts
CHANGED
|
@@ -767,7 +767,7 @@ export type Image = {
|
|
|
767
767
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
768
768
|
/** The type of the image, eg. 'graphic'. */
|
|
769
769
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
770
|
-
/** The url of the image, eg. 'https://
|
|
770
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
771
771
|
readonly url: Scalars['String']['output'];
|
|
772
772
|
/** The width of the image in pixels. */
|
|
773
773
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -796,7 +796,7 @@ export type ImageDesktop = Image & {
|
|
|
796
796
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
797
797
|
/** The type of the image, eg. 'graphic'. */
|
|
798
798
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
799
|
-
/** The url of the image, eg. 'https://
|
|
799
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
800
800
|
readonly url: Scalars['String']['output'];
|
|
801
801
|
/** The width of the image in pixels. */
|
|
802
802
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -825,7 +825,7 @@ export type ImageLandscape = Image & {
|
|
|
825
825
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
826
826
|
/** The type of the image, eg. 'graphic'. */
|
|
827
827
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
828
|
-
/** The url of the image, eg. 'https://
|
|
828
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
829
829
|
readonly url: Scalars['String']['output'];
|
|
830
830
|
/** The width of the image in pixels. */
|
|
831
831
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -854,7 +854,7 @@ export type ImageMobile = Image & {
|
|
|
854
854
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
855
855
|
/** The type of the image, eg. 'graphic'. */
|
|
856
856
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
857
|
-
/** The url of the image, eg. 'https://
|
|
857
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
858
858
|
readonly url: Scalars['String']['output'];
|
|
859
859
|
/** The width of the image in pixels. */
|
|
860
860
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -883,7 +883,7 @@ export type ImagePortrait = Image & {
|
|
|
883
883
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
884
884
|
/** The type of the image, eg. 'graphic'. */
|
|
885
885
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
886
|
-
/** The url of the image, eg. 'https://
|
|
886
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
887
887
|
readonly url: Scalars['String']['output'];
|
|
888
888
|
/** The width of the image in pixels. */
|
|
889
889
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -905,7 +905,7 @@ export type ImageSet = Reference & {
|
|
|
905
905
|
export type ImageSource = {
|
|
906
906
|
/** The device pixel ratio. DPR is calculated as DPR = Physical Pixels (pixels seen on screen) / Logical Pixels (pixel that can fit in a given space). */
|
|
907
907
|
readonly dpr: Scalars['Int']['output'];
|
|
908
|
-
/** The url of the image source, eg. 'https://
|
|
908
|
+
/** The url of the image source, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
909
909
|
readonly url: Scalars['String']['output'];
|
|
910
910
|
/** The width of the image source in pixels. */
|
|
911
911
|
readonly width: Scalars['Int']['output'];
|
|
@@ -928,7 +928,7 @@ export type ImageSquare = Image & {
|
|
|
928
928
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
929
929
|
/** The type of the image, eg. 'graphic'. */
|
|
930
930
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
931
|
-
/** The url of the image, eg. 'https://
|
|
931
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
932
932
|
readonly url: Scalars['String']['output'];
|
|
933
933
|
/** The width of the image in pixels. */
|
|
934
934
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -957,7 +957,7 @@ export type ImageSquareFtEdit = Image & {
|
|
|
957
957
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
958
958
|
/** The type of the image, eg. 'graphic'. */
|
|
959
959
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
960
|
-
/** The url of the image, eg. 'https://
|
|
960
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
961
961
|
readonly url: Scalars['String']['output'];
|
|
962
962
|
/** The width of the image in pixels. */
|
|
963
963
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -986,7 +986,7 @@ export type ImageStandard = Image & {
|
|
|
986
986
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
987
987
|
/** The type of the image, eg. 'graphic'. */
|
|
988
988
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
989
|
-
/** The url of the image, eg. 'https://
|
|
989
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
990
990
|
readonly url: Scalars['String']['output'];
|
|
991
991
|
/** The width of the image in pixels. */
|
|
992
992
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -1015,7 +1015,7 @@ export type ImageStandardInline = Image & {
|
|
|
1015
1015
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
1016
1016
|
/** The type of the image, eg. 'graphic'. */
|
|
1017
1017
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
1018
|
-
/** The url of the image, eg. 'https://
|
|
1018
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
1019
1019
|
readonly url: Scalars['String']['output'];
|
|
1020
1020
|
/** The width of the image in pixels. */
|
|
1021
1021
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -1044,7 +1044,7 @@ export type ImageWide = Image & {
|
|
|
1044
1044
|
readonly sourceSet: ReadonlyArray<ImageSource>;
|
|
1045
1045
|
/** The type of the image, eg. 'graphic'. */
|
|
1046
1046
|
readonly type?: Maybe<Scalars['ImageType']['output']>;
|
|
1047
|
-
/** The url of the image, eg. 'https://
|
|
1047
|
+
/** The url of the image, eg. 'https://images.ft.com/v3/image/raw/ftcms%3Aimage%3A0e7e7b6e-4e7b-11e9-bde6-79e4b4311f90?source=cp-content-pipeline-api&fit=scale-down&width=700'. */
|
|
1048
1048
|
readonly url: Scalars['String']['output'];
|
|
1049
1049
|
/** The width of the image in pixels. */
|
|
1050
1050
|
readonly width?: Maybe<Scalars['Int']['output']>;
|
|
@@ -19,7 +19,7 @@ export default function imageServiceUrl({
|
|
|
19
19
|
}: ImageServiceUrlArguments): string {
|
|
20
20
|
const imageSource = encodeURIComponent(id ? `ftcms:${id}` : url)
|
|
21
21
|
const imageServiceUrl = new URL(
|
|
22
|
-
`https://
|
|
22
|
+
`https://images.ft.com/v3/image/raw/${imageSource}`
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
const adjustedWidth = width > MAX_IMAGE_WIDTH ? MAX_IMAGE_WIDTH : width
|
package/src/index.ts
CHANGED