Skip to content

Commit 5865d0c

Browse files
committed
Use executor HTTP layer for GraphQL discovery
1 parent db1ee2c commit 5865d0c

2 files changed

Lines changed: 54 additions & 10 deletions

File tree

packages/plugins/graphql/src/sdk/plugin.test.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { describe, it, expect } from "@effect/vitest";
2-
import { Effect } from "effect";
3-
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
2+
import { Effect, Layer } from "effect";
3+
import {
4+
HttpClient,
5+
HttpClientRequest,
6+
HttpClientResponse,
7+
HttpServerRequest,
8+
HttpServerResponse,
9+
} from "effect/unstable/http";
410

511
import {
612
AuthTemplateSlug,
@@ -318,6 +324,48 @@ describe("graphqlPlugin real protocol server", () => {
318324
}),
319325
);
320326

327+
it.effect("uses the executor HttpClient layer for connection-time introspection", () =>
328+
Effect.gen(function* () {
329+
const seen: string[] = [];
330+
const httpClientLayer = Layer.succeed(HttpClient.HttpClient)(
331+
HttpClient.make((request: HttpClientRequest.HttpClientRequest) => {
332+
seen.push(request.url);
333+
return Effect.succeed(
334+
HttpClientResponse.fromWeb(
335+
request,
336+
new Response(JSON.stringify({ data: introspectionResult }), {
337+
status: 200,
338+
headers: { "content-type": "application/json" },
339+
}),
340+
),
341+
);
342+
}),
343+
);
344+
const config = makeTestConfig({
345+
plugins: [memoryCredentialsPlugin(), graphqlPlugin()] as const,
346+
});
347+
const executor = yield* createExecutor({ ...config, httpClientLayer });
348+
349+
yield* executor.graphql.addIntegration({
350+
endpoint: "https://internal.example/graphql",
351+
slug: "guarded_graph",
352+
name: "Guarded Graph",
353+
});
354+
yield* createOrgConnection(executor, {
355+
integration: "guarded_graph",
356+
name: "default",
357+
template: "none",
358+
value: "unused",
359+
});
360+
361+
const tools = yield* executor.tools.list();
362+
expect(seen).toEqual(["https://internal.example/graphql"]);
363+
expect(tools.map((tool) => String(tool.name))).toEqual(
364+
expect.arrayContaining(["query.hello", "mutation.setGreeting"]),
365+
);
366+
}),
367+
);
368+
321369
it.effect("invokes a live query through an apiKey header template", () =>
322370
Effect.gen(function* () {
323371
const server = yield* serveGreetingServer;

packages/plugins/graphql/src/sdk/plugin.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Effect, Match, Option, Schema } from "effect";
22
import type { Layer } from "effect";
3-
import { FetchHttpClient, HttpClient } from "effect/unstable/http";
3+
import { HttpClient } from "effect/unstable/http";
44

55
import {
66
authToolFailure,
@@ -893,11 +893,13 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => {
893893
template,
894894
storage,
895895
getValues,
896+
httpClientLayer,
896897
}: {
897898
readonly config: IntegrationConfig;
898899
readonly template: AuthTemplateSlug | null;
899900
readonly storage: GraphqlStore;
900901
readonly getValues: () => Effect.Effect<Record<string, string | null>, unknown>;
902+
readonly httpClientLayer: Layer.Layer<HttpClient.HttpClient>;
901903
}) =>
902904
Effect.gen(function* () {
903905
const decoded = yield* decodeGraphqlIntegrationConfig(config).pipe(Effect.option);
@@ -917,7 +919,7 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => {
917919
introspectionJson,
918920
values,
919921
template,
920-
options?.httpClientLayer ?? httpClientLayerFallback,
922+
options?.httpClientLayer ?? httpClientLayer,
921923
).pipe(Effect.option);
922924
if (Option.isNone(introspection)) return { tools: [] };
923925
const extracted = yield* extract(introspection.value).pipe(Effect.option);
@@ -1117,9 +1119,3 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => {
11171119
// HTTP transport (routes/handlers/extensionService) is layered on by the
11181120
// api-aware factory in `@executor-js/plugin-graphql/api`.
11191121
});
1120-
1121-
// The fallback HTTP layer for `resolveTools`. The hook input carries no `ctx`,
1122-
// so when no explicit layer is passed to the plugin we use the same default the
1123-
// executor wires into `ctx.httpClientLayer` (`FetchHttpClient.layer`). Hosts/
1124-
// tests that need a custom transport pass `options.httpClientLayer`.
1125-
const httpClientLayerFallback: Layer.Layer<HttpClient.HttpClient> = FetchHttpClient.layer;

0 commit comments

Comments
 (0)