Skip to content

Commit

Permalink
refactor into headers plugin with hooks/decorators
Browse files Browse the repository at this point in the history
- refactor into headers plugin that handles add all necessary headers across all paths/modules
- move headers related tests into new headers test suite
- add additional tests for rev proxy, functions, client routes
- add test coverage for `header-builder.ts`
- rewrite documentation

Resolves:
- resolves #392
- resolves #392
- resolves #392
- resolves #392
- resolves #392
- resolves #392
- resolves #392
- resolves #392
- resolves #392
  • Loading branch information
tsdexter committed Mar 13, 2023
1 parent 568e8ad commit 70e5443
Show file tree
Hide file tree
Showing 21 changed files with 431 additions and 238 deletions.
2 changes: 1 addition & 1 deletion .changeset/neat-pans-serve.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"gatsby-plugin-fastify": minor
---

added plugin option for configuring headers as well as defaults for security
Added to `options.features.headers` for configuring `customHeaders`, `useDefaultCaching`, and `useDefaultSecurity`. Please see the [Headers section](https://github.com/gatsby-uc/plugins/blob/main/packages/gatsby-plugin-fastify/README.md#headers) in the docs for more information.
51 changes: 30 additions & 21 deletions integration-tests/plugin-fastify/gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,36 @@ module.exports = {
{
resolve: `gatsby-plugin-fastify`,
options: {
headers: {
"/posts/page-1/*": {
"x-test-page-specific": "shows on /posts/page-1 and its page-data",
},
"/posts/page-2/*": {
"X-Content-Type-Options": "nosniff by default, overwritten for this page",
},
"/**": {
"x-test-all-pages": "shows on every page/file",
},
"/icon.png": {
"cache-control": "max-age=60000",
},
"/component*.js": {
"x-test-js": "root js file",
"x-cache-control": "overwrite cache-control for all root js files",
"cache-control": "max-age=60000",
},
"/ssr/**": {
"x-test-ssr-kept": "ssr page",
"x-test-ssr-overwrite": "ssr page",
features: {
headers: {
customHeaders: {
"/posts/page-1*": {
"x-test-page-specific": "shows on /posts/page-1 and its page-data",
},
"/posts/page-2*": {
"X-Content-Type-Options": "nosniff by default, overwritten for this page",
},
"/**": {
"x-test-all-pages": "shows on every page/file",
},
"/icon.png": {
"cache-control": "max-age=60000",
},
"/component*.js": {
"x-test-js": "root js file",
"x-cache-control": "overwrite cache-control for all root js files",
"cache-control": "max-age=60000",
},
"/ssr/**": {
"x-test-ssr-kept": "ssr page",
"x-test-ssr-overwrite": "ssr page",
},
"/posts/page-1/index.html": {
"x-test-page-specific": "shows on /posts/page-1 and its page-data (overwritten)",
},
},
useDefaultCaching: true,
useDefaultSecurity: true,
},
},
},
Expand Down
1 change: 1 addition & 0 deletions integration-tests/plugin-fastify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"benchmark": "node benchmark.js",
"develop": "gatsby develop",
"start": "gserve",
"start:devserver": "yarn build:deps && yarn start",
"build": "gatsby build",
"serve": "gatsby serve",
"clean": "gatsby clean",
Expand Down
38 changes: 23 additions & 15 deletions packages/gatsby-plugin-fastify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,31 +258,39 @@ We have implemented a compatability layer to support the Gatsby flavor of routin

### Headers

Sensible default security headers are added to all files/paths served via Fastify (this does not include proxied paths). These headers include:
Sensible default security headers are added to all files/paths.. These headers include:

- X-Frame-Options: DENY
- X-XSS-Protection: 1; mode=block
- X-Content-Type-Options: nosniff
- Referrer-Policy: same-origin

Headers for user-defined files/paths can be added/overwritten via options config with [glob patterns](https://www.npmjs.com/package/picomatch#basic-globbing). For example, to add headers to all posts with a URL structure such as `/posts/category-name/post-name` you would use a pattern like `/posts/**` as opposed to `/posts/*` as a single asterisk would only match the second level sub-directory after `/posts/` (in this case the `category-name`), not the third level where the posts reside.
Headers for user-defined path patterns can be added/overwritten via `options.features.headers.customHeaders`.

We use [picomatch](https://www.npmjs.com/package/picomatch) for pattern matching so you need to use [globbing](https://www.npmjs.com/package/picomatch#basic-globbing) to match paths. For example, to add headers to all posts with a URL structure such as `/posts/category-name/post-name` you would use a pattern like `/posts/**` rather than `/posts/*` as a single asterisk would only match the second level sub-directory after `/posts/` (in this case the `category-name`), not the third level where the posts reside.

```
{
resolve: `gatsby-plugin-fastify`,
options: {
headers: {
"/posts/**": { // all categories and posts
"x-test": "post",
},
"/posts/fun-stuff/trampolines": { // just the trampoline post
"x-test": "trampoline post",
},
},
},
resolve: `gatsby-plugin-fastify`,
options: {
features: {
headers: {
useDefaultCaching: true, // default: true
useDefaultSecurity: true, // default: true
customHeaders: {
"/posts/**": { // all categories and posts
"x-test": "post",
},
"/posts/fun-stuff/trampolines": { // just the trampoline post
"x-test": "trampoline post",
},
},
},
},
},
},
```

As in the example above, successive matching entries in `options.headers` will overwrite previous matches - this includes the default caching and security headers if you choose to use your own.
As in the example above, successive matching entries in `customHeaders` will overwrite previous matches. This successive overwriting includes overwriting the default caching and default security headers if you are including them via `options.features.headers.useDefaultCaching` and/or `options.features.headers.useDefaultSecurity` which are both `true` by default.

For SSR pages, headers configured in `options.headers` will be added to the matching routes alongside headers returned from `getServerData`, however, if both places set the same header the value in `getServerData` will take precedence.
For SSR pages, headers configured in `options.features.headers.customHeaders` will be added to the matching routes alongside headers returned from `getServerData`. If both places set the same header the value in `getServerData` will take precedence.
2 changes: 1 addition & 1 deletion packages/gatsby-plugin-fastify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"scripts": {
"build": "babel src --out-dir ./dist --ignore \"**/__tests__,**/*.d.ts\" --extensions \".ts,.js\"",
"pretest": "yarn run build && cd ../../integration-tests/plugin-fastify/ && yarn run build",
"pretest": "cd ../../integration-tests/plugin-fastify/ && yarn run build",
"test": "jest --coverage",
"watch": "yarn build --watch"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ exports[`Gatsby Node API Should Build Config 1`] = `
},
],
"fakeOption": "fakeValue",
"features": {
"headers": {
"customHeaders": {},
"useDefaultCaching": false,
"useDefaultSecurity": false,
},
},
"functions": [
{
"absoluteCompiledFilePath": "/Users/user/code/gatsby-uc/plugins/integration-tests/plugin-fastify/.cache/functions/splat/:splat.js",
Expand All @@ -27,6 +34,7 @@ exports[`Gatsby Node API Should Build Config 1`] = `
"relativeCompiledFilePath": "test.js",
},
],
"headers": {},
"prefix": "/test",
"proxies": [],
"redirects": [
Expand Down
20 changes: 15 additions & 5 deletions packages/gatsby-plugin-fastify/src/__tests__/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { onPostBuild } from "../gatsby-node";
import fs from "fs-extra";
import mockPath from "node:path";
import { onPostBuild } from "../gatsby-node";

jest.mock("../utils/constants", () => ({
...jest.requireActual("../utils/constants"),
Expand All @@ -10,8 +11,8 @@ jest.mock("../utils/constants", () => ({
}));

jest.mock("fs-extra", () => ({
existsSync: jest.fn((path) => {
if (path.includes(".cache/functions")) {
existsSync: jest.fn((filePath) => {
if (filePath.includes(mockPath.join(".cache", "functions"))) {
return true;
}
return false;
Expand Down Expand Up @@ -42,6 +43,8 @@ jest.mock("fs-extra", () => ({
"/Users/user/code/gatsby-uc/plugins/integration-tests/plugin-fastify/.cache/functions/test.js",
},
];
} else if (path.includes("webpack.stats.json")) {
return {};
} else {
throw new Error("Invalid path");
}
Expand All @@ -52,7 +55,7 @@ const pathPrefix = "/test";
const store = {
getState: jest.fn(() => ({
program: {
directory: process.cwd() + "/__files__/",
directory: "",
},
pages: [
{
Expand Down Expand Up @@ -107,6 +110,13 @@ const reporter = {

const pluginOptions = {
fakeOption: "fakeValue",
features: {
headers: {
customHeaders: {},
useDefaultCaching: false,
useDefaultSecurity: false,
},
},
};

describe(`Gatsby Node API`, () => {
Expand All @@ -115,7 +125,7 @@ describe(`Gatsby Node API`, () => {

const writeJSONCall = fs.writeJSON.mock.calls[0];
expect(fs.writeJSON).toHaveBeenCalledTimes(1);
expect(writeJSONCall[0]).toContain(".cache/gatsby-plugin-fastify.json");
expect(writeJSONCall[0]).toContain(mockPath.join(".cache", "gatsby-plugin-fastify.json"));
expect(writeJSONCall[1]).toMatchSnapshot();
});
});
Loading

0 comments on commit 70e5443

Please sign in to comment.