: 'babel-jest',\n '^.+\\\\.ts?
: 'babel-jest',\n '^.+\\\\.js
: 'babel-jest',\n },\n};\n","id":"70d9ddd1-f0a9-46ec-9a3d-87dae6419105","is_binary":false,"title":"jest.config.js","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"4UBPTaEvPm","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","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.25.8\",\n \"@babel\u002Fplugin-proposal-class-properties\": \"7.18.6\",\n \"@babel\u002Fpreset-env\": \"7.25.8\",\n \"@babel\u002Fpreset-typescript\": \"7.25.7\",\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.6.3\"\n }\n}\n","id":"32097b19-f866-497c-8742-4ee58dc79209","is_binary":false,"title":"package.json","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"SmsqNNOxoC","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","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":"937e97bc-9166-4c0d-ab4a-142e2a3c66bf","is_binary":false,"title":"config.json","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"TIBrizIyNN","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"XwM1fKKZl"},{"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":"363c20b8-e473-449a-8526-575bd8969a6b","is_binary":false,"title":"index.ts","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"mrsXkXlyLr","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"XwM1fKKZl"},{"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":"643cf388-4479-4777-8d7d-10636b10c1e8","is_binary":false,"title":"products.graphql","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"3SM8_gcoNV","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"7zLsJERO0-"},{"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":"cb9bb776-fdc8-4e59-b216-dbf013981aa3","is_binary":false,"title":"reviews.graphql","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"L1djWaJvuK","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"7zLsJERO0-"},{"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":"8f9bd2e4-337a-40cc-ad7c-27c0f433d962","is_binary":false,"title":"users.graphql","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"RgSCzZVZnq","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"7zLsJERO0-"},{"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":"9bc36caa-ec37-492c-bfd8-43153e83c911","is_binary":false,"title":"schema_builder.ts","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"zN4CLitMMc","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"XwM1fKKZl"},{"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":"06726fc2-1731-4c51-89d2-d01fa51ee965","is_binary":false,"title":"gateway.test.ts","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"7NpOvWAKgm","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"pLxVa0kkhI"},{"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":"6e1f7d3d-d580-43d8-b217-606b28d43c06","is_binary":false,"title":"products.ts","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"mWvrvAouiV","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"brH_6qDQ7U"},{"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":"3cf3e00a-e479-4ef8-8bd7-2b577471f5f4","is_binary":false,"title":"reviews.ts","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"54WbRLSSVDt","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"brH_6qDQ7U"},{"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":"25073ab1-eb96-4d64-a6dc-59c803e836fd","is_binary":false,"title":"users.ts","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"82qOGwWMPrJ","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"brH_6qDQ7U"},{"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":"f6528d1b-259e-48fe-8db9-dfad8d8082e8","is_binary":false,"title":"test_helper.ts","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"cy2y6gOipX5","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":"pLxVa0kkhI"},{"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":"8794d4eb-7f89-4d8d-88e1-d5fcc95e1c88","is_binary":false,"title":"tsconfig.json","sha":null,"inserted_at":"2024-10-12T00:55:47","updated_at":"2024-10-12T00:55:47","upload_id":null,"shortid":"aDdDLeyBUX4","source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","directory_shortid":null}],"source_id":"648a5dc1-b617-43b4-b3c2-5831bfbdf1a6","base_git":null,"like_count":0,"id":"sbo9sj","npm_registries":[],"free_plan_editing_restricted":false,"settings":{"ai_consent":null},"title":"continuous-integration-testing","screenshot_url":"https:\u002F\u002Fscreenshots.codesandbox.io\u002Fsbo9sj\u002F61.png","pr_number":null,"template":"create-react-app","permissions":{"prevent_sandbox_export":false,"prevent_sandbox_leaving":false},"npm_dependencies":{},"authorization":"read","forked_template":null,"forked_from_sandbox":null,"ai_consent":false,"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},"room_id":null};