From c84e824d980597b1e89b731bb7f530196ac66c95 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Fri, 26 Jan 2024 13:14:47 +0100 Subject: [PATCH 1/8] Add example and tests for persisted queries --- examples/persisted-operations/.meshrc.yml | 22 ++++++++++ examples/persisted-operations/jest.config.js | 5 +++ examples/persisted-operations/package.json | 20 +++++++++ .../persisted-operations/sandbox.config.json | 8 ++++ .../persisted-queries.test.ts.snap | 11 +++++ .../tests/persisted-queries.test.ts | 44 +++++++++++++++++++ 6 files changed, 110 insertions(+) create mode 100644 examples/persisted-operations/.meshrc.yml create mode 100644 examples/persisted-operations/jest.config.js create mode 100644 examples/persisted-operations/package.json create mode 100644 examples/persisted-operations/sandbox.config.json create mode 100644 examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap create mode 100644 examples/persisted-operations/tests/persisted-queries.test.ts diff --git a/examples/persisted-operations/.meshrc.yml b/examples/persisted-operations/.meshrc.yml new file mode 100644 index 0000000000000..3598ca670bdf8 --- /dev/null +++ b/examples/persisted-operations/.meshrc.yml @@ -0,0 +1,22 @@ +sources: + - name: Hello World + handler: + jsonSchema: + operations: + - type: Query + field: greeting + method: GET + path: / + responseSample: + hello: world +plugins: + - mock: + mocks: + - apply: Query.greeting +documents: + - | + query HelloWorld { + greeting { + hello + } + } diff --git a/examples/persisted-operations/jest.config.js b/examples/persisted-operations/jest.config.js new file mode 100644 index 0000000000000..3745fc223702d --- /dev/null +++ b/examples/persisted-operations/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/examples/persisted-operations/package.json b/examples/persisted-operations/package.json new file mode 100644 index 0000000000000..478932ca19662 --- /dev/null +++ b/examples/persisted-operations/package.json @@ -0,0 +1,20 @@ +{ + "name": "persisted-operations", + "version": "0.7.5", + "license": "MIT", + "private": true, + "scripts": { + "start": "mesh dev", + "test": "jest" + }, + "dependencies": { + "@graphql-mesh/cli": "0.88.5", + "@graphql-mesh/json-schema": "0.97.4", + "@graphql-mesh/plugin-mock": "0.96.3", + "@graphql-yoga/plugin-sofa": "3.1.1", + "graphql": "16.8.1" + }, + "devDependencies": { + "jest": "29.7.0" + } +} diff --git a/examples/persisted-operations/sandbox.config.json b/examples/persisted-operations/sandbox.config.json new file mode 100644 index 0000000000000..dbf0b15a128b4 --- /dev/null +++ b/examples/persisted-operations/sandbox.config.json @@ -0,0 +1,8 @@ +{ + "infiniteLoopProtection": true, + "hardReloadOnChange": false, + "template": "node", + "container": { + "node": "21" + } +} diff --git a/examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap b/examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap new file mode 100644 index 0000000000000..44017e8e963f1 --- /dev/null +++ b/examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Hello World should give correct response for given hash 1`] = ` +{ + "data": { + "greeting": { + "hello": "world", + }, + }, +} +`; diff --git a/examples/persisted-operations/tests/persisted-queries.test.ts b/examples/persisted-operations/tests/persisted-queries.test.ts new file mode 100644 index 0000000000000..ba693b938ffd4 --- /dev/null +++ b/examples/persisted-operations/tests/persisted-queries.test.ts @@ -0,0 +1,44 @@ +import { join } from 'path'; +import { ExecutionResult } from 'graphql'; +import { findAndParseConfig } from '@graphql-mesh/cli'; +import { createMeshHTTPHandler, MeshHTTPHandler } from '@graphql-mesh/http'; +import { MeshInstance } from '@graphql-mesh/runtime'; +import { getTestMesh } from '../../../packages/testing/getTestMesh'; + +const baseDir = join(__dirname, '..'); + +describe('Hello World', () => { + let mesh: MeshInstance; + let meshHttp: MeshHTTPHandler; + + beforeAll(async () => { + const config = await findAndParseConfig({ dir: baseDir }); + mesh = await getTestMesh(config); + meshHttp = createMeshHTTPHandler({ + baseDir, + getBuiltMesh: () => Promise.resolve(mesh), + }); + }); + + afterAll(() => mesh.destroy()); + + it('should give correct response for given hash', async () => { + const response = await meshHttp.fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + extensions: { + persistedQuery: { + version: 1, + sha256Hash: '2e0534aab6b2b83bc791439094830b45b621deec2087b8567539f37defd391ac', + }, + }, + }), + }); + + expect(response.status).toBe(200); + const result = (await response.json()) as ExecutionResult; + expect(result?.errors).toBeFalsy(); + expect(result).toMatchSnapshot(); + }); +}); From 9ba214756fb743bb7b2ecc771bbfc804d0bce73b Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Fri, 26 Jan 2024 13:21:35 +0100 Subject: [PATCH 2/8] add test for file documents --- examples/persisted-operations/.meshrc.yml | 7 +++--- .../src/hello-world.graphql | 5 ++++ .../persisted-queries.test.ts.snap | 11 --------- .../tests/persisted-queries.test.ts | 24 +++++++++++++++++-- 4 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 examples/persisted-operations/src/hello-world.graphql delete mode 100644 examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap diff --git a/examples/persisted-operations/.meshrc.yml b/examples/persisted-operations/.meshrc.yml index 3598ca670bdf8..9f636b3971b91 100644 --- a/examples/persisted-operations/.meshrc.yml +++ b/examples/persisted-operations/.meshrc.yml @@ -15,8 +15,7 @@ plugins: - apply: Query.greeting documents: - | - query HelloWorld { - greeting { - hello - } + query TypeName { + __typename } + - ./src/**/*.graphql diff --git a/examples/persisted-operations/src/hello-world.graphql b/examples/persisted-operations/src/hello-world.graphql new file mode 100644 index 0000000000000..2752f8e3d87ff --- /dev/null +++ b/examples/persisted-operations/src/hello-world.graphql @@ -0,0 +1,5 @@ +query HelloWorld { + greeting { + hello + } +} diff --git a/examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap b/examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap deleted file mode 100644 index 44017e8e963f1..0000000000000 --- a/examples/persisted-operations/tests/__snapshots__/persisted-queries.test.ts.snap +++ /dev/null @@ -1,11 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Hello World should give correct response for given hash 1`] = ` -{ - "data": { - "greeting": { - "hello": "world", - }, - }, -} -`; diff --git a/examples/persisted-operations/tests/persisted-queries.test.ts b/examples/persisted-operations/tests/persisted-queries.test.ts index ba693b938ffd4..a76b3a92d92e1 100644 --- a/examples/persisted-operations/tests/persisted-queries.test.ts +++ b/examples/persisted-operations/tests/persisted-queries.test.ts @@ -22,7 +22,27 @@ describe('Hello World', () => { afterAll(() => mesh.destroy()); - it('should give correct response for given hash', async () => { + it('should give correct response for inline persisted operation', async () => { + const response = await meshHttp.fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + extensions: { + persistedQuery: { + version: 1, + sha256Hash: 'ece829f774dcb3e1358987feb1f86832b39472406a3ef65dce6a2a740304148a', + }, + }, + }), + }); + + expect(response.status).toBe(200); + const result = (await response.json()) as ExecutionResult; + expect(result?.errors).toBeFalsy(); + expect(result.data).toEqual({ __typename: 'Query' }); + }); + + it('should give correct response for file documents', async () => { const response = await meshHttp.fetch('/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -39,6 +59,6 @@ describe('Hello World', () => { expect(response.status).toBe(200); const result = (await response.json()) as ExecutionResult; expect(result?.errors).toBeFalsy(); - expect(result).toMatchSnapshot(); + expect(result.data).toEqual({ greeting: { hello: 'world' } }); }); }); From 1871e6c22aef2cd03bd2e8d6422f62f9975441a7 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Fri, 26 Jan 2024 13:29:04 +0100 Subject: [PATCH 3/8] add tests to verify we can still run arbitrary queries --- examples/persisted-operations/.meshrc.yml | 4 +++- .../tests/persisted-queries.test.ts | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/persisted-operations/.meshrc.yml b/examples/persisted-operations/.meshrc.yml index 9f636b3971b91..3342b408dfbad 100644 --- a/examples/persisted-operations/.meshrc.yml +++ b/examples/persisted-operations/.meshrc.yml @@ -14,8 +14,10 @@ plugins: mocks: - apply: Query.greeting documents: + # Documents can be specified by filename or as a glob pattern + - ./src/**/*.graphql + # Or by inline definition - | query TypeName { __typename } - - ./src/**/*.graphql diff --git a/examples/persisted-operations/tests/persisted-queries.test.ts b/examples/persisted-operations/tests/persisted-queries.test.ts index a76b3a92d92e1..70e78d232131c 100644 --- a/examples/persisted-operations/tests/persisted-queries.test.ts +++ b/examples/persisted-operations/tests/persisted-queries.test.ts @@ -61,4 +61,25 @@ describe('Hello World', () => { expect(result?.errors).toBeFalsy(); expect(result.data).toEqual({ greeting: { hello: 'world' } }); }); + + it('should not restrict to persisted queries only', async () => { + const response = await meshHttp.fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: /* GraphQL */ ` + query HelloWorld { + greeting { + __typename + } + } + `, + }), + }); + + expect(response.status).toBe(200); + const result = (await response.json()) as ExecutionResult; + expect(result?.errors).toBeFalsy(); + expect(result.data).toEqual({ greeting: { __typename: 'query_greeting' } }); + }); }); From 046f612a42121651d57d8313111d1caf2c8a8598 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Fri, 26 Jan 2024 14:43:45 +0100 Subject: [PATCH 4/8] allow to configure persisted queries --- examples/persisted-operations/.meshrc.yml | 3 + examples/persisted-operations/package.json | 2 +- .../tests/persisted-queries.test.ts | 139 ++++++++++-------- packages/config/src/process.ts | 2 + packages/config/yaml-config.graphql | 35 +++++ packages/types/src/config-schema.json | 42 ++++++ packages/types/src/config.ts | 32 ++++ 7 files changed, 190 insertions(+), 65 deletions(-) diff --git a/examples/persisted-operations/.meshrc.yml b/examples/persisted-operations/.meshrc.yml index 3342b408dfbad..21052ebe62776 100644 --- a/examples/persisted-operations/.meshrc.yml +++ b/examples/persisted-operations/.meshrc.yml @@ -21,3 +21,6 @@ documents: query TypeName { __typename } + +persistedOperations: + allowArbitraryOperations: true diff --git a/examples/persisted-operations/package.json b/examples/persisted-operations/package.json index 478932ca19662..7ab32015eaed5 100644 --- a/examples/persisted-operations/package.json +++ b/examples/persisted-operations/package.json @@ -5,7 +5,7 @@ "private": true, "scripts": { "start": "mesh dev", - "test": "jest" + "test": "mesh build && jest" }, "dependencies": { "@graphql-mesh/cli": "0.88.5", diff --git a/examples/persisted-operations/tests/persisted-queries.test.ts b/examples/persisted-operations/tests/persisted-queries.test.ts index 70e78d232131c..a8d572ee32cd2 100644 --- a/examples/persisted-operations/tests/persisted-queries.test.ts +++ b/examples/persisted-operations/tests/persisted-queries.test.ts @@ -2,84 +2,95 @@ import { join } from 'path'; import { ExecutionResult } from 'graphql'; import { findAndParseConfig } from '@graphql-mesh/cli'; import { createMeshHTTPHandler, MeshHTTPHandler } from '@graphql-mesh/http'; -import { MeshInstance } from '@graphql-mesh/runtime'; -import { getTestMesh } from '../../../packages/testing/getTestMesh'; +import { getMesh, MeshInstance } from '@graphql-mesh/runtime'; const baseDir = join(__dirname, '..'); -describe('Hello World', () => { - let mesh: MeshInstance; - let meshHttp: MeshHTTPHandler; - - beforeAll(async () => { +const meshInstances = { + 'Mesh runtime': async () => { const config = await findAndParseConfig({ dir: baseDir }); - mesh = await getTestMesh(config); - meshHttp = createMeshHTTPHandler({ - baseDir, - getBuiltMesh: () => Promise.resolve(mesh), - }); - }); + return getMesh(config); + }, + 'Mesh artifact': async () => { + const { getBuiltMesh } = await import('../.mesh/index'); + return getBuiltMesh(); + }, +}; - afterAll(() => mesh.destroy()); +describe('Persisted Queries', () => { + describe.each(Object.entries(meshInstances))('%s', (_, getMeshInstance) => { + let mesh: MeshInstance; + let meshHttp: MeshHTTPHandler; - it('should give correct response for inline persisted operation', async () => { - const response = await meshHttp.fetch('/graphql', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - extensions: { - persistedQuery: { - version: 1, - sha256Hash: 'ece829f774dcb3e1358987feb1f86832b39472406a3ef65dce6a2a740304148a', - }, - }, - }), + beforeAll(async () => { + mesh = await getMeshInstance(); + meshHttp = createMeshHTTPHandler({ + baseDir, + getBuiltMesh: () => Promise.resolve(mesh), + }); }); - expect(response.status).toBe(200); - const result = (await response.json()) as ExecutionResult; - expect(result?.errors).toBeFalsy(); - expect(result.data).toEqual({ __typename: 'Query' }); - }); + afterAll(() => mesh.destroy()); - it('should give correct response for file documents', async () => { - const response = await meshHttp.fetch('/graphql', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - extensions: { - persistedQuery: { - version: 1, - sha256Hash: '2e0534aab6b2b83bc791439094830b45b621deec2087b8567539f37defd391ac', + it('should give correct response for inline persisted operation', async () => { + const response = await meshHttp.fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + extensions: { + persistedQuery: { + version: 1, + sha256Hash: 'ece829f774dcb3e1358987feb1f86832b39472406a3ef65dce6a2a740304148a', + }, }, - }, - }), + }), + }); + + expect(response.status).toBe(200); + const result = (await response.json()) as ExecutionResult; + expect(result?.errors).toBeFalsy(); + expect(result.data).toEqual({ __typename: 'Query' }); }); - expect(response.status).toBe(200); - const result = (await response.json()) as ExecutionResult; - expect(result?.errors).toBeFalsy(); - expect(result.data).toEqual({ greeting: { hello: 'world' } }); - }); + it('should give correct response for file documents', async () => { + const response = await meshHttp.fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + extensions: { + persistedQuery: { + version: 1, + sha256Hash: '2e0534aab6b2b83bc791439094830b45b621deec2087b8567539f37defd391ac', + }, + }, + }), + }); - it('should not restrict to persisted queries only', async () => { - const response = await meshHttp.fetch('/graphql', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - query: /* GraphQL */ ` - query HelloWorld { - greeting { - __typename - } - } - `, - }), + expect(response.status).toBe(200); + const result = (await response.json()) as ExecutionResult; + expect(result?.errors).toBeFalsy(); + expect(result.data).toEqual({ greeting: { hello: 'world' } }); }); - expect(response.status).toBe(200); - const result = (await response.json()) as ExecutionResult; - expect(result?.errors).toBeFalsy(); - expect(result.data).toEqual({ greeting: { __typename: 'query_greeting' } }); + it('should not restrict to persisted queries only', async () => { + const response = await meshHttp.fetch('/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: /* GraphQL */ ` + query HelloWorld { + greeting { + __typename + } + } + `, + }), + }); + + expect(response.status).toBe(200); + const result = (await response.json()) as ExecutionResult; + expect(result?.errors).toBeFalsy(); + expect(result.data).toEqual({ greeting: { __typename: 'query_greeting' } }); + }); }); }); diff --git a/packages/config/src/process.ts b/packages/config/src/process.ts index 10c310d133a13..cc12d21b6cbe7 100644 --- a/packages/config/src/process.ts +++ b/packages/config/src/process.ts @@ -640,6 +640,7 @@ export async function processConfig( }, skipDocumentValidation: true, allowArbitraryOperations: true, + ...config.persistedOperations, }), ); if (options.generateCode) { @@ -653,6 +654,7 @@ export async function processConfig( getPersistedOperation(key) { return documentHashMap[key]; }, + ...${JSON.stringify(config.persistedOperations ?? {}, null, 2)} }))`); } } diff --git a/packages/config/yaml-config.graphql b/packages/config/yaml-config.graphql index 6c6d87d3adc78..b94d22b833af0 100644 --- a/packages/config/yaml-config.graphql +++ b/packages/config/yaml-config.graphql @@ -35,6 +35,10 @@ type Query { """ documents: [String!] """ + Configure persisted operations options + """ + persistedOperations: PersistedOperationsConfig + """ Logger instance that matches `Console` interface of NodeJS """ logger: Any @@ -151,3 +155,34 @@ type PubSubConfig { name: String! config: Any } + +type PersistedOperationsConfig { + """ + Whether to allow execution of arbitrary GraphQL operations aside from persisted operations. + """ + allowArbitraryOperations: Boolean + """ + Whether to skip validation of the persisted operation + """ + skipDocumentValidation: Boolean + + """ + Custom errors to be thrown + """ + customErrors: CustomPersistedQueryErrors +} + +type CustomPersistedQueryErrors { + """ + Error to be thrown when the persisted operation is not found + """ + notFound: String + """ + Error to be thrown when rejecting non-persisted operations + """ + persistedQueryOnly: String + """ + Error to be thrown when the extraction of the persisted operation id failed + """ + keyNotFound: String +} diff --git a/packages/types/src/config-schema.json b/packages/types/src/config-schema.json index 44d9dfe1a98b2..b2652b75c54c1 100644 --- a/packages/types/src/config-schema.json +++ b/packages/types/src/config-schema.json @@ -722,6 +722,44 @@ }, "required": ["name"] }, + "PersistedOperationsConfig": { + "additionalProperties": false, + "type": "object", + "title": "PersistedOperationsConfig", + "properties": { + "allowArbitraryOperations": { + "type": "boolean", + "description": "Whether to allow execution of arbitrary GraphQL operations aside from persisted operations." + }, + "skipDocumentValidation": { + "type": "boolean", + "description": "Whether to skip validation of the persisted operation" + }, + "customErrors": { + "$ref": "#/definitions/CustomPersistedQueryErrors", + "description": "Custom errors to be thrown" + } + } + }, + "CustomPersistedQueryErrors": { + "additionalProperties": false, + "type": "object", + "title": "CustomPersistedQueryErrors", + "properties": { + "notFound": { + "type": "string", + "description": "Error to be thrown when the persisted operation is not found" + }, + "persistedQueryOnly": { + "type": "string", + "description": "Error to be thrown when rejecting non-persisted operations" + }, + "keyNotFound": { + "type": "string", + "description": "Error to be thrown when the extraction of the persisted operation id failed" + } + } + }, "GraphQLHandlerMultipleHTTPConfiguration": { "additionalProperties": false, "type": "object", @@ -4211,6 +4249,10 @@ "additionalItems": false, "description": "Provide a query or queries for GraphQL Playground, validation and SDK Generation\nThe value can be the file path, glob expression for the file paths or the SDL.\n(.js, .jsx, .graphql, .gql, .ts and .tsx files are supported." }, + "persistedOperations": { + "$ref": "#/definitions/PersistedOperationsConfig", + "description": "Configure persisted operations options" + }, "logger": { "anyOf": [ { diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index 52403c740caa0..9974791cd0eed 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -49,6 +49,7 @@ export interface Config { * (.js, .jsx, .graphql, .gql, .ts and .tsx files are supported. */ documents?: string[]; + persistedOperations?: PersistedOperationsConfig; /** * Logger instance that matches `Console` interface of NodeJS */ @@ -1730,6 +1731,37 @@ export interface PubSubConfig { name: string; config?: any; } +/** + * Configure persisted operations options + */ +export interface PersistedOperationsConfig { + /** + * Whether to allow execution of arbitrary GraphQL operations aside from persisted operations. + */ + allowArbitraryOperations?: boolean; + /** + * Whether to skip validation of the persisted operation + */ + skipDocumentValidation?: boolean; + customErrors?: CustomPersistedQueryErrors; +} +/** + * Custom errors to be thrown + */ +export interface CustomPersistedQueryErrors { + /** + * Error to be thrown when the persisted operation is not found + */ + notFound?: string; + /** + * Error to be thrown when rejecting non-persisted operations + */ + persistedQueryOnly?: string; + /** + * Error to be thrown when the extraction of the persisted operation id failed + */ + keyNotFound?: string; +} export interface Plugin { maskedErrors?: MaskedErrorsPluginConfig; immediateIntrospection?: any; From 4d73c7b09f241807c8ab55b2639719db7fb3b001 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Fri, 26 Jan 2024 14:50:07 +0100 Subject: [PATCH 5/8] add documentation --- .../docs/guides/persisted-operations.mdx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/website/src/pages/docs/guides/persisted-operations.mdx b/website/src/pages/docs/guides/persisted-operations.mdx index 8d9650d14a46b..e2c51692cb8e1 100644 --- a/website/src/pages/docs/guides/persisted-operations.mdx +++ b/website/src/pages/docs/guides/persisted-operations.mdx @@ -58,3 +58,41 @@ curl -X POST -H 'Content-Type: application/json' http://localhost:4000/graphql \ {"data":{"__typename":"Query"}} ``` + +## Allow only persisted operations + +By default, GraphQL Mesh will allow all operations to be executed in dev mode, but not in production +mode. + +You can change this behavior by setting `allowUnpersistedOperations` to `false` or `true` in your +configuration file. + +```yml +persistedOperations: + allowArbitraryOperations: true +``` + +## Other options + +You can also configure the following options: + +```yml +persistedOperations: + # Whether to allow execution of arbitrary GraphQL operations aside from persisted operations. + allowArbitraryOperations: false; + + # Whether to skip validation of the persisted operation + skipDocumentValidation: false; + + # Customize error messages + customErrors: + # Error message to return when the operation is not found + notFound: 'PersistedQueryNotFound' + + # Error to be thrown when rejecting non-persisted operations + persistedQueryOnly?: string; + + # Error to be thrown when the extraction of the persisted operation id failed + + keyNotFound?: string; +``` From eaa1fc4c5717e15207be7b311089594791075c94 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Fri, 26 Jan 2024 15:00:31 +0100 Subject: [PATCH 6/8] add persisted operation example to build list of integration test --- examples/persisted-operations/package.json | 5 +++-- newrelic_agent.log | 21 +++++++++++++++++++++ package.json | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/examples/persisted-operations/package.json b/examples/persisted-operations/package.json index 7ab32015eaed5..b7e8956531688 100644 --- a/examples/persisted-operations/package.json +++ b/examples/persisted-operations/package.json @@ -1,9 +1,10 @@ { - "name": "persisted-operations", - "version": "0.7.5", + "name": "example-persisted-operations", + "version": "0.0.1", "license": "MIT", "private": true, "scripts": { + "build": "mesh build", "start": "mesh dev", "test": "mesh build && jest" }, diff --git a/newrelic_agent.log b/newrelic_agent.log index a92dfd2dbbf86..55c674bbedbc5 100644 --- a/newrelic_agent.log +++ b/newrelic_agent.log @@ -1419,3 +1419,24 @@ {"v":0,"level":40,"name":"newrelic","hostname":"ARDAL","pid":51523,"time":"2023-09-14T19:01:30.698Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"zlib"} {"v":0,"level":50,"name":"newrelic","hostname":"ARDAL","pid":51523,"time":"2023-09-14T19:01:30.830Z","msg":"Unable to create segment for external request: External/localhost:3000/graphql","component":"Envelop_NewRelic_Plugin","module":"Test Agent"} {"v":0,"level":30,"name":"newrelic","hostname":"ARDAL","pid":51523,"time":"2023-09-14T19:01:30.887Z","msg":"Envelop_NewRelic_Plugin registered","component":"Envelop_NewRelic_Plugin","module":"Test Agent"} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.159Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.495Z","msg":"Using New Relic for Node.js. Agent version: 10.6.2; Node version: v20.10.0."} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.499Z","msg":"Using LegacyContextManager"} +{"v":0,"level":50,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.500Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/newrelic/index.js:149:11)\n at initialize (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/newrelic/index.js:86:15)\n at Object. (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/newrelic/index.js:37:3)\n at Runtime._execModule (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/jest-config/node_modules/jest-runtime/build/index.js:1439:24)\n at Runtime._loadModule (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/jest-config/node_modules/jest-runtime/build/index.js:1022:12)\n at Runtime.requireModule (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/jest-config/node_modules/jest-runtime/build/index.js:882:12)\n at Runtime.requireModuleOrMock (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/jest-config/node_modules/jest-runtime/build/index.js:1048:21)\n at Object.require (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/packages/plugins/newrelic/src/index.ts:3:1)\n at Runtime._execModule (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/jest-config/node_modules/jest-runtime/build/index.js:1439:24)\n at Runtime._loadModule (/Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/jest-config/node_modules/jest-runtime/build/index.js:1022:12)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.521Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/valentin/Dev/Projects/TheGuild/graphql-mesh/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.522Z","msg":"Using LegacyContextManager"} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.522Z","msg":"Agent state changed from stopped to started."} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.522Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"globals"} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.524Z","msg":"Adding destroy hook to clean up unresolved promises.","component":"async_hooks"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.524Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"child_process"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.525Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"crypto"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.526Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"dns"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.526Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"fs"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.527Z","msg":"Enabling debug mode for shim!","component":"TransactionShim","module":"http"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.529Z","msg":"Enabling debug mode for shim!","component":"TransactionShim","module":"https"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.529Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"inspector"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.530Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"net"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.530Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"timers"} +{"v":0,"level":40,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.531Z","msg":"Enabling debug mode for shim!","component":"Shim","module":"zlib"} +{"v":0,"level":50,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.558Z","msg":"Unable to create segment for external request: External/localhost:3000/graphql","component":"Envelop_NewRelic_Plugin","module":"Test Agent"} +{"v":0,"level":30,"name":"newrelic","hostname":"Pohm.local","pid":7751,"time":"2024-01-26T13:59:42.663Z","msg":"Envelop_NewRelic_Plugin registered","component":"Envelop_NewRelic_Plugin","module":"Test Agent"} diff --git a/package.json b/package.json index 9942a5f50f286..753b303b38210 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "packageManager": "yarn@1.22.21", "scripts": { "build": "bob build", - "build-test-artifacts": "yarn workspace json-schema-example build && yarn workspace example-fastify build", + "build-test-artifacts": "yarn workspace json-schema-example build && yarn workspace example-fastify build && yarn workspace example-persisted-operations build", "build:website": "cd website && yarn build", "ci:lint": "eslint --output-file eslint_report.json --ext .ts --format json \"./packages/**/src/**/*.ts\"", "clean": "rm -rf packages/**/dist packages/**/**/dist examples/**/node_modules/.bin/*mesh* .bob", From 51fce65acb69c5df0d82c4c9080675553910c5cc Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Mon, 29 Jan 2024 11:13:16 +0100 Subject: [PATCH 7/8] changeset --- .changeset/cuddly-islands-cross.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/cuddly-islands-cross.md diff --git a/.changeset/cuddly-islands-cross.md b/.changeset/cuddly-islands-cross.md new file mode 100644 index 0000000000000..5d613ee063da0 --- /dev/null +++ b/.changeset/cuddly-islands-cross.md @@ -0,0 +1,7 @@ +--- +"example-persisted-operations": minor +"@graphql-mesh/config": minor +"@graphql-mesh/types": minor +--- + +Allow to configure persisted operations behaviour From bcfad0ad2ef2e420279585424a9c28658593bd23 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Mon, 29 Jan 2024 11:59:23 +0100 Subject: [PATCH 8/8] Fix changeset version --- .changeset/cuddly-islands-cross.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.changeset/cuddly-islands-cross.md b/.changeset/cuddly-islands-cross.md index 5d613ee063da0..a6d42dfd56451 100644 --- a/.changeset/cuddly-islands-cross.md +++ b/.changeset/cuddly-islands-cross.md @@ -1,7 +1,6 @@ --- -"example-persisted-operations": minor -"@graphql-mesh/config": minor -"@graphql-mesh/types": minor +"@graphql-mesh/config": patch +"@graphql-mesh/types": patch --- Allow to configure persisted operations behaviour