Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gatsby, gatsby-plugin-utils, gatsby-source-wordpress, gatsby-source-contentful, gatsby-source-drupal): Add setRequestHeaders action/api #35655

Merged
merged 59 commits into from
Jun 2, 2022
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
50b4491
start adding new action
TylerBarnes May 12, 2022
1b88fc4
Add request headers api
TylerBarnes May 13, 2022
8e082b4
types are in src
TylerBarnes May 13, 2022
6057597
Add source plugin docs
TylerBarnes May 13, 2022
dc054d4
try removing type imports
TylerBarnes May 13, 2022
74f76c9
any
TylerBarnes May 13, 2022
aead073
move validation from reducer into setRequestHeaders action
TylerBarnes May 13, 2022
b49a3a2
add comments
TylerBarnes May 13, 2022
885031c
move headers into each place fetchRemoteFile is called instead of ins…
TylerBarnes May 13, 2022
d19dccd
for cases where gatsby core doesn't have this yet
TylerBarnes May 17, 2022
44141cf
add tests
TylerBarnes May 17, 2022
14b6730
use import from to get the gatsby store
TylerBarnes May 17, 2022
081cd4c
try any
TylerBarnes May 17, 2022
938033b
Add action type to action union
TylerBarnes May 17, 2022
1f57bb7
Update index.d.ts
TylerBarnes May 17, 2022
3408213
move test to the bottom so the state doesn't effect the other tests
TylerBarnes May 17, 2022
4ba7177
move getRequestHeadersForUrl into gatsby/utils
TylerBarnes May 17, 2022
c744b85
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 17, 2022
ca8fc21
it seems some test generates these
TylerBarnes May 17, 2022
cc58029
remove snapshots and expect size of request headers Map
TylerBarnes May 18, 2022
7faf0d9
Revert "move getRequestHeadersForUrl into gatsby/utils"
TylerBarnes May 18, 2022
c925bd5
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 18, 2022
c07f1d7
Update get-request-headers-for-url.ts
TylerBarnes May 18, 2022
a526a9e
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 19, 2022
801a85d
review changes
TylerBarnes May 19, 2022
dd9f607
pass store down instead of importing it
TylerBarnes May 19, 2022
bba3863
remove console log
TylerBarnes May 19, 2022
1f1c122
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 19, 2022
e9b12d5
add missing actions
TylerBarnes May 19, 2022
0f0588a
add store to addRemoteFilePolyfillInterface
TylerBarnes May 19, 2022
7115053
Merge branch 'feat/get-request-headers' of https://github.com/gatsbyj…
TylerBarnes May 19, 2022
0b82135
add httpHeaders to FILE_CDN job
TylerBarnes May 19, 2022
43714c0
unused import
TylerBarnes May 19, 2022
874d424
more missing stores and pass httpHeaders into job & transformImage in…
TylerBarnes May 19, 2022
497adf7
remove reporter import
TylerBarnes May 19, 2022
dff417e
Update get-request-headers-for-url.ts
TylerBarnes May 19, 2022
c2a5dfb
fix tests
TylerBarnes May 19, 2022
6634607
fix more tests
TylerBarnes May 19, 2022
bf48099
update warning message
TylerBarnes May 19, 2022
57c75d0
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 19, 2022
a519fd4
fix store type as it may not exist
TylerBarnes May 20, 2022
69ba76c
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 20, 2022
4bdda47
pass the actual request url, not the url url param
TylerBarnes May 20, 2022
fe9e2d0
Update placeholder-handler.ts
TylerBarnes May 20, 2022
a65242b
Merge branch 'feat/get-request-headers' of https://github.com/gatsbyj…
TylerBarnes May 20, 2022
4ac2b81
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 26, 2022
e3d7aad
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 26, 2022
96138fc
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 27, 2022
9bee6fd
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 27, 2022
39ee715
show test paths on output
TylerBarnes May 27, 2022
b7056e2
debugging
TylerBarnes May 28, 2022
a2b6d47
debugging
TylerBarnes May 28, 2022
90e4fe7
add missing btoa fn
TylerBarnes May 28, 2022
7899e8b
Merge branch 'master' into feat/get-request-headers
wardpeet May 30, 2022
c9d9884
don't use btoa, use a buffer instead
TylerBarnes May 30, 2022
b42308c
group imports
TylerBarnes May 30, 2022
f9745f4
remove store & actions requires
TylerBarnes May 30, 2022
56f6509
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 30, 2022
b7305b4
Merge branch 'master' into feat/get-request-headers
TylerBarnes May 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 25 additions & 3 deletions docs/docs/how-to/plugins-and-themes/creating-a-source-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,7 @@ It is also recommended that you add a polyfill to provide support back through G
```js
import { addRemoteFilePolyfillInterface } from "gatsby-plugin-utils/polyfill-remote-file"

exports.createSchemaCustomization = ({ actions, schema }) => {
exports.createSchemaCustomization = ({ actions, schema, store }) => {
const imageAssetType = addRemoteFilePolyfillInterface(
schema.buildObjectType({
name: `YourImageAssetNodeType`,
Expand All @@ -1039,6 +1039,7 @@ exports.createSchemaCustomization = ({ actions, schema }) => {
{
schema,
actions,
store,
}
)

Expand All @@ -1061,6 +1062,27 @@ You might notice that `width`, `height`, `resize`, and `gatsbyImage` can be null

The string returned from `gatsbyImage` is intended to work seamlessly with [Gatsby Image Component](/docs/reference/built-in-components/gatsby-plugin-image/#gatsbyimage) just like `gatsbyImageData` does.

#### Adding Image CDN request headers with the `setRequestHeaders` action

Since Gatsby will be fetching files from your CMS instead of your source plugin fetching those files, you may need to set request headers for Gatsby to use in those requests.
This is needed if for example your CMS is locked down behind some kind of authentication.
For each domain Image CDN will make requests to, set the required headers following this example:

```js
exports.onPluginInit = ({ actions }, pluginOptions) => {
if (typeof actions.setRequestHeaders === `function`) {
actions.setRequestHeaders({
// set the domain the headers should apply to
domain: pluginOptions.apiUrl,
headers: {
// add any needed headers
Authorization: pluginOptions.authToken,
},
})
}
}
```

#### `sourceNodes` node API additions

When creating nodes, you must add some fields to the node itself to match what the `RemoteFile` interface expects. You will need `url`, `mimeType`, `filename` as mandatory fields. When you have an image type, `width` and `height` are required as well. The optional fields are `placeholderUrl` and `filesize`. `placeholderUrl` will be the url used to generate blurred or dominant color placeholder so it should contain `%width%` and `%height%` url params if possible.
Expand All @@ -1087,8 +1109,8 @@ Add the polyfill, `polyfillImageServiceDevRoutes`, to ensure that the developmen
```js
import { polyfillImageServiceDevRoutes } from "gatsby-plugin-utils/polyfill-remote-file"

export const onCreateDevServer = ({ app }) => {
polyfillImageServiceDevRoutes(app)
export const onCreateDevServer = ({ app, store }) => {
polyfillImageServiceDevRoutes(app, store)
}
```

Expand Down
14 changes: 6 additions & 8 deletions e2e-tests/development-runtime/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ exports.createSchemaCustomization = ({ actions, schema, store }) => {
{
store,
schema,
actions,
}
)
)
Expand All @@ -27,8 +28,7 @@ exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const items = [
{
name: "photoA.jpg",
url:
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
url: "https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
placeholderUrl:
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
mimeType: "image/jpg",
Expand All @@ -38,17 +38,15 @@ exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
},
{
name: "photoB.jpg",
url:
"https://images.unsplash.com/photo-1552053831-71594a27632d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&h=2000&q=10",
url: "https://images.unsplash.com/photo-1552053831-71594a27632d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&h=2000&q=10",
mimeType: "image/jpg",
filename: "photo-1552053831.jpg",
width: 1247,
height: 2000,
},
{
name: "photoC.jpg",
url:
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
url: "https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
placeholderUrl:
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
mimeType: "image/jpg",
Expand Down Expand Up @@ -275,6 +273,6 @@ exports.createResolvers = ({ createResolvers }) => {
}

/** @type{import('gatsby').onCreateDevServer} */
exports.onCreateDevServer = ({ app }) => {
polyfillImageServiceDevRoutes(app)
exports.onCreateDevServer = ({ app, store }) => {
polyfillImageServiceDevRoutes(app, store)
}
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ module.exports = {
snapshotSerializers: [`jest-serializer-path`],
collectCoverageFrom: coverageDirs,
reporters: process.env.CI
? [[`jest-silent-reporter`, { useDots: true }]].concat(
? [[`jest-silent-reporter`, { useDots: true, showPaths: true }]].concat(
useCoverage ? `jest-junit` : []
)
: [`default`].concat(useCoverage ? `jest-junit` : []),
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby-core-utils/src/fetch-remote-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ async function fetchFile({
// See if there's response headers for this url
// from a previous request.
const headers = { ...httpHeaders }

if (cachedEntry?.headers?.etag && (await fs.pathExists(filename))) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
headers[`If-None-Match`] = cachedEntry.headers.etag
Expand Down
7 changes: 4 additions & 3 deletions packages/gatsby-plugin-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const {
polyfillImageServiceDevRoutes,
} = require(`gatsby-plugin-utils/pollyfill-remote-file`)

exports.createSchemaCustomization ({ actions, schema }) => {
exports.createSchemaCustomization ({ actions, schema, store }) => {
actions.createTypes([
addRemoteFilePolyfillInterface(
schema.buildObjectType({
Expand All @@ -108,13 +108,14 @@ exports.createSchemaCustomization ({ actions, schema }) => {
{
schema,
actions,
store
}
)
]);
}

/** @type {import('gatsby').onCreateDevServer} */
exports.onCreateDevServer = ({ app }) => {
polyfillImageServiceDevRoutes(app)
exports.onCreateDevServer = ({ app, store }) => {
polyfillImageServiceDevRoutes(app, store)
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { gatsbyImageResolver } from "../index"
import * as dispatchers from "../jobs/dispatchers"
import { PlaceholderType } from "../placeholder-handler"
import { generateImageUrl } from "../utils/url-generator"
import type { Actions } from "gatsby"
import type { Actions, Store } from "gatsby"

jest.spyOn(dispatchers, `shouldDispatch`).mockImplementation(() => false)
jest.mock(`import-from`)
Expand Down Expand Up @@ -36,6 +36,14 @@ function parseSrcSet(
})
}

const store = {
getState: (): { requestHeaders: Map<string, Record<string, string>> } => {
return {
requestHeaders: new Map(),
}
},
} as unknown as Store

describe(`gatsbyImageData`, () => {
const cacheDir = path.join(__dirname, `.cache`)

Expand Down Expand Up @@ -86,7 +94,8 @@ describe(`gatsbyImageData`, () => {
width: 300,
placeholder: `none`,
},
actions
actions,
store
)

const parsedSrcSet = parseSrcSet(result.images.sources[0].srcSet)
Expand Down Expand Up @@ -120,7 +129,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
cropFocus: [`entropy`],
},
actions
actions,
store
)
const parsedSrcSet = parseSrcSet(result.images.sources[0].srcSet)
expect(parsedSrcSet[0].src).toEqual(
Expand Down Expand Up @@ -159,7 +169,8 @@ describe(`gatsbyImageData`, () => {
},
// @ts-ignore - don't care
{},
actions
actions,
store
)
).toBe(null)
expect(dispatchers.shouldDispatch).not.toHaveBeenCalled()
Expand All @@ -173,7 +184,8 @@ describe(`gatsbyImageData`, () => {
width: 300,
placeholder: `none`,
},
actions
actions,
store
)

const parsedSrcSet = parseSrcSet(result.images.sources[0].srcSet)
Expand Down Expand Up @@ -247,7 +259,8 @@ describe(`gatsbyImageData`, () => {
width: 300,
placeholder: `none`,
},
actions
actions,
store
)

const parsedSrcSet = parseSrcSet(result.images.sources[0].srcSet)
Expand Down Expand Up @@ -357,7 +370,8 @@ describe(`gatsbyImageData`, () => {
width: 2000,
placeholder: `none`,
},
actions
actions,
store
)

const parsedSrcSet = parseSrcSet(result.images.sources[0].srcSet)
Expand Down Expand Up @@ -464,7 +478,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
outputPixelDensities: [1, 2],
},
actions
actions,
store
)
const constrainedResult = await gatsbyImageResolver(
portraitSource,
Expand All @@ -474,7 +489,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
outputPixelDensities: [1, 2],
},
actions
actions,
store
)
const fullWidthResult = await gatsbyImageResolver(
{
Expand All @@ -488,7 +504,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
outputPixelDensities: [1, 2],
},
actions
actions,
store
)

const parsedFixedSrcSet = parseSrcSet(fixedResult.images.sources[0].srcSet)
Expand Down Expand Up @@ -576,7 +593,8 @@ describe(`gatsbyImageData`, () => {
layout: `constrained`,
placeholder: `none`,
},
actions
actions,
store
)

expect(result.images.fallback.src).not.toContain(` `)
Expand All @@ -597,7 +615,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
breakpoints: [350, 700],
},
actions
actions,
store
)
const constrainedResult = await gatsbyImageResolver(
biggerPortraitSource,
Expand All @@ -607,7 +626,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
breakpoints: [350, 700],
},
actions
actions,
store
)
const fullWidthResult = await gatsbyImageResolver(
biggerPortraitSource,
Expand All @@ -617,7 +637,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
breakpoints: [350, 700],
},
actions
actions,
store
)

const parsedFixedSrcSet = parseSrcSet(fixedResult.images.sources[0].srcSet)
Expand Down Expand Up @@ -647,7 +668,8 @@ describe(`gatsbyImageData`, () => {
layout: `fixed`,
width: 300,
},
actions
actions,
store
)

expect(fetchRemoteFile).toHaveBeenCalledTimes(1)
Expand All @@ -665,7 +687,8 @@ describe(`gatsbyImageData`, () => {
width: 300,
placeholder: PlaceholderType.BLURRED,
},
actions
actions,
store
)

expect(fetchRemoteFile).toHaveBeenCalledTimes(1)
Expand All @@ -692,7 +715,8 @@ describe(`gatsbyImageData`, () => {
width: 300,
placeholder: PlaceholderType.TRACED_SVG,
},
actions
actions,
store
)

expect(fetchRemoteFile).toHaveBeenCalledTimes(1)
Expand All @@ -717,7 +741,8 @@ describe(`gatsbyImageData`, () => {
placeholder: `none`,
outputPixelDensities: [1, 2],
},
actions
actions,
store
)

expect(constrainedResult?.images.sources[0].type).toBe(`image/avif`)
Expand All @@ -726,4 +751,53 @@ describe(`gatsbyImageData`, () => {

expect(constrainedResult?.images.sources.length).toBe(2)
})

it(`should fetch placeholder file with headers from the setRequestHeaders action`, async () => {
fetchRemoteFile.mockImplementationOnce(input => {
if (
!input.httpHeaders ||
input.httpHeaders.Authorization !== `Bearer 123`
) {
throw Error(`No headers found for url ${input.url}`)
} else {
return path.join(__dirname, `__fixtures__`, `dog-portrait.jpg`)
}
})

const { store } = jest.requireActual(`gatsby/dist/redux`)
const { actions } = jest.requireActual(`gatsby/dist/redux/actions/public`)

store.dispatch(
actions.setRequestHeaders(
{
domain: portraitSource.url,
headers: {
Authorization: `Bearer 123`,
},
},
{
name: `gatsby-source-test`,
}
)
)
TylerBarnes marked this conversation as resolved.
Show resolved Hide resolved

const fixedResult = await gatsbyImageResolver(
portraitSource,
{
layout: `fixed`,
width: 300,
placeholder: PlaceholderType.BLURRED,
},
actions,
store
)

expect(fetchRemoteFile).toHaveBeenCalledTimes(1)
expect(fetchRemoteFile).toHaveBeenCalledWith(
expect.objectContaining({
url: portraitSource.url,
})
)
expect(fixedResult?.placeholder).toBeTruthy()
})
})