diff --git a/e2e/scenarios/graphql-introspection-health.test.ts b/e2e/scenarios/graphql-introspection-health.test.ts index aaa48979ec..eb7cce6fe6 100644 --- a/e2e/scenarios/graphql-introspection-health.test.ts +++ b/e2e/scenarios/graphql-introspection-health.test.ts @@ -8,6 +8,11 @@ import { graphqlHttpPlugin } from "@executor-js/plugin-graphql/api"; import { AuthTemplateSlug, ConnectionName, IntegrationSlug } from "@executor-js/sdk/shared"; import { variable } from "@executor-js/sdk/http-auth"; +import { + makeGreetingGraphqlSchema, + serveGraphqlTestServer, +} from "@executor-js/plugin-graphql/testing"; + import { scenario } from "../src/scenario"; import { Api, Browser, Target } from "../src/services"; import { visit } from "../src/surfaces/browser"; @@ -144,3 +149,60 @@ scenario( }), ), ); + +scenario( + "GraphQL ยท a healthy account row keeps the connection's name", + {}, + Effect.scoped( + Effect.gen(function* () { + const target = yield* Target; + const browser = yield* Browser; + const { client: makeApiClient } = yield* Api; + const identity = yield* target.newIdentity(); + const client = yield* makeApiClient(api, identity); + const slug = unique("graphql_label"); + const upstream = yield* serveGraphqlTestServer({ schema: makeGreetingGraphqlSchema() }); + + yield* client.graphql.addIntegration({ + payload: { + endpoint: upstream.endpoint, + slug, + name: "Greeting API", + }, + }); + + yield* Effect.gen(function* () { + yield* client.connections.create({ + payload: { + owner: "org", + name: ConnectionName.make("workspace"), + integration: IntegrationSlug.make(slug), + template: AuthTemplateSlug.make("none"), + value: "unused", + }, + }); + + yield* browser.session(identity, async ({ page, step }) => { + await step("The healthy account row is labeled with the connection name", async () => { + await visit(page, `/integrations/${slug}`); + await page.getByRole("tab", { name: "Accounts" }).click(); + // Wait for the automatic health probe to land so a probe identity + // would have replaced the label if the plugin still reported one. + await page.getByLabel("Status: Healthy").waitFor(); + await page.getByText("workspace", { exact: true }).waitFor(); + expect( + await page.getByText("GraphQL schema", { exact: false }).count(), + "the schema root type never masquerades as the account label", + ).toBe(0); + }); + }); + }).pipe( + Effect.ensuring( + client.integrations + .remove({ params: { slug: IntegrationSlug.make(slug) } }) + .pipe(Effect.ignore), + ), + ); + }), + ), +); diff --git a/packages/plugins/graphql/src/sdk/plugin.test.ts b/packages/plugins/graphql/src/sdk/plugin.test.ts index a9597fd721..acc095adaf 100644 --- a/packages/plugins/graphql/src/sdk/plugin.test.ts +++ b/packages/plugins/graphql/src/sdk/plugin.test.ts @@ -729,8 +729,12 @@ describe("graphqlPlugin real protocol server", () => { expect(result).toMatchObject({ status: "healthy", httpStatus: 200, - identity: "GraphQL schema: Query", + responseSample: [{ path: "__schema.queryType.name", value: "Query" }], }); + // The schema's root type name is not an account identity; reporting it + // as one made the accounts UI label every GraphQL connection + // "GraphQL schema: Query". + expect(result).not.toHaveProperty("identity"); }), ); diff --git a/packages/plugins/graphql/src/sdk/plugin.ts b/packages/plugins/graphql/src/sdk/plugin.ts index fb53f28ac6..31bfea8362 100644 --- a/packages/plugins/graphql/src/sdk/plugin.ts +++ b/packages/plugins/graphql/src/sdk/plugin.ts @@ -737,16 +737,16 @@ const checkGraphqlHealth = (input: { : { ...verdict, detail: scrubCredentialValues(verdict.detail, input.credential.values) }; } + // No `identity`: the schema's root type name ("Query" almost everywhere) + // identifies nothing, and the accounts UI would show it as the account + // label. Introspection proves reachability, not who the credential is. const queryType = result.introspection.__schema.queryType?.name; return { status: "healthy", httpStatus: 200, checkedAt, ...(queryType != null - ? { - identity: `GraphQL schema: ${queryType}`, - responseSample: [{ path: "__schema.queryType.name", value: queryType }], - } + ? { responseSample: [{ path: "__schema.queryType.name", value: queryType }] } : {}), } satisfies HealthCheckResult; });