: 'babel-jest',\n '^.+\\\\.ts?
: 'babel-jest',\n '^.+\\\\.js
: 'babel-jest',\n },\n};\n","id":"e5d9a6ac-39e1-40c8-b37e-ee05ad859014","is_binary":false,"title":"jest.config.js","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"381l9aom54","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":null},{"code":"{\n \"name\": \"continuous-integration-testing\",\n \"version\": \"0.0.0\",\n \"license\": \"MIT\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"start\": \"ts-node-dev src\u002Findex.ts\",\n \"test\": \"jest test\"\n },\n \"dependencies\": {\n \"@graphql-tools\u002Fdelegate\": \"^10.0.0\",\n \"@graphql-tools\u002Fexecutor-http\": \"^1.0.0\",\n \"@graphql-tools\u002Fstitch\": \"^9.0.0\",\n \"@graphql-tools\u002Fstitching-directives\": \"^3.0.0\",\n \"graphql\": \"^16.6.0\",\n \"graphql-yoga\": \"^5.0.0\"\n },\n \"devDependencies\": {\n \"@babel\u002Fcore\": \"7.24.4\",\n \"@babel\u002Fplugin-proposal-class-properties\": \"7.18.6\",\n \"@babel\u002Fpreset-env\": \"7.24.4\",\n \"@babel\u002Fpreset-typescript\": \"7.24.1\",\n \"@graphql-tools\u002Fmock\": \"^9.0.0\",\n \"@graphql-tools\u002Futils\": \"^10.0.0\",\n \"@types\u002Fjest\": \"^29.2.6\",\n \"@types\u002Fnode\": \"^20.0.0\",\n \"jest\": \"^29.0.0\",\n \"typescript\": \"5.4.5\"\n }\n}\n","id":"e5a414b6-416b-4ba2-b8af-5a856456f333","is_binary":false,"title":"package.json","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"O86UXKdvjs","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":null},{"code":"{\n \"services\": {\n \"products\": {\n \"url\": \"http:\u002F\u002Flocalhost:4001\u002Fgraphql\"\n },\n \"reviews\": {\n \"url\": \"http:\u002F\u002Flocalhost:4002\u002Fgraphql\"\n },\n \"users\": {\n \"url\": \"http:\u002F\u002Flocalhost:4003\u002Fgraphql\"\n }\n }\n}\n","id":"59c78843-639f-41f6-9657-25f27d4e672f","is_binary":false,"title":"config.json","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"i_vRcv2A1g","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"n9NhxhCy-"},{"code":"import { createServer } from 'node:http';\nimport { createYoga } from 'graphql-yoga';\nimport { buildGatewaySchema, buildSubschemaConfigs } from '.\u002Fschema_builder';\n\nconst schema = buildGatewaySchema(buildSubschemaConfigs());\n\nconst server = createServer(createYoga({ schema }));\n\nserver.listen(4000, () =\u003E console.log('gateway running at http:\u002F\u002Flocalhost:4000\u002Fgraphql'));\n","id":"8fcee424-c0c4-4597-a9b6-2fa4347e0ee5","is_binary":false,"title":"index.ts","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"xpl0lwpUL6","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"n9NhxhCy-"},{"code":"directive @key(selectionSet: String!) on OBJECT\ndirective @merge(\n keyField: String\n keyArg: String\n additionalArgs: String\n key: [String!]\n argsExpr: String\n) on FIELD_DEFINITION\ndirective @computed(selectionSet: String!) on FIELD_DEFINITION\n\ntype Product {\n upc: ID!\n name: String!\n price: Int!\n weight: Int!\n}\n\ntype Query {\n products(upcs: [ID!]!): [Product]! @merge(keyField: \"upc\")\n _sdl: String!\n}\n","id":"7866dee4-5f00-48f3-8204-dcabffc39b34","is_binary":false,"title":"products.graphql","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"HKCvUEBKk9","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"eVV3OuhDf3"},{"code":"directive @key(selectionSet: String!) on OBJECT\ndirective @merge(\n keyField: String\n keyArg: String\n additionalArgs: String\n key: [String!]\n argsExpr: String\n) on FIELD_DEFINITION\ndirective @computed(selectionSet: String!) on FIELD_DEFINITION\n\ntype Review {\n id: ID!\n body: String\n author: User\n product: Product\n}\n\ntype User {\n id: ID!\n totalReviews: Int!\n reviews: [Review]\n}\n\ntype Product {\n upc: ID!\n reviews: [Review]\n}\n\ntype Query {\n review(id: ID!): Review\n _users(ids: [ID!]!): [User]! @merge(keyField: \"id\")\n _products(upcs: [ID!]!): [Product]! @merge(keyField: \"upc\")\n _sdl: String!\n}\n","id":"1a60f1ff-d944-4aec-ba2e-a4c803f5cb92","is_binary":false,"title":"reviews.graphql","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"6k4mvI1Ao6","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"eVV3OuhDf3"},{"code":"directive @key(selectionSet: String!) on OBJECT\ndirective @merge(\n keyField: String\n keyArg: String\n additionalArgs: String\n key: [String!]\n argsExpr: String\n) on FIELD_DEFINITION\ndirective @computed(selectionSet: String!) on FIELD_DEFINITION\n\ntype User {\n id: ID!\n name: String!\n username: String!\n}\n\ntype Query {\n user(id: ID!): User @merge(keyField: \"id\")\n _sdl: String!\n}\n","id":"eb3bff29-abe9-4137-bd73-2d3c52c018e6","is_binary":false,"title":"users.graphql","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"4VfuPUeJdI","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"eVV3OuhDf3"},{"code":"import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { buildSchema } from 'graphql';\nimport { SubschemaConfig } from '@graphql-tools\u002Fdelegate';\nimport { buildHTTPExecutor } from '@graphql-tools\u002Fexecutor-http';\nimport { stitchSchemas } from '@graphql-tools\u002Fstitch';\nimport { stitchingDirectives } from '@graphql-tools\u002Fstitching-directives';\nimport config from '.\u002Fconfig.json';\n\nexport function buildSubschemaConfigs(): Record\u003Cstring, SubschemaConfig\u003E {\n return Object.entries(config.services).reduce((memo, [name, settings]) =\u003E {\n const sdl = readFileSync(join(__dirname, `.\u002Fremote_schemas\u002F${name}.graphql`), 'utf-8');\n memo[name] = {\n schema: buildSchema(sdl),\n executor: buildHTTPExecutor({ endpoint: settings.url }),\n batch: true,\n };\n return memo;\n }, {});\n}\n\nexport function buildGatewaySchema(subschemasByName: Record\u003Cstring, SubschemaConfig\u003E) {\n const { stitchingDirectivesTransformer } = stitchingDirectives();\n\n return stitchSchemas({\n subschemaConfigTransforms: [stitchingDirectivesTransformer],\n subschemas: Object.values(subschemasByName),\n });\n}\n","id":"a7feec24-6194-4cc6-9a69-ddbad2d37f8e","is_binary":false,"title":"schema_builder.ts","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"Pdcz4QmApb","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"n9NhxhCy-"},{"code":"import { queryMockedGateway } from '.\u002Ftest_helper';\n\ndescribe('gateway schema', () =\u003E {\n const USER_QUERY = \u002F* GraphQL *\u002F `\n {\n user(id: \"1\") {\n # id \u003C\u003C must work without this selected\n name\n username\n reviews {\n # id \u003C\u003C must work without this selected\n body\n product {\n # upc \u003C\u003C must work without this selected\n name\n price\n weight\n }\n }\n }\n }\n `;\n\n test('resolves users -\u003E reviews -\u003E products', async () =\u003E {\n const { data } = await queryMockedGateway(USER_QUERY);\n\n expect(data).toEqual({\n user: {\n name: 'users-value',\n username: 'hansolo',\n reviews: [\n {\n body: 'great',\n product: {\n name: 'gizmo',\n price: 23,\n weight: 23,\n },\n },\n ],\n },\n });\n });\n\n const REVIEW_QUERY = \u002F* GraphQL *\u002F `\n {\n review(id: \"1\") {\n # id \u003C\u003C must work without this selected\n body\n author {\n # id \u003C\u003C must work without this selected\n name\n username\n }\n product {\n # upc \u003C\u003C must work without this selected\n name\n price\n weight\n }\n }\n }\n `;\n\n test('resolves review -\u003E author + product', async () =\u003E {\n const { data } = await queryMockedGateway(REVIEW_QUERY);\n\n expect(data).toEqual({\n review: {\n body: 'great',\n author: {\n name: 'users-value',\n username: 'hansolo',\n },\n product: {\n name: 'gizmo',\n price: 23,\n weight: 23,\n },\n },\n });\n });\n\n const PRODUCTS_QUERY = \u002F* GraphQL *\u002F `\n {\n products(upcs: [\"2\"]) {\n # upc \u003C\u003C must work without this selected\n name\n price\n weight\n reviews {\n # id \u003C\u003C must work without this selected\n body\n author {\n # id \u003C\u003C must work without this selected\n name\n username\n }\n }\n }\n }\n `;\n\n test('resolves products -\u003E reviews -\u003E user', async () =\u003E {\n const { data } = await queryMockedGateway(PRODUCTS_QUERY);\n\n expect(data).toEqual({\n products: [\n {\n name: 'widget',\n price: 23,\n weight: 23,\n reviews: [\n {\n body: 'awful',\n author: {\n name: 'users-value',\n username: 'yoda',\n },\n },\n ],\n },\n ],\n });\n });\n});\n","id":"e3b2ab5d-c32e-4385-b4f2-831ef60cd2ea","is_binary":false,"title":"gateway.test.ts","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"lKClRkqlPZ","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"l-Av8soEN1"},{"code":"const products = [\n { upc: '1', name: 'gizmo' },\n { upc: '2', name: 'widget' },\n];\n\nexport const resolvers = {\n Query: {\n products: (_, { upcs }) =\u003E upcs.map(upc =\u003E products.find(p =\u003E p.upc === upc)),\n },\n};\n\nexport const mocks = {\n Int: () =\u003E 23,\n};\n","id":"9e71768a-178f-4e35-9f1d-b8fafaaaa730","is_binary":false,"title":"products.ts","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"pEaFJ8c06A","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"159Hl9eFrO"},{"code":"import { IResolvers } from '@graphql-tools\u002Futils';\n\nconst reviews = [\n { id: '1', body: 'great', author: { id: '1' }, product: { upc: '1' } },\n { id: '2', body: 'awful', author: { id: '2' }, product: { upc: '2' } },\n];\n\nexport const resolvers: IResolvers = {\n Query: {\n review: (_, { id }) =\u003E reviews.find(r =\u003E r.id === id),\n _users: (_, { ids }) =\u003E ids.map((id: string) =\u003E ({ id })),\n _products: (_, { upcs }) =\u003E upcs.map((upc: string) =\u003E ({ upc })),\n },\n Product: {\n reviews: p =\u003E reviews.filter(r =\u003E r.product.upc === p.upc),\n },\n User: {\n reviews: u =\u003E reviews.filter(r =\u003E r.author.id === u.id),\n },\n};\n","id":"138f59a2-df54-42af-8ddf-8eff77b41bd7","is_binary":false,"title":"reviews.ts","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"zbce8HTYKSG","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"159Hl9eFrO"},{"code":"const users = [\n { id: '1', username: 'hansolo' },\n { id: '2', username: 'yoda' },\n];\n\nexport const resolvers = {\n Query: {\n user: (_, { id }) =\u003E users.find(u =\u003E u.id === id),\n },\n};\n","id":"a1199068-392c-4cf8-b295-40d90c869b4a","is_binary":false,"title":"users.ts","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"OunvYuLQIOt","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"159Hl9eFrO"},{"code":"import { graphql } from 'graphql';\nimport { createSchema } from 'graphql-yoga';\nimport { addMocksToSchema, IMocks } from '@graphql-tools\u002Fmock';\nimport { stitchingDirectives } from '@graphql-tools\u002Fstitching-directives';\nimport { IResolvers, printSchemaWithDirectives } from '@graphql-tools\u002Futils';\nimport { buildGatewaySchema, buildSubschemaConfigs } from '..\u002Fsrc\u002Fschema_builder';\nimport * as productsFixtures from '.\u002Fmock_services\u002Fproducts';\nimport * as reviewsFixtures from '.\u002Fmock_services\u002Freviews';\nimport * as usersFixtures from '.\u002Fmock_services\u002Fusers';\n\n\u002F\u002F Setup a mapping of test fixtures by service name\n\ninterface Fixture {\n resolvers?: IResolvers;\n mocks?: IMocks;\n}\n\nconst fixturesByName: Record\u003Cstring, Fixture\u003E = {\n products: productsFixtures,\n reviews: reviewsFixtures,\n users: usersFixtures,\n};\n\n\u002F\u002F Get the actual subschemas built for the production app:\nconst subschemaConfigs = buildSubschemaConfigs();\n\n\u002F\u002F Reconfigure the subschema configurations for testing...\n\u002F\u002F makes all subschemas locally-executable,\n\u002F\u002F and sets them up with mocked fixture data to test with.\nObject.entries(subschemaConfigs).forEach(([name, subschemaConfig]) =\u003E {\n const fixtures = fixturesByName[name] || {};\n\n const { stitchingDirectivesValidator } = stitchingDirectives();\n\n \u002F\u002F build all of the base schemas into locally-executable test schemas\n \u002F\u002F apply mock service resolvers to give them some simple record fixtures\n const schema = stitchingDirectivesValidator(\n createSchema({\n typeDefs: printSchemaWithDirectives(subschemaConfig.schema),\n resolvers: fixtures.resolvers,\n }),\n );\n\n \u002F\u002F apply mocks to fill in missing values\n \u002F\u002F anything without a resolver or fixture data\n \u002F\u002F gets filled in with a predictable service-specific scalar\n subschemaConfig.schema = addMocksToSchema({\n preserveResolvers: true,\n schema,\n mocks: {\n String: () =\u003E `${name}-value`,\n ...(fixtures.mocks || {}),\n },\n });\n\n \u002F\u002F remove all executors (run everything as a locally-executable schema)\n delete subschemaConfig.executor;\n});\n\n\u002F\u002F Run the gateway builder using the mocked subservices\n\u002F\u002F this gives the complete stitched gateway talking to mocked services.\nconst mockedGateway = buildGatewaySchema(subschemaConfigs);\n\nexport function queryMockedGateway(query: string, variables: any = {}) {\n return graphql({\n schema: mockedGateway,\n source: query,\n variableValues: variables,\n });\n}\n","id":"05bcbfcd-ca0e-4908-9021-56b7a544f75f","is_binary":false,"title":"test_helper.ts","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"Sma0KChvuLV","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":"l-Av8soEN1"},{"code":"{\n \"compilerOptions\": {\n \"target\": \"esnext\",\n \"moduleResolution\": \"node\",\n \"module\": \"commonjs\",\n \"sourceMap\": true,\n \"allowSyntheticDefaultImports\": true,\n \"esModuleInterop\": true,\n \"lib\": [\"esnext\", \"DOM\"],\n \"skipLibCheck\": true,\n \"resolveJsonModule\": true\n }\n}\n","id":"f6a47d02-9a9c-4f70-965b-eaf3a9a34d3f","is_binary":false,"title":"tsconfig.json","sha":null,"inserted_at":"2024-04-10T20:53:24","updated_at":"2024-04-10T20:53:24","upload_id":null,"shortid":"LtJqQzmaGLf","source_id":"5dea8a12-246e-4497-bc9f-7a92a970fd0e","directory_shortid":null}],"is_frozen":false,"fork_count":0,"external_resources":[],"is_sse":false,"free_plan_editing_restricted":false,"room_id":null,"forked_template":null,"ai_consent":false,"npm_registries":[],"authorization":"read","owned":false,"original_git_commit_sha":null,"collection":false,"base_git":null,"v2":false,"user_liked":false,"updated_at":"2024-04-10T20:53:24","custom_template":{"id":"619bdd8e-2a1c-4bcb-95d7-74cdd68dbce6","title":"continuous-integration-testing","color":"#61DAFB","v2":false,"url":null,"published":false,"icon_url":"github","official":false},"description":null,"picks":[]};