Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions e2e/scenarios/graphql-introspection-health.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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),
),
);
}),
),
);
6 changes: 5 additions & 1 deletion packages/plugins/graphql/src/sdk/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}),
);

Expand Down
8 changes: 4 additions & 4 deletions packages/plugins/graphql/src/sdk/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
Expand Down
Loading