diff --git a/packages/plugins/typescript/effect/src/visitor.ts b/packages/plugins/typescript/effect/src/visitor.ts index 00868c81a8..c0de826f59 100644 --- a/packages/plugins/typescript/effect/src/visitor.ts +++ b/packages/plugins/typescript/effect/src/visitor.ts @@ -10,21 +10,71 @@ import { export interface EffectPluginConfig extends ClientSideBasePluginConfig {} -const additionalStaticContent = (documentMode: DocumentMode) => ` -export type GraphQLSuccessResponse = { - body: ExecutionResult & { data: A }; - headers: Record; +const clientCode = `export type GraphQLSuccessResponse = Pick< + Http.response.ClientResponse, + 'status' | 'headers' +> & { + readonly body: ExecutionResult & { readonly data: A }; }; -export type GraphQLErrorResponse = { - body: Omit; - headers: Record; +export type GraphQLErrorResponse = Pick & { + readonly body: Pick; }; export class MissingDataGraphQLResponseError extends Data.TaggedError( 'MissingDataGraphQLResponseError', ) {} +const headers = { + // https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed + Accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', + 'Content-Type': 'application/json', +}; + +export class GraphQLClient extends Context.Tag('GraphQLClient')< + GraphQLClient, + Http.client.Client< + never, + Http.error.HttpClientError | MissingDataGraphQLResponseError, + GraphQLSuccessResponse + > +>() { + static fromDefaultClient(client: Http.client.Client.Default): Layer.Layer { + return Layer.succeed( + GraphQLClient, + client.pipe( + Http.client.mapRequest(Http.request.setHeaders(headers)), + Http.client.filterStatusOk, + Http.client.mapEffectScoped(res => + Effect.flatMap(res.json, _ => { + const body = _ as ExecutionResult; + return body.data + ? Effect.succeed({ ...res, body: { ...body, data: body.data } }) + : Effect.fail(new MissingDataGraphQLResponseError({ ...res, body })); + }), + ), + ), + ); + } + + static Live: Layer.Layer = Layer.unwrapEffect( + Effect.map(Http.client.Client, GraphQLClient.fromDefaultClient), + ); + + static fromEndpoint( + endpoint: string, + ): Layer.Layer { + return Layer.unwrapEffect( + Effect.map(Http.client.Client, client => + GraphQLClient.fromDefaultClient( + Http.client.mapRequest(client, Http.request.prependUrl(endpoint)), + ), + ), + ); + } +}`; + +const additionalStaticContent = (documentMode: DocumentMode) => ` export type GraphQLOperationOptions = { preferredOpName?: string; }; @@ -34,28 +84,7 @@ type GraphQLOperationArgs = { fallbackOperationName: string; }; -// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed -const headers = { - Accept: - "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", - "Content-Type": "application/json", -}; - -const makeGraphQLClient = () => - Effect.map(Http.client.Client, client => - Http.client.mapRequest(client, Http.request.setHeaders(headers)).pipe( - Http.client.filterStatusOk, - Http.client.mapEffectScoped(res => - Effect.flatMap(res.json, _ => { - const result = { body: _ as ExecutionResult, headers: res.headers }; - - return result.body.data - ? Effect.succeed(result as GraphQLSuccessResponse) - : Effect.fail(new MissingDataGraphQLResponseError(result)); - }), - ), - ), - ); +${clientCode} const makeGraphQLOperation = ({ document, fallbackOperationName }: GraphQLOperationArgs) => @@ -63,7 +92,7 @@ const makeGraphQLOperation = const operationName = opts?.preferredOpName ?? fallbackOperationName; const query = ${documentMode === DocumentMode.string ? 'document' : 'print(document)'}; - return Effect.flatMap(makeGraphQLClient(), client => + return Effect.flatMap(GraphQLClient, client => Http.request.post('').pipe( Http.request.jsonBody({ query, @@ -71,6 +100,7 @@ const makeGraphQLOperation = variables, }), Effect.flatMap(client), + Effect.map(_ => _ as GraphQLSuccessResponse), ), ); }; @@ -113,8 +143,10 @@ export class EffectVisitor extends ClientSideBaseVisitor< [ createNamedImport( [ + ['Context', 'value'], ['Data', 'value'], ['Effect', 'value'], + ['Layer', 'value'], ], 'effect', ), diff --git a/packages/plugins/typescript/effect/tests/__snapshots__/effect.spec.ts.snap b/packages/plugins/typescript/effect/tests/__snapshots__/effect.spec.ts.snap index 2da1a3c6d8..0ba365c920 100644 --- a/packages/plugins/typescript/effect/tests/__snapshots__/effect.spec.ts.snap +++ b/packages/plugins/typescript/effect/tests/__snapshots__/effect.spec.ts.snap @@ -6,7 +6,7 @@ export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -import { Data, Effect } from 'effect'; +import { Context, Data, Effect, Layer } from 'effect'; import { DocumentNode, ExecutionResult, print } from 'graphql'; import * as Http from '@effect/platform/HttpClient'; import gql from 'graphql-tag'; @@ -240,20 +240,6 @@ export const Feed4Document = gql\` } \`; -export type GraphQLSuccessResponse = { - body: ExecutionResult & { data: A }; - headers: Record; -}; - -export type GraphQLErrorResponse = { - body: Omit; - headers: Record; -}; - -export class MissingDataGraphQLResponseError extends Data.TaggedError( - 'MissingDataGraphQLResponseError', -) {} - export type GraphQLOperationOptions = { preferredOpName?: string; }; @@ -263,36 +249,77 @@ type GraphQLOperationArgs = { fallbackOperationName: string; }; -// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed +export type GraphQLSuccessResponse = Pick< + Http.response.ClientResponse, + 'status' | 'headers' +> & { + readonly body: ExecutionResult & { readonly data: A }; +}; + +export type GraphQLErrorResponse = Pick & { + readonly body: Pick; +}; + +export class MissingDataGraphQLResponseError extends Data.TaggedError( + 'MissingDataGraphQLResponseError', +) {} + const headers = { - Accept: - "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", - "Content-Type": "application/json", + // https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed + Accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', + 'Content-Type': 'application/json', }; -const makeGraphQLClient = () => - Effect.map(Http.client.Client, client => - Http.client.mapRequest(client, Http.request.setHeaders(headers)).pipe( - Http.client.filterStatusOk, - Http.client.mapEffectScoped(res => - Effect.flatMap(res.json, _ => { - const result = { body: _ as ExecutionResult, headers: res.headers }; - - return result.body.data - ? Effect.succeed(result as GraphQLSuccessResponse) - : Effect.fail(new MissingDataGraphQLResponseError(result)); - }), +export class GraphQLClient extends Context.Tag('GraphQLClient')< + GraphQLClient, + Http.client.Client< + never, + Http.error.HttpClientError | MissingDataGraphQLResponseError, + GraphQLSuccessResponse + > +>() { + static fromDefaultClient(client: Http.client.Client.Default): Layer.Layer { + return Layer.succeed( + GraphQLClient, + client.pipe( + Http.client.mapRequest(Http.request.setHeaders(headers)), + Http.client.filterStatusOk, + Http.client.mapEffectScoped(res => + Effect.flatMap(res.json, _ => { + const body = _ as ExecutionResult; + return body.data + ? Effect.succeed({ ...res, body: { ...body, data: body.data } }) + : Effect.fail(new MissingDataGraphQLResponseError({ ...res, body })); + }), + ), ), - ), + ); + } + + static Live: Layer.Layer = Layer.unwrapEffect( + Effect.map(Http.client.Client, GraphQLClient.fromDefaultClient), ); + static fromEndpoint( + endpoint: string, + ): Layer.Layer { + return Layer.unwrapEffect( + Effect.map(Http.client.Client, client => + GraphQLClient.fromDefaultClient( + Http.client.mapRequest(client, Http.request.prependUrl(endpoint)), + ), + ), + ); + } +} + const makeGraphQLOperation = ({ document, fallbackOperationName }: GraphQLOperationArgs) => (variables: Vars, opts?: GraphQLOperationOptions) => { const operationName = opts?.preferredOpName ?? fallbackOperationName; const query = print(document); - return Effect.flatMap(makeGraphQLClient(), client => + return Effect.flatMap(GraphQLClient, client => Http.request.post('').pipe( Http.request.jsonBody({ query, @@ -300,6 +327,7 @@ const makeGraphQLOperation = variables, }), Effect.flatMap(client), + Effect.map(_ => _ as GraphQLSuccessResponse), ), ); }; @@ -321,19 +349,15 @@ export const feed4 = makeGraphQLOperation({ fallbackOperationName: 'feed4', }); -import { Layer } from 'effect'; import { NodeHttpClient } from '@effect/platform-node'; +import { flow } from 'effect'; -const HttpClientLive = Layer.effect( - Http.client.Client, - Effect.map( - Http.client.Client, - Http.client.mapRequest(Http.request.prependUrl('http://localhost:4000/graphql')), +const run = flow( + Effect.provide( + GraphQLClient.fromEndpoint('http://localhost:4000/graphql').pipe(Layer.provide(NodeHttpClient.layer)), ), -).pipe(Layer.provide(NodeHttpClient.layer)); - -const run = (e: Effect.Effect) => - e.pipe(Effect.provide(HttpClientLive), Effect.runPromise); + Effect.runPromise +); async function test() { await run(feed({})); @@ -357,7 +381,7 @@ export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -import { Data, Effect } from 'effect'; +import { Context, Data, Effect, Layer } from 'effect'; import { DocumentNode, ExecutionResult, print } from 'graphql'; import * as Http from '@effect/platform/HttpClient'; /** All built-in and custom scalars, mapped to their actual values */ @@ -590,20 +614,6 @@ export const Feed4Document = \` } \`; -export type GraphQLSuccessResponse = { - body: ExecutionResult & { data: A }; - headers: Record; -}; - -export type GraphQLErrorResponse = { - body: Omit; - headers: Record; -}; - -export class MissingDataGraphQLResponseError extends Data.TaggedError( - 'MissingDataGraphQLResponseError', -) {} - export type GraphQLOperationOptions = { preferredOpName?: string; }; @@ -613,36 +623,77 @@ type GraphQLOperationArgs = { fallbackOperationName: string; }; -// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed +export type GraphQLSuccessResponse = Pick< + Http.response.ClientResponse, + 'status' | 'headers' +> & { + readonly body: ExecutionResult & { readonly data: A }; +}; + +export type GraphQLErrorResponse = Pick & { + readonly body: Pick; +}; + +export class MissingDataGraphQLResponseError extends Data.TaggedError( + 'MissingDataGraphQLResponseError', +) {} + const headers = { - Accept: - "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", - "Content-Type": "application/json", + // https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed + Accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', + 'Content-Type': 'application/json', }; -const makeGraphQLClient = () => - Effect.map(Http.client.Client, client => - Http.client.mapRequest(client, Http.request.setHeaders(headers)).pipe( - Http.client.filterStatusOk, - Http.client.mapEffectScoped(res => - Effect.flatMap(res.json, _ => { - const result = { body: _ as ExecutionResult, headers: res.headers }; - - return result.body.data - ? Effect.succeed(result as GraphQLSuccessResponse) - : Effect.fail(new MissingDataGraphQLResponseError(result)); - }), +export class GraphQLClient extends Context.Tag('GraphQLClient')< + GraphQLClient, + Http.client.Client< + never, + Http.error.HttpClientError | MissingDataGraphQLResponseError, + GraphQLSuccessResponse + > +>() { + static fromDefaultClient(client: Http.client.Client.Default): Layer.Layer { + return Layer.succeed( + GraphQLClient, + client.pipe( + Http.client.mapRequest(Http.request.setHeaders(headers)), + Http.client.filterStatusOk, + Http.client.mapEffectScoped(res => + Effect.flatMap(res.json, _ => { + const body = _ as ExecutionResult; + return body.data + ? Effect.succeed({ ...res, body: { ...body, data: body.data } }) + : Effect.fail(new MissingDataGraphQLResponseError({ ...res, body })); + }), + ), ), - ), + ); + } + + static Live: Layer.Layer = Layer.unwrapEffect( + Effect.map(Http.client.Client, GraphQLClient.fromDefaultClient), ); + static fromEndpoint( + endpoint: string, + ): Layer.Layer { + return Layer.unwrapEffect( + Effect.map(Http.client.Client, client => + GraphQLClient.fromDefaultClient( + Http.client.mapRequest(client, Http.request.prependUrl(endpoint)), + ), + ), + ); + } +} + const makeGraphQLOperation = ({ document, fallbackOperationName }: GraphQLOperationArgs) => (variables: Vars, opts?: GraphQLOperationOptions) => { const operationName = opts?.preferredOpName ?? fallbackOperationName; const query = document; - return Effect.flatMap(makeGraphQLClient(), client => + return Effect.flatMap(GraphQLClient, client => Http.request.post('').pipe( Http.request.jsonBody({ query, @@ -650,6 +701,7 @@ const makeGraphQLOperation = variables, }), Effect.flatMap(client), + Effect.map(_ => _ as GraphQLSuccessResponse), ), ); }; @@ -671,19 +723,15 @@ export const feed4 = makeGraphQLOperation({ fallbackOperationName: 'feed4', }); -import { Layer } from 'effect'; import { NodeHttpClient } from '@effect/platform-node'; +import { flow } from 'effect'; -const HttpClientLive = Layer.effect( - Http.client.Client, - Effect.map( - Http.client.Client, - Http.client.mapRequest(Http.request.prependUrl('http://localhost:4000/graphql')), +const run = flow( + Effect.provide( + GraphQLClient.fromEndpoint('http://localhost:4000/graphql').pipe(Layer.provide(NodeHttpClient.layer)), ), -).pipe(Layer.provide(NodeHttpClient.layer)); - -const run = (e: Effect.Effect) => - e.pipe(Effect.provide(HttpClientLive), Effect.runPromise); + Effect.runPromise +); async function test() { await run(feed({})); @@ -707,7 +755,7 @@ export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -import { Data, Effect } from 'effect'; +import { Context, Data, Effect, Layer } from 'effect'; import { type DocumentNode, type ExecutionResult, print } from 'graphql'; import * as Http from '@effect/platform/HttpClient'; import gql from 'graphql-tag'; @@ -941,20 +989,6 @@ export const Feed4Document = gql\` } \`; -export type GraphQLSuccessResponse = { - body: ExecutionResult & { data: A }; - headers: Record; -}; - -export type GraphQLErrorResponse = { - body: Omit; - headers: Record; -}; - -export class MissingDataGraphQLResponseError extends Data.TaggedError( - 'MissingDataGraphQLResponseError', -) {} - export type GraphQLOperationOptions = { preferredOpName?: string; }; @@ -964,36 +998,77 @@ type GraphQLOperationArgs = { fallbackOperationName: string; }; -// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed +export type GraphQLSuccessResponse = Pick< + Http.response.ClientResponse, + 'status' | 'headers' +> & { + readonly body: ExecutionResult & { readonly data: A }; +}; + +export type GraphQLErrorResponse = Pick & { + readonly body: Pick; +}; + +export class MissingDataGraphQLResponseError extends Data.TaggedError( + 'MissingDataGraphQLResponseError', +) {} + const headers = { - Accept: - "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", - "Content-Type": "application/json", + // https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#legacy-watershed + Accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', + 'Content-Type': 'application/json', }; -const makeGraphQLClient = () => - Effect.map(Http.client.Client, client => - Http.client.mapRequest(client, Http.request.setHeaders(headers)).pipe( - Http.client.filterStatusOk, - Http.client.mapEffectScoped(res => - Effect.flatMap(res.json, _ => { - const result = { body: _ as ExecutionResult, headers: res.headers }; - - return result.body.data - ? Effect.succeed(result as GraphQLSuccessResponse) - : Effect.fail(new MissingDataGraphQLResponseError(result)); - }), +export class GraphQLClient extends Context.Tag('GraphQLClient')< + GraphQLClient, + Http.client.Client< + never, + Http.error.HttpClientError | MissingDataGraphQLResponseError, + GraphQLSuccessResponse + > +>() { + static fromDefaultClient(client: Http.client.Client.Default): Layer.Layer { + return Layer.succeed( + GraphQLClient, + client.pipe( + Http.client.mapRequest(Http.request.setHeaders(headers)), + Http.client.filterStatusOk, + Http.client.mapEffectScoped(res => + Effect.flatMap(res.json, _ => { + const body = _ as ExecutionResult; + return body.data + ? Effect.succeed({ ...res, body: { ...body, data: body.data } }) + : Effect.fail(new MissingDataGraphQLResponseError({ ...res, body })); + }), + ), ), - ), + ); + } + + static Live: Layer.Layer = Layer.unwrapEffect( + Effect.map(Http.client.Client, GraphQLClient.fromDefaultClient), ); + static fromEndpoint( + endpoint: string, + ): Layer.Layer { + return Layer.unwrapEffect( + Effect.map(Http.client.Client, client => + GraphQLClient.fromDefaultClient( + Http.client.mapRequest(client, Http.request.prependUrl(endpoint)), + ), + ), + ); + } +} + const makeGraphQLOperation = ({ document, fallbackOperationName }: GraphQLOperationArgs) => (variables: Vars, opts?: GraphQLOperationOptions) => { const operationName = opts?.preferredOpName ?? fallbackOperationName; const query = print(document); - return Effect.flatMap(makeGraphQLClient(), client => + return Effect.flatMap(GraphQLClient, client => Http.request.post('').pipe( Http.request.jsonBody({ query, @@ -1001,6 +1076,7 @@ const makeGraphQLOperation = variables, }), Effect.flatMap(client), + Effect.map(_ => _ as GraphQLSuccessResponse), ), ); }; @@ -1022,19 +1098,15 @@ export const feed4 = makeGraphQLOperation({ fallbackOperationName: 'feed4', }); -import { Layer } from 'effect'; import { NodeHttpClient } from '@effect/platform-node'; +import { flow } from 'effect'; -const HttpClientLive = Layer.effect( - Http.client.Client, - Effect.map( - Http.client.Client, - Http.client.mapRequest(Http.request.prependUrl('http://localhost:4000/graphql')), +const run = flow( + Effect.provide( + GraphQLClient.fromEndpoint('http://localhost:4000/graphql').pipe(Layer.provide(NodeHttpClient.layer)), ), -).pipe(Layer.provide(NodeHttpClient.layer)); - -const run = (e: Effect.Effect) => - e.pipe(Effect.provide(HttpClientLive), Effect.runPromise); + Effect.runPromise +); async function test() { await run(feed({})); diff --git a/packages/plugins/typescript/effect/tests/effect.spec.ts b/packages/plugins/typescript/effect/tests/effect.spec.ts index fc2776c9f8..86207dabdd 100644 --- a/packages/plugins/typescript/effect/tests/effect.spec.ts +++ b/packages/plugins/typescript/effect/tests/effect.spec.ts @@ -68,27 +68,16 @@ describe('effect', () => { }; describe('sdk', () => { - it('Should generate the correct content', async () => { - const config = {}; - const docs = [{ location: '', document: basicDoc }]; - const result = (await plugin(schema, docs, config, { - outputFile: 'graphql.ts', - })) as Types.ComplexPluginOutput; - - const usage = ` -import { Layer } from 'effect'; + const usage = ` import { NodeHttpClient } from '@effect/platform-node'; +import { flow } from 'effect'; -const HttpClientLive = Layer.effect( - Http.client.Client, - Effect.map( - Http.client.Client, - Http.client.mapRequest(Http.request.prependUrl('http://localhost:4000/graphql')), +const run = flow( + Effect.provide( + GraphQLClient.fromEndpoint('http://localhost:4000/graphql').pipe(Layer.provide(NodeHttpClient.layer)), ), -).pipe(Layer.provide(NodeHttpClient.layer)); - -const run = (e: Effect.Effect) => - e.pipe(Effect.provide(HttpClientLive), Effect.runPromise); + Effect.runPromise +); async function test() { await run(feed({})); @@ -104,10 +93,16 @@ async function test() { } } `; + it('Should generate the correct content', async () => { + const config = {}; + const docs = [{ location: '', document: basicDoc }]; + const result = (await plugin(schema, docs, config, { + outputFile: 'graphql.ts', + })) as Types.ComplexPluginOutput; const output = await validate(result, config, docs, schema, usage); - expect(result.prepend).toContain("import { Data, Effect } from 'effect';"); + expect(result.prepend).toContain("import { Context, Data, Effect, Layer } from 'effect';"); expect(result.prepend).toContain( "import { DocumentNode, ExecutionResult, print } from 'graphql';", ); @@ -122,39 +117,9 @@ async function test() { outputFile: 'graphql.ts', })) as Types.ComplexPluginOutput; - const usage = ` -import { Layer } from 'effect'; -import { NodeHttpClient } from '@effect/platform-node'; - -const HttpClientLive = Layer.effect( - Http.client.Client, - Effect.map( - Http.client.Client, - Http.client.mapRequest(Http.request.prependUrl('http://localhost:4000/graphql')), - ), -).pipe(Layer.provide(NodeHttpClient.layer)); - -const run = (e: Effect.Effect) => - e.pipe(Effect.provide(HttpClientLive), Effect.runPromise); - -async function test() { - await run(feed({})); - await run(feed3({})); - await run(feed4({})); - - const { body: { data } } = await run(feed2({})); - - if (data.feed) { - if (data.feed[0]) { - const id = data.feed[0].id - } - } -} -`; - const output = await validate(result, config, docs, schema, usage); - expect(result.prepend).toContain("import { Data, Effect } from 'effect';"); + expect(result.prepend).toContain("import { Context, Data, Effect, Layer } from 'effect';"); expect(result.prepend).toContain( "import { DocumentNode, ExecutionResult, print } from 'graphql';", ); @@ -169,39 +134,9 @@ async function test() { outputFile: 'graphql.ts', })) as Types.ComplexPluginOutput; - const usage = ` -import { Layer } from 'effect'; -import { NodeHttpClient } from '@effect/platform-node'; - -const HttpClientLive = Layer.effect( - Http.client.Client, - Effect.map( - Http.client.Client, - Http.client.mapRequest(Http.request.prependUrl('http://localhost:4000/graphql')), - ), -).pipe(Layer.provide(NodeHttpClient.layer)); - -const run = (e: Effect.Effect) => - e.pipe(Effect.provide(HttpClientLive), Effect.runPromise); - -async function test() { - await run(feed({})); - await run(feed3({})); - await run(feed4({})); - - const { body: { data } } = await run(feed2({})); - - if (data.feed) { - if (data.feed[0]) { - const id = data.feed[0].id - } - } -} -`; - const output = await validate(result, config, docs, schema, usage); - expect(result.prepend).toContain("import { Data, Effect } from 'effect';"); + expect(result.prepend).toContain("import { Context, Data, Effect, Layer } from 'effect';"); expect(result.prepend).toContain( "import { type DocumentNode, type ExecutionResult, print } from 'graphql';", ); diff --git a/packages/plugins/typescript/effect/tests/integration.spec.ts b/packages/plugins/typescript/effect/tests/integration.spec.ts index 195a58f83b..787ac8ca2d 100644 --- a/packages/plugins/typescript/effect/tests/integration.spec.ts +++ b/packages/plugins/typescript/effect/tests/integration.spec.ts @@ -8,28 +8,36 @@ import * as TypeScriptOperationsPlugin from '@graphql-codegen/typescript-operati import { makeExecutableSchema } from '@graphql-tools/schema'; import * as EffectPlugin from '../src/index.js'; +const sdkFileName = 'effect-sdk.ts'; +const sdkFilePath = path.join(__dirname, './test-files', sdkFileName); +const typeDefs = parse(/* GraphQL */ ` + type Query { + add(x: Int!, y: Int!): Int! + } +`); +const schema = makeExecutableSchema({ + typeDefs, + resolvers: { + Query: { + add: (_, { x, y }) => x + y, + }, + }, +}); +const exampleQuery = /* GraphQL */ ` + query Add($x: Int!, $y: Int!) { + add(x: $x, y: $y) + } +`; +const mockServer = mockGraphQLServer({ + schema, + host: 'http://localhost:4000', + path: '/graphql', +}); + describe('Effect Integration', () => { + afterAll(() => mockServer.done()); + it('should send requests correctly', async () => { - const sdkFileName = 'effect-sdk.ts'; - const sdkFilePath = path.join(__dirname, './test-files', sdkFileName); - const typeDefs = parse(/* GraphQL */ ` - type Query { - add(x: Int!, y: Int!): Int! - } - `); - const schema = makeExecutableSchema({ - typeDefs, - resolvers: { - Query: { - add: (_, { x, y }) => x + y, - }, - }, - }); - const exampleQuery = /* GraphQL */ ` - query Add($x: Int!, $y: Int!) { - add(x: $x, y: $y) - } - `; const sdkCodeString = await codegen({ schema: typeDefs, schemaAst: schema, @@ -53,21 +61,16 @@ describe('Effect Integration', () => { 'typescript-operations': {}, }, { - effect: {}, + effect: { mode: 'mixed' }, }, ], config: {}, }); await fs.writeFile(sdkFilePath, sdkCodeString, 'utf-8'); - const mockServer = mockGraphQLServer({ - schema, - host: 'http://localhost:4000', - path: '/graphql', - }); - const { runExampleQuery } = require('./test-files/run-example-query'); - const { body } = await runExampleQuery(2, 3); + const { exampleQueries } = require('./test-files/run-example-query'); + const { add } = exampleQueries('http://localhost:4000/graphql'); + const { body } = await add(2, 3); expect(body.data.add).toBe(5); - mockServer.done(); await fs.remove(sdkFilePath); }); }); diff --git a/packages/plugins/typescript/effect/tests/test-files/run-example-query.ts b/packages/plugins/typescript/effect/tests/test-files/run-example-query.ts index 0a4766f306..af7df0480d 100644 --- a/packages/plugins/typescript/effect/tests/test-files/run-example-query.ts +++ b/packages/plugins/typescript/effect/tests/test-files/run-example-query.ts @@ -1,16 +1,13 @@ import { Effect, Layer } from 'effect'; import { NodeHttpClient } from '@effect/platform-node'; -import * as Http from '@effect/platform/HttpClient'; -import { Add } from './effect-sdk.js'; +import { Add, GraphQLClient } from './effect-sdk.js'; -const HttpClientLive = Layer.effect( - Http.client.Client, - Effect.map( - Http.client.Client, - Http.client.mapRequest(Http.request.prependUrl('http://localhost:4000/graphql')), - ), -).pipe(Layer.provide(NodeHttpClient.layer)); - -export const runExampleQuery = (x: number, y: number) => { - return Add({ x, y }).pipe(Effect.provide(HttpClientLive), Effect.runPromise); -}; +export const exampleQueries = (endpoint: string) => ({ + add: (x: number, y: number) => + Add({ x, y }).pipe( + Effect.provide( + GraphQLClient.fromEndpoint(endpoint).pipe(Layer.provide(NodeHttpClient.layer)), + ), + Effect.runPromise, + ), +}); diff --git a/yarn.lock b/yarn.lock index 85f33c7290..f46313ed14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1352,20 +1352,20 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@effect/platform-node-shared@^0.3.12": - version "0.3.12" - resolved "https://registry.yarnpkg.com/@effect/platform-node-shared/-/platform-node-shared-0.3.12.tgz#a2c4e199e63579604176e536d49fe0dc140a1be1" - integrity sha512-sbjA6lW4mTN5cSVNzm9rGuObYY61AQzGxzbdtV/J8ATWK5jBLZMh5M5/1vAjiStF1hMI5koF8ZBUZ3p3vyNF0g== +"@effect/platform-node-shared@^0.3.13": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@effect/platform-node-shared/-/platform-node-shared-0.3.13.tgz#fcad21159ab77dd9f0c146a04351a13f02b72507" + integrity sha512-DW0xLaYH7PyXbLaTzve9iOUc0F+T3kPE/j5UeOm3wu1MffgwA31HzWhiwvbvjF+qLPqTrthYIZlhFEFC/zjOZg== dependencies: "@parcel/watcher" "^2.4.1" multipasta "^0.1.21" "@effect/platform-node@~0.45.14": - version "0.45.14" - resolved "https://registry.yarnpkg.com/@effect/platform-node/-/platform-node-0.45.14.tgz#cc353e7ddff99955fe0e1b1c307ab14d0d43c614" - integrity sha512-HveyfM+UrUjOOfmstyvXII4QlYUvYIuMgptw79h3VzmAPhTDa6fS6S/3MpDtE+8INFzNjHuo/1DKB/BF0rDFNg== + version "0.45.15" + resolved "https://registry.yarnpkg.com/@effect/platform-node/-/platform-node-0.45.15.tgz#d5cdc52d0bea097acf554f94029104d5b0bd35be" + integrity sha512-cXxDLIpEEgLO0v4YXK8JHS63x3/mGaAoR/QYueu4+DAIwVc0BykurYQh+n1PMKJvvHHprDK8G0xndRWwP0/Neg== dependencies: - "@effect/platform-node-shared" "^0.3.12" + "@effect/platform-node-shared" "^0.3.13" mime "^3.0.0" ws "^8.16.0" @@ -7397,7 +7397,7 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" -safe-array-concat@^1.1.0, safe-array-concat@^1.1.2: +safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== @@ -7761,7 +7761,7 @@ string-width@^7.0.0: get-east-asian-width "^1.0.0" strip-ansi "^7.1.0" -string.prototype.trim@^1.2.8, string.prototype.trim@^1.2.9: +string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== @@ -7771,7 +7771,7 @@ string.prototype.trim@^1.2.8, string.prototype.trim@^1.2.9: es-abstract "^1.23.0" es-object-atoms "^1.0.0" -string.prototype.trimend@^1.0.7, string.prototype.trimend@^1.0.8: +string.prototype.trimend@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==