From ed2232e715c1dadc3817d8b3b469f75ddbae6ac6 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Mon, 17 Apr 2023 15:49:01 +0300 Subject: [PATCH] Serialize Headers plugin (#5333) --- .changeset/hot-queens-beg.md | 6 +++ .../plugins/serialize-headers/package.json | 45 ++++++++++++++++++ .../plugins/serialize-headers/src/index.ts | 23 +++++++++ .../test/serialize-headers.test.ts | 47 +++++++++++++++++++ .../serialize-headers/yaml-config.graphql | 7 +++ packages/types/src/config-schema.json | 18 +++++++ packages/types/src/config.ts | 4 ++ .../SerializeHeadersConfig.generated.md | 2 + yarn.lock | 6 +-- 9 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 .changeset/hot-queens-beg.md create mode 100644 packages/plugins/serialize-headers/package.json create mode 100644 packages/plugins/serialize-headers/src/index.ts create mode 100644 packages/plugins/serialize-headers/test/serialize-headers.test.ts create mode 100644 packages/plugins/serialize-headers/yaml-config.graphql create mode 100644 website/src/generated-markdown/SerializeHeadersConfig.generated.md diff --git a/.changeset/hot-queens-beg.md b/.changeset/hot-queens-beg.md new file mode 100644 index 0000000000000..4538acd7dc744 --- /dev/null +++ b/.changeset/hot-queens-beg.md @@ -0,0 +1,6 @@ +--- +'@graphql-mesh/types': patch +'@graphql-mesh/plugin-serialize-headers': patch +--- + +New Serialize Headers plugin diff --git a/packages/plugins/serialize-headers/package.json b/packages/plugins/serialize-headers/package.json new file mode 100644 index 0000000000000..2ff7774450448 --- /dev/null +++ b/packages/plugins/serialize-headers/package.json @@ -0,0 +1,45 @@ +{ + "name": "@graphql-mesh/plugin-serialize-headers", + "version": "0.0.0", + "type": "module", + "repository": { + "type": "git", + "url": "Urigo/graphql-mesh", + "directory": "packages/plugins/serialize-headers" + }, + "license": "MIT", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "exports": { + ".": { + "require": { + "types": "./dist/typings/index.d.cts", + "default": "./dist/cjs/index.js" + }, + "import": { + "types": "./dist/typings/index.d.ts", + "default": "./dist/esm/index.js" + }, + "default": { + "types": "./dist/typings/index.d.ts", + "default": "./dist/esm/index.js" + } + }, + "./package.json": "./package.json" + }, + "typings": "dist/typings/index.d.ts", + "peerDependencies": { + "@graphql-mesh/types": "^0.91.13", + "@graphql-mesh/utils": "^0.43.21", + "graphql": "*", + "tslib": "^2.4.0" + }, + "publishConfig": { + "access": "public", + "directory": "dist" + }, + "sideEffects": false, + "typescript": { + "definition": "dist/typings/index.d.ts" + } +} diff --git a/packages/plugins/serialize-headers/src/index.ts b/packages/plugins/serialize-headers/src/index.ts new file mode 100644 index 0000000000000..e10fdfa35247e --- /dev/null +++ b/packages/plugins/serialize-headers/src/index.ts @@ -0,0 +1,23 @@ +import { MeshPlugin, YamlConfig } from '@graphql-mesh/types'; + +export default function useMeshSerializeHeaders( + options: YamlConfig.SerializeHeadersConfig, +): MeshPlugin { + const map: Record = {}; + for (const headerName of options.names) { + map[headerName.toLowerCase()] = headerName; + } + function headersSerializer(headers: Headers) { + const headersObj: Record = {}; + for (const [key, value] of headers) { + const finalKey = map[key] ?? key; + headersObj[finalKey] = value; + } + return headersObj; + } + return { + onFetch({ options }) { + (options as any).headersSerializer = headersSerializer; + }, + }; +} diff --git a/packages/plugins/serialize-headers/test/serialize-headers.test.ts b/packages/plugins/serialize-headers/test/serialize-headers.test.ts new file mode 100644 index 0000000000000..f23729157afcb --- /dev/null +++ b/packages/plugins/serialize-headers/test/serialize-headers.test.ts @@ -0,0 +1,47 @@ +/* eslint-disable import/no-nodejs-modules */ + +/* eslint-disable import/no-extraneous-dependencies */ +import http from 'http'; +import useMeshSerializeHeaders from '@graphql-mesh/plugin-serialize-headers'; +import { wrapFetchWithPlugins } from '@graphql-mesh/runtime'; +import { fetch } from '@whatwg-node/fetch'; + +describe('serialize-headers', () => { + const server = http.createServer((req, res) => { + res.end('ok'); + }); + beforeEach(done => { + server.listen(0, done); + }); + afterEach(done => { + server.close(done); + }); + it('works', async () => { + jest.spyOn(http, 'request'); + const fetchFn = wrapFetchWithPlugins([ + { + onFetch({ setFetchFn }) { + setFetchFn(fetch); + }, + }, + useMeshSerializeHeaders({ + names: ['x-FOO'], + }), + ]); + const fullUrl = `http://localhost:${(server.address() as any).port}/somePath`; + const response = await fetchFn(fullUrl, { + headers: { + 'x-foo': 'bar', + }, + }); + await response.text(); + expect(http.request).toHaveBeenCalledWith( + fullUrl, + expect.objectContaining({ + headers: { + 'x-FOO': 'bar', + }, + }), + ); + }); +}); diff --git a/packages/plugins/serialize-headers/yaml-config.graphql b/packages/plugins/serialize-headers/yaml-config.graphql new file mode 100644 index 0000000000000..ce3a1bb5bae0b --- /dev/null +++ b/packages/plugins/serialize-headers/yaml-config.graphql @@ -0,0 +1,7 @@ +extend type Plugin { + serializeHeaders: SerializeHeadersConfig +} + +type SerializeHeadersConfig @md { + names: [String!]! +} diff --git a/packages/types/src/config-schema.json b/packages/types/src/config-schema.json index f5f40741461dc..963df57c37552 100644 --- a/packages/types/src/config-schema.json +++ b/packages/types/src/config-schema.json @@ -544,6 +544,9 @@ "responseCache": { "$ref": "#/definitions/ResponseCacheConfig" }, + "serializeHeaders": { + "$ref": "#/definitions/SerializeHeadersConfig" + }, "snapshot": { "$ref": "#/definitions/SnapshotPluginConfig", "description": "Configuration for Snapshot extension" @@ -2674,6 +2677,21 @@ }, "required": ["coordinate", "ttl"] }, + "SerializeHeadersConfig": { + "additionalProperties": false, + "type": "object", + "title": "SerializeHeadersConfig", + "properties": { + "names": { + "type": "array", + "items": { + "type": "string" + }, + "additionalItems": false + } + }, + "required": ["names"] + }, "SnapshotPluginConfig": { "additionalProperties": false, "type": "object", diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index 5cbe26bd8c34c..7a35535dc1393 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -1667,6 +1667,7 @@ export interface Plugin { prometheus?: PrometheusConfig; rateLimit?: RateLimitPluginConfig; responseCache?: ResponseCacheConfig; + serializeHeaders?: SerializeHeadersConfig; snapshot?: SnapshotPluginConfig; statsd?: StatsdPlugin; [k: string]: any; @@ -2125,6 +2126,9 @@ export interface ResponseCacheTTLConfig { coordinate: string; ttl: number; } +export interface SerializeHeadersConfig { + names: string[]; +} /** * Configuration for Snapshot extension */ diff --git a/website/src/generated-markdown/SerializeHeadersConfig.generated.md b/website/src/generated-markdown/SerializeHeadersConfig.generated.md new file mode 100644 index 0000000000000..fc1373ec5e694 --- /dev/null +++ b/website/src/generated-markdown/SerializeHeadersConfig.generated.md @@ -0,0 +1,2 @@ + +* `names` (type: `Array of String`, required) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 758335ac87d95..97ff3925eb33e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5969,9 +5969,9 @@ tslib "^2.3.1" "@whatwg-node/node-fetch@^0.3.3": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.4.tgz#4beb88579c53ebd870e28d0f2f0376191b9fd6c3" - integrity sha512-gP1MN6DiHVbhkLWH1eCELhE2ZtLRxb+HRKu4eYze1Tijxz0uT1T2kk3lseZp94txzxCfbxGFU0jsWkxNdH3EXA== + version "0.3.5" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.5.tgz#a0e76ef72c33b0c81728421ce6d7d97257541ecb" + integrity sha512-96crxTZn6L+xFefEKkeAZrGmZ7WUXDYUzAfBf1VtrdS5YozLnFbj9/CNZ8S2LdRS2iL3pMSCvE1xD1wiIAXkAA== dependencies: "@whatwg-node/events" "^0.0.2" busboy "^1.6.0"