diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 300e955813b2..1473253eeec4 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -22,8 +22,10 @@ "@types/mongodb": "^3.6.20", "@types/mysql": "^2.15.21", "@types/pg": "^8.6.5", + "apollo-server": "^3.6.7", "cors": "^2.8.5", "express": "^4.17.3", + "graphql": "^16.3.0", "mongodb": "^3.7.3", "mongodb-memory-server-global": "^7.6.3", "mysql": "^2.18.1", diff --git a/packages/node-integration-tests/suites/tracing/apollo-graphql/scenario.ts b/packages/node-integration-tests/suites/tracing/apollo-graphql/scenario.ts new file mode 100644 index 000000000000..cea4a03df10e --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/apollo-graphql/scenario.ts @@ -0,0 +1,44 @@ +import * as Sentry from '@sentry/node'; +import * as Tracing from '@sentry/tracing'; +import { ApolloServer, gql } from 'apollo-server'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + integrations: [new Tracing.Integrations.GraphQL(), new Tracing.Integrations.Apollo()], +}); + +const typeDefs = gql` + type Query { + hello: String + } +`; + +const resolvers = { + Query: { + hello: () => { + return 'Hello world!'; + }, + }, +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, +}); + +const transaction = Sentry.startTransaction({ name: 'test_transaction', op: 'transaction' }); + +Sentry.configureScope(scope => { + scope.setSpan(transaction); +}); + +void (async () => { + // Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation + await server.executeOperation({ + query: '{hello}', + }); + + transaction.finish(); +})(); diff --git a/packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts b/packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts new file mode 100644 index 000000000000..c387175281b9 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts @@ -0,0 +1,35 @@ +import { assertSentryTransaction, conditionalTest, getEnvelopeRequest, runServer } from '../../../utils'; + +// Node 10 is not supported by `graphql-js` +// Ref: https://github.com/graphql/graphql-js/blob/main/package.json +conditionalTest({ min: 12 })('GraphQL/Apollo Tests', () => { + test('should instrument GraphQL and Apollo Server.', async () => { + const url = await runServer(__dirname); + const envelope = await getEnvelopeRequest(url); + + expect(envelope).toHaveLength(3); + + const transaction = envelope[2]; + const parentSpanId = (transaction as any)?.contexts?.trace?.span_id; + const graphqlSpanId = (transaction as any)?.spans?.[0].span_id; + + expect(parentSpanId).toBeDefined(); + expect(graphqlSpanId).toBeDefined(); + + assertSentryTransaction(transaction, { + transaction: 'test_transaction', + spans: [ + { + description: 'execute', + op: 'db.graphql', + parent_span_id: parentSpanId, + }, + { + description: 'Query.hello', + op: 'db.graphql.apollo', + parent_span_id: graphqlSpanId, + }, + ], + }); + }); +}); diff --git a/packages/tracing/src/hubextensions.ts b/packages/tracing/src/hubextensions.ts index cfc886932696..0884e7eaadab 100644 --- a/packages/tracing/src/hubextensions.ts +++ b/packages/tracing/src/hubextensions.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { getMainCarrier, Hub } from '@sentry/hub'; import { ClientOptions, diff --git a/packages/tracing/src/integrations/index.ts b/packages/tracing/src/integrations/index.ts index 6fb86c1afc78..7522599aa4cf 100644 --- a/packages/tracing/src/integrations/index.ts +++ b/packages/tracing/src/integrations/index.ts @@ -3,6 +3,8 @@ export { Postgres } from './node/postgres'; export { Mysql } from './node/mysql'; export { Mongo } from './node/mongo'; export { Prisma } from './node/prisma'; +export { GraphQL } from './node/graphql'; +export { Apollo } from './node/apollo'; // TODO(v7): Remove this export // Please see `src/index.ts` for more details. diff --git a/packages/tracing/src/integrations/node/apollo.ts b/packages/tracing/src/integrations/node/apollo.ts new file mode 100644 index 000000000000..1450f1431e72 --- /dev/null +++ b/packages/tracing/src/integrations/node/apollo.ts @@ -0,0 +1,101 @@ +import { Hub } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; +import { fill, isThenable, loadModule, logger } from '@sentry/utils'; + +type ApolloResolverGroup = { + [key: string]: () => unknown; +}; + +type ApolloModelResolvers = { + [key: string]: ApolloResolverGroup; +}; + +/** Tracing integration for Apollo */ +export class Apollo implements Integration { + /** + * @inheritDoc + */ + public static id: string = 'Apollo'; + + /** + * @inheritDoc + */ + public name: string = Apollo.id; + + /** + * @inheritDoc + */ + public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + const pkg = loadModule<{ + ApolloServerBase: { + prototype: { + constructSchema: () => unknown; + }; + }; + }>('apollo-server-core'); + + if (!pkg) { + logger.error('Apollo Integration was unable to require apollo-server-core package.'); + return; + } + + /** + * Iterate over resolvers of the ApolloServer instance before schemas are constructed. + */ + fill(pkg.ApolloServerBase.prototype, 'constructSchema', function (orig: () => unknown) { + return function (this: { config: { resolvers: ApolloModelResolvers[] } }) { + const resolvers = Array.isArray(this.config.resolvers) ? this.config.resolvers : [this.config.resolvers]; + + this.config.resolvers = resolvers.map(model => { + Object.keys(model).forEach(resolverGroupName => { + Object.keys(model[resolverGroupName]).forEach(resolverName => { + if (typeof model[resolverGroupName][resolverName] !== 'function') { + return; + } + + wrapResolver(model, resolverGroupName, resolverName, getCurrentHub); + }); + }); + + return model; + }); + + return orig.call(this); + }; + }); + } +} + +/** + * Wrap a single resolver which can be a parent of other resolvers and/or db operations. + */ +function wrapResolver( + model: ApolloModelResolvers, + resolverGroupName: string, + resolverName: string, + getCurrentHub: () => Hub, +): void { + fill(model[resolverGroupName], resolverName, function (orig: () => unknown | Promise) { + return function (this: unknown, ...args: unknown[]) { + const scope = getCurrentHub().getScope(); + const parentSpan = scope?.getSpan(); + const span = parentSpan?.startChild({ + description: `${resolverGroupName}.${resolverName}`, + op: 'db.graphql.apollo', + }); + + const rv = orig.call(this, ...args); + + if (isThenable(rv)) { + return rv.then((res: unknown) => { + span?.finish(); + return res; + }); + } + + span?.finish(); + + return rv; + }; + }); +} diff --git a/packages/tracing/src/integrations/node/graphql.ts b/packages/tracing/src/integrations/node/graphql.ts new file mode 100644 index 000000000000..1f16c7a69e41 --- /dev/null +++ b/packages/tracing/src/integrations/node/graphql.ts @@ -0,0 +1,59 @@ +import { Hub } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; +import { fill, isThenable, loadModule, logger } from '@sentry/utils'; + +/** Tracing integration for graphql package */ +export class GraphQL implements Integration { + /** + * @inheritDoc + */ + public static id: string = 'GraphQL'; + + /** + * @inheritDoc + */ + public name: string = GraphQL.id; + + /** + * @inheritDoc + */ + public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + const pkg = loadModule<{ + [method: string]: (...args: unknown[]) => unknown; + }>('graphql/execution/execute.js'); + + if (!pkg) { + logger.error('GraphQL Integration was unable to require graphql/execution package.'); + return; + } + + fill(pkg, 'execute', function (orig: () => void | Promise) { + return function (this: unknown, ...args: unknown[]) { + const scope = getCurrentHub().getScope(); + const parentSpan = scope?.getSpan(); + + const span = parentSpan?.startChild({ + description: 'execute', + op: 'db.graphql', + }); + + scope?.setSpan(span); + + const rv = orig.call(this, ...args); + + if (isThenable(rv)) { + return rv.then((res: unknown) => { + span?.finish(); + scope?.setSpan(parentSpan); + + return res; + }); + } + + span?.finish(); + scope?.setSpan(parentSpan); + return rv; + }; + }); + } +} diff --git a/packages/tracing/test/integrations/apollo.test.ts b/packages/tracing/test/integrations/apollo.test.ts new file mode 100644 index 000000000000..6b69754edf1d --- /dev/null +++ b/packages/tracing/test/integrations/apollo.test.ts @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import { Hub, Scope } from '@sentry/hub'; + +import { Apollo } from '../../src/integrations/node/apollo'; +import { Span } from '../../src/span'; + +type ApolloResolverGroup = { + [key: string]: () => any; +}; + +type ApolloModelResolvers = { + [key: string]: ApolloResolverGroup; +}; + +class ApolloServerBase { + config: { + resolvers: ApolloModelResolvers[]; + }; + + constructor() { + this.config = { + resolvers: [ + { + Query: { + res_1(..._args: unknown[]) { + return 'foo'; + }, + }, + Mutation: { + res_2(..._args: unknown[]) { + return 'bar'; + }, + }, + }, + ], + }; + + this.constructSchema(); + } + + public constructSchema(..._args: unknown[]) { + return null; + } +} + +// mock for ApolloServer package +jest.mock('@sentry/utils', () => { + const actual = jest.requireActual('@sentry/utils'); + return { + ...actual, + loadModule() { + return { + ApolloServerBase, + }; + }, + }; +}); + +describe('setupOnce', () => { + let scope = new Scope(); + let parentSpan: Span; + let childSpan: Span; + let ApolloServer: ApolloServerBase; + + beforeAll(() => { + new Apollo().setupOnce( + () => undefined, + () => new Hub(undefined, scope), + ); + + ApolloServer = new ApolloServerBase(); + }); + + beforeEach(() => { + scope = new Scope(); + parentSpan = new Span(); + childSpan = parentSpan.startChild(); + jest.spyOn(scope, 'getSpan').mockReturnValueOnce(parentSpan); + jest.spyOn(scope, 'setSpan'); + jest.spyOn(parentSpan, 'startChild').mockReturnValueOnce(childSpan); + jest.spyOn(childSpan, 'finish'); + }); + + it('should wrap a simple resolver', () => { + ApolloServer.config.resolvers[0]?.['Query']?.['res_1']?.(); + expect(scope.getSpan).toBeCalled(); + expect(parentSpan.startChild).toBeCalledWith({ + description: 'Query.res_1', + op: 'db.graphql.apollo', + }); + expect(childSpan.finish).toBeCalled(); + }); + + it('should wrap another simple resolver', () => { + ApolloServer.config.resolvers[0]?.['Mutation']?.['res_2']?.(); + expect(scope.getSpan).toBeCalled(); + expect(parentSpan.startChild).toBeCalledWith({ + description: 'Mutation.res_2', + op: 'db.graphql.apollo', + }); + expect(childSpan.finish).toBeCalled(); + }); +}); diff --git a/packages/tracing/test/integrations/graphql.test.ts b/packages/tracing/test/integrations/graphql.test.ts new file mode 100644 index 000000000000..49db69479097 --- /dev/null +++ b/packages/tracing/test/integrations/graphql.test.ts @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import { Hub, Scope } from '@sentry/hub'; + +import { GraphQL } from '../../src/integrations/node/graphql'; +import { Span } from '../../src/span'; + +const GQLExecute = { + execute() { + return Promise.resolve(); + }, +}; + +// mock for 'graphql/execution/execution.js' package +jest.mock('@sentry/utils', () => { + const actual = jest.requireActual('@sentry/utils'); + return { + ...actual, + loadModule() { + return GQLExecute; + }, + }; +}); + +describe('setupOnce', () => { + let scope = new Scope(); + let parentSpan: Span; + let childSpan: Span; + + beforeAll(() => { + new GraphQL().setupOnce( + () => undefined, + () => new Hub(undefined, scope), + ); + }); + + beforeEach(() => { + scope = new Scope(); + parentSpan = new Span(); + childSpan = parentSpan.startChild(); + jest.spyOn(scope, 'getSpan').mockReturnValueOnce(parentSpan); + jest.spyOn(scope, 'setSpan'); + jest.spyOn(parentSpan, 'startChild').mockReturnValueOnce(childSpan); + jest.spyOn(childSpan, 'finish'); + }); + + it('should wrap execute method', async () => { + await GQLExecute.execute(); + expect(scope.getSpan).toBeCalled(); + expect(parentSpan.startChild).toBeCalledWith({ + description: 'execute', + op: 'db.graphql', + }); + expect(childSpan.finish).toBeCalled(); + expect(scope.setSpan).toHaveBeenCalledTimes(2); + }); +}); diff --git a/yarn.lock b/yarn.lock index 1ee8f4cd1349..a96b3bc98fe3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -201,6 +201,37 @@ dependencies: tslib "^2.0.0" +"@apollo/protobufjs@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@apollo/protobufjs/-/protobufjs-1.2.2.tgz#4bd92cd7701ccaef6d517cdb75af2755f049f87c" + integrity sha512-vF+zxhPiLtkwxONs6YanSt1EpwpGilThpneExUN5K3tCymuxNnVq2yojTvnpRjv2QfsEIt/n7ozPIIzBLwGIDQ== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.0" + "@types/node" "^10.1.0" + long "^4.0.0" + +"@apollographql/apollo-tools@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.5.3.tgz#ba241d50f0849150ca0de54fd2927160033bc0bc" + integrity sha512-VcsXHfTFoCodDAgJZxN04GdFK1kqOhZQnQY/9Fa147P+I8xfvOSz5d+lKAPB+hwSgBNyd7ncAKGIs4+utbL+yA== + +"@apollographql/graphql-playground-html@1.6.29": + version "1.6.29" + resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz#a7a646614a255f62e10dcf64a7f68ead41dec453" + integrity sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA== + dependencies: + xss "^1.0.8" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -2756,6 +2787,41 @@ stream-events "^1.0.1" xdg-basedir "^4.0.0" +"@graphql-tools/merge@8.2.9": + version "8.2.9" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.2.9.tgz#f4bb6ca58d0d89dbfa4fded6a1457bb359de1450" + integrity sha512-mHRrqMc1NTL6MALBQK1DmAzSxJIKoaCaW7ZCk5bRGzVj/MNQz3OsqlDb/+t9/ONT0V+WI/uxBFsrLMwa4p6L7A== + dependencies: + "@graphql-tools/utils" "8.6.8" + tslib "~2.3.0" + +"@graphql-tools/mock@^8.1.2": + version "8.6.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/mock/-/mock-8.6.7.tgz#2beb4bce7a853d9c5894441643bf3219c19016cf" + integrity sha512-KQ/L0kddMgEQG0mATI4vWdeL7d7SJg8Y+SaJUWClLPmTcepZ0xJA9zTXBheklnj3XOCwgAV0Fhzx1pSzYGGpfw== + dependencies: + "@graphql-tools/schema" "8.3.9" + "@graphql-tools/utils" "8.6.8" + fast-json-stable-stringify "^2.1.0" + tslib "~2.3.0" + +"@graphql-tools/schema@8.3.9", "@graphql-tools/schema@^8.0.0": + version "8.3.9" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.3.9.tgz#2b83464a0ef083c92d7076da0fa5939f2f5a1e34" + integrity sha512-9YFCzn0sYAGTWhZrVYY/neK5cie3s0dNm7Qq38tkhOh2ME5BtHW/8ZIq+UrLGKsBYwa+Qjb/UojGWUm2yG/z6Q== + dependencies: + "@graphql-tools/merge" "8.2.9" + "@graphql-tools/utils" "8.6.8" + tslib "~2.3.0" + value-or-promise "1.0.11" + +"@graphql-tools/utils@8.6.8": + version "8.6.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.6.8.tgz#a0824ed5810f66c504df4e97c5900786ac0c260e" + integrity sha512-EdUUeKi/wp/UvuknyINpQ/uXDqTM3qxPPPDIq5RpfW0zQOeCvbZcx8xHoMox0TYKvKtg3zoB7aprUtoW+iZLxw== + dependencies: + tslib "~2.3.0" + "@grpc/grpc-js@~1.2.0": version "1.2.12" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.12.tgz#0153f27512acf69184bb52c0a1035ca91d6c14b0" @@ -3128,6 +3194,11 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@josephg/resolvable@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@josephg/resolvable/-/resolvable-1.0.1.tgz#69bc4db754d79e1a2f17a650d3466e038d94a5eb" + integrity sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg== + "@jridgewell/resolve-uri@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" @@ -4470,6 +4541,13 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== +"@types/accepts@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" + integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== + dependencies: + "@types/node" "*" + "@types/aria-query@^4.2.0": version "4.2.1" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.1.tgz#78b5433344e2f92e8b306c06a5622c50c245bf6b" @@ -4533,6 +4611,14 @@ "@types/connect" "*" "@types/node" "*" +"@types/body-parser@1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/bson@*": version "4.2.0" resolved "https://registry.yarnpkg.com/@types/bson/-/bson-4.2.0.tgz#a2f71e933ff54b2c3bf267b67fa221e295a33337" @@ -4574,7 +4660,7 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== -"@types/cors@^2.8.12": +"@types/cors@2.8.12", "@types/cors@^2.8.12": version "2.8.12" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== @@ -4809,6 +4895,15 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/express-serve-static-core@4.17.28": + version "4.17.28" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/express-serve-static-core@^4.17.18": version "4.17.19" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d" @@ -4818,6 +4913,16 @@ "@types/qs" "*" "@types/range-parser" "*" +"@types/express@4.17.13": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/express@^4.17.1", "@types/express@^4.17.2": version "4.17.11" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.11.tgz#debe3caa6f8e5fcda96b47bd54e2f40c4ee59545" @@ -5018,11 +5123,21 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@>= 8", "@types/node@>=10.0.0", "@types/node@>=12.12.47", "@types/node@>=13.7.0": +"@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "17.0.38" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.38.tgz#f8bb07c371ccb1903f3752872c89f44006132947" integrity sha512-5jY9RhV7c0Z4Jy09G+NIDTsCZ5G0L5n+Z+p+Y7t5VJHM30bgwzSjVtlcBxqAj+6L/swIlvtOSzr8rBk/aNyV2g== +"@types/node@>=10.0.0": + version "17.0.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" + integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== + +"@types/node@^10.1.0": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + "@types/node@^14.6.4": version "14.14.37" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" @@ -5700,6 +5815,14 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +accepts@^1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -5708,14 +5831,6 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -6020,6 +6135,108 @@ anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +apollo-datasource@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-3.3.1.tgz#a1168dd68371930de3ed4245ad12fa8600efe2cc" + integrity sha512-Z3a8rEUXVPIZ1p8xrFL8bcNhWmhOmovgDArvwIwmJOBnh093ZpRfO+ESJEDAN4KswmyzCLDAwjsW4zQOONdRUw== + dependencies: + apollo-server-caching "^3.3.0" + apollo-server-env "^4.2.1" + +apollo-reporting-protobuf@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.3.1.tgz#8c8761f9ac4375fd8490262d6144057cec6ce0b3" + integrity sha512-tyvj3Vj71TCh6c8PtdHOLgHHBSJ05DF/A/Po3q8yfHTBkOPcOJZE/GGN/PT/pwKg7HHxKcAeHDw7+xciVvGx0w== + dependencies: + "@apollo/protobufjs" "1.2.2" + +apollo-server-caching@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/apollo-server-caching/-/apollo-server-caching-3.3.0.tgz#f501cbeb820a4201d98c2b768c085f22848d9dc5" + integrity sha512-Wgcb0ArjZ5DjQ7ID+tvxUcZ7Yxdbk5l1MxZL8D8gkyjooOkhPNzjRVQ7ubPoXqO54PrOMOTm1ejVhsF+AfIirQ== + dependencies: + lru-cache "^6.0.0" + +apollo-server-core@^3.6.7: + version "3.6.7" + resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-3.6.7.tgz#6a52ce48fe1a79da9691c3fff4ea1031e9225602" + integrity sha512-OnZ9vu7LrYy2rvEu+nbgqucw6VyTSIPAEjK87c4rkzlVOxpwtGUaQ4FMWD9zBIj7yLq9q22b638E8LdYoaTAjQ== + dependencies: + "@apollographql/apollo-tools" "^0.5.3" + "@apollographql/graphql-playground-html" "1.6.29" + "@graphql-tools/mock" "^8.1.2" + "@graphql-tools/schema" "^8.0.0" + "@josephg/resolvable" "^1.0.0" + apollo-datasource "^3.3.1" + apollo-reporting-protobuf "^3.3.1" + apollo-server-caching "^3.3.0" + apollo-server-env "^4.2.1" + apollo-server-errors "^3.3.1" + apollo-server-plugin-base "^3.5.2" + apollo-server-types "^3.5.2" + async-retry "^1.2.1" + fast-json-stable-stringify "^2.1.0" + graphql-tag "^2.11.0" + lodash.sortby "^4.7.0" + loglevel "^1.6.8" + lru-cache "^6.0.0" + sha.js "^2.4.11" + uuid "^8.0.0" + +apollo-server-env@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-4.2.1.tgz#ea5b1944accdbdba311f179e4dfaeca482c20185" + integrity sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g== + dependencies: + node-fetch "^2.6.7" + +apollo-server-errors@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz#ba5c00cdaa33d4cbd09779f8cb6f47475d1cd655" + integrity sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA== + +apollo-server-express@^3.6.7: + version "3.6.7" + resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-3.6.7.tgz#9b83ba088cc40ea98bc0f903f9bfed41ea98fdf8" + integrity sha512-B4gH5j8t3XxTCIa9bl7Iq/F3YFzMxX/LV4Sc+L/3xkHm648u576G5Lkskl8HsoTGSzzyeVcVsPDoYHiBjCAN0Q== + dependencies: + "@types/accepts" "^1.3.5" + "@types/body-parser" "1.19.2" + "@types/cors" "2.8.12" + "@types/express" "4.17.13" + "@types/express-serve-static-core" "4.17.28" + accepts "^1.3.5" + apollo-server-core "^3.6.7" + apollo-server-types "^3.5.2" + body-parser "^1.19.0" + cors "^2.8.5" + parseurl "^1.3.3" + +apollo-server-plugin-base@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-3.5.2.tgz#18a08353f5ea207a07f2c1e66dd7ef2335ce8c84" + integrity sha512-SwIf1waDmNDb0kmn57QR++InwK6Iv/X2slpm/aFIoqFBe91r6uJfakJvQZuh8dLEgk68gxqFsT8zHRpxBclE+g== + dependencies: + apollo-server-types "^3.5.2" + +apollo-server-types@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/apollo-server-types/-/apollo-server-types-3.5.2.tgz#903d499a70b9010764cbab4d704ddb8c086aa06f" + integrity sha512-vhcbIWsBkoNibABOym4AAPBoNDjokhjUQokKYdwZMeqrb850PMQdNJFrGyXT5onP408Ghv4O8PfgBuPQmeJhVQ== + dependencies: + apollo-reporting-protobuf "^3.3.1" + apollo-server-caching "^3.3.0" + apollo-server-env "^4.2.1" + +apollo-server@^3.6.7: + version "3.6.7" + resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-3.6.7.tgz#6779de1dfd33afce08dff905e561f628d71ad83d" + integrity sha512-WERZqaVCkZJMoOc9y692NribgNtKbHDjOwiAmgXI2WBlON2oUvCwgxPvsMg+bXVpQx4itrMyj31a2N6BeKmbmQ== + dependencies: + apollo-server-core "^3.6.7" + apollo-server-express "^3.6.7" + express "^4.17.1" + app-module-path@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" @@ -6357,6 +6574,13 @@ async-promise-queue@^1.0.3, async-promise-queue@^1.0.5: async "^2.4.1" debug "^2.6.8" +async-retry@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" + integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== + dependencies: + retry "0.13.1" + async-retry@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" @@ -9825,6 +10049,11 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +cssfilter@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" + integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4= + cssnano-preset-default@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" @@ -12512,7 +12741,7 @@ fast-glob@^3.2.4: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -13843,6 +14072,18 @@ graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= +graphql-tag@^2.11.0: + version "2.12.6" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" + integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== + dependencies: + tslib "^2.1.0" + +graphql@^16.3.0: + version "16.3.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05" + integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A== + graphviz@0.0.9: version "0.0.9" resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.9.tgz#0bbf1df588c6a92259282da35323622528c4bbc4" @@ -19597,7 +19838,7 @@ parseuri@0.0.6: resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@^1.3.3, parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -21933,6 +22174,11 @@ retry@0.12.0, retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +retry@0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + retry@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" @@ -22476,7 +22722,7 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -sha.js@^2.4.0, sha.js@^2.4.8: +sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -24508,7 +24754,7 @@ tslib@^2.0.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -tslib@^2.3.0, tslib@^2.3.1: +tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@~2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== @@ -25132,6 +25378,11 @@ value-equal@^1.0.1: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== +value-or-promise@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" + integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -25907,6 +26158,14 @@ xmlhttprequest-ssl@~1.5.4: resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= +xss@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.11.tgz#211cb82e95b5071d4c75d597283c021157ebe46a" + integrity sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ== + dependencies: + commander "^2.20.3" + cssfilter "0.0.10" + xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"