diff --git a/packages/plugins/graphql/src/sdk/plugin.test.ts b/packages/plugins/graphql/src/sdk/plugin.test.ts index 030466168..44531c8dd 100644 --- a/packages/plugins/graphql/src/sdk/plugin.test.ts +++ b/packages/plugins/graphql/src/sdk/plugin.test.ts @@ -1,6 +1,12 @@ import { describe, it, expect } from "@effect/vitest"; -import { Effect } from "effect"; -import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"; +import { Effect, Layer } from "effect"; +import { + HttpClient, + HttpClientRequest, + HttpClientResponse, + HttpServerRequest, + HttpServerResponse, +} from "effect/unstable/http"; import { AuthTemplateSlug, @@ -318,6 +324,48 @@ describe("graphqlPlugin real protocol server", () => { }), ); + it.effect("uses the executor HttpClient layer for connection-time introspection", () => + Effect.gen(function* () { + const seen: string[] = []; + const httpClientLayer = Layer.succeed(HttpClient.HttpClient)( + HttpClient.make((request: HttpClientRequest.HttpClientRequest) => { + seen.push(request.url); + return Effect.succeed( + HttpClientResponse.fromWeb( + request, + new Response(JSON.stringify({ data: introspectionResult }), { + status: 200, + headers: { "content-type": "application/json" }, + }), + ), + ); + }), + ); + const config = makeTestConfig({ + plugins: [memoryCredentialsPlugin(), graphqlPlugin()] as const, + }); + const executor = yield* createExecutor({ ...config, httpClientLayer }); + + yield* executor.graphql.addIntegration({ + endpoint: "https://internal.example/graphql", + slug: "guarded_graph", + name: "Guarded Graph", + }); + yield* createOrgConnection(executor, { + integration: "guarded_graph", + name: "default", + template: "none", + value: "unused", + }); + + const tools = yield* executor.tools.list(); + expect(seen).toEqual(["https://internal.example/graphql"]); + expect(tools.map((tool) => String(tool.name))).toEqual( + expect.arrayContaining(["query.hello", "mutation.setGreeting"]), + ); + }), + ); + it.effect("invokes a live query through an apiKey header template", () => Effect.gen(function* () { const server = yield* serveGreetingServer; diff --git a/packages/plugins/graphql/src/sdk/plugin.ts b/packages/plugins/graphql/src/sdk/plugin.ts index 0a2eb7d7d..31424b8f8 100644 --- a/packages/plugins/graphql/src/sdk/plugin.ts +++ b/packages/plugins/graphql/src/sdk/plugin.ts @@ -1,6 +1,6 @@ import { Effect, Match, Option, Schema } from "effect"; import type { Layer } from "effect"; -import { FetchHttpClient, HttpClient } from "effect/unstable/http"; +import { HttpClient } from "effect/unstable/http"; import { authToolFailure, @@ -893,11 +893,13 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => { template, storage, getValues, + httpClientLayer, }: { readonly config: IntegrationConfig; readonly template: AuthTemplateSlug | null; readonly storage: GraphqlStore; readonly getValues: () => Effect.Effect, unknown>; + readonly httpClientLayer: Layer.Layer; }) => Effect.gen(function* () { const decoded = yield* decodeGraphqlIntegrationConfig(config).pipe(Effect.option); @@ -917,7 +919,7 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => { introspectionJson, values, template, - options?.httpClientLayer ?? httpClientLayerFallback, + options?.httpClientLayer ?? httpClientLayer, ).pipe(Effect.option); if (Option.isNone(introspection)) return { tools: [] }; const extracted = yield* extract(introspection.value).pipe(Effect.option); @@ -1117,9 +1119,3 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => { // HTTP transport (routes/handlers/extensionService) is layered on by the // api-aware factory in `@executor-js/plugin-graphql/api`. }); - -// The fallback HTTP layer for `resolveTools`. The hook input carries no `ctx`, -// so when no explicit layer is passed to the plugin we use the same default the -// executor wires into `ctx.httpClientLayer` (`FetchHttpClient.layer`). Hosts/ -// tests that need a custom transport pass `options.httpClientLayer`. -const httpClientLayerFallback: Layer.Layer = FetchHttpClient.layer;