196 lines · 7.1 KB · TypeScript
RawDownload
// Connection endpoints — create / list / get / remove round-trip and error// fidelity within a single org (v2).//// Ports the v1 "secrets api" suite. In v2 a connection IS the credential:// owner-scoped, bound 1:1 to an integration, identified by (owner, integration,// name). There is no scope id and no separate secret value endpoint — the value// is stored through the connection's provider and never echoed back. import { describe, expect, it } from "@effect/vitest";import { Effect, Result } from "effect";import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi";import { Schema } from "effect"; import {  AuthTemplateSlug,  ConnectionName,  IntegrationSlug,  connectionIdentifier,} from "@executor-js/sdk";import { makeOpenApiHttpApiTestAddSpecPayload } from "@executor-js/plugin-openapi/testing"; import { asOrg } from "../testing/api-harness"; const PingApi = HttpApi.make("connectionsApiTest")  .add(    HttpApiGroup.make("default", { topLevel: true }).add(      HttpApiEndpoint.get("ping", "/ping", { success: Schema.Unknown }),    ),  )  .annotateMerge(OpenApi.annotations({ title: "Connections API Test", version: "1.0.0" })); const TEMPLATE_API_KEY = AuthTemplateSlug.make("apiKey");const canonicalConnectionName = (name: ConnectionName): ConnectionName =>  connectionIdentifier(String(name)); // Registers a minimal openapi integration so connections have something to bind// to, then returns its slug.const registerIntegration = (org: string) =>  Effect.gen(function* () {    const slug = IntegrationSlug.make(`ns_${crypto.randomUUID().replace(/-/g, "_")}`);    yield* asOrg(org, (client) =>      client.openapi.addSpec({        payload: makeOpenApiHttpApiTestAddSpecPayload(PingApi, {          slug,          baseUrl: "http://example.com",        }),      }),    );    return slug;  }); describe("connections api (HTTP)", () => {  it.effect("create → list → get returns connection metadata without the value", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const integration = yield* registerIntegration(org);      const name = ConnectionName.make(`conn_${crypto.randomUUID().slice(0, 8)}`);      const storedName = canonicalConnectionName(name);       const secretValue = "sk-test-abc";      const created = yield* asOrg(org, (client) =>        client.connections.create({          payload: {            owner: "org",            name,            integration,            template: TEMPLATE_API_KEY,            identityLabel: "My API Token",            value: secretValue,          },        }),      );      expect(created.name).toBe(storedName);      expect(created.owner).toBe("org");      expect(JSON.stringify(created)).not.toContain(secretValue);       const list = yield* asOrg(org, (client) =>        client.connections.list({ query: { integration } }),      );      expect(list.find((c) => c.name === storedName)?.identityLabel).toBe("My API Token");      expect(JSON.stringify(list)).not.toContain(secretValue);       const fetched = yield* asOrg(org, (client) =>        client.connections.get({ params: { owner: "org", integration, name: storedName } }),      );      expect(fetched.name).toBe(storedName);      expect(fetched.integration).toBe(integration);    }),  );   it.effect("get on an unknown connection fails with ConnectionNotFoundError", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const integration = yield* registerIntegration(org);      const missing = ConnectionName.make(`missing_${crypto.randomUUID().slice(0, 8)}`);       const result = yield* asOrg(org, (client) =>        client.connections          .get({ params: { owner: "org", integration, name: missing } })          .pipe(Effect.result),      );      expect(Result.isFailure(result)).toBe(true);    }),  );   it.effect("remove deletes the connection; list drops it and get fails", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const integration = yield* registerIntegration(org);      const name = ConnectionName.make(`conn_${crypto.randomUUID().slice(0, 8)}`);      const storedName = canonicalConnectionName(name);       yield* asOrg(org, (client) =>        Effect.gen(function* () {          yield* client.connections.create({            payload: { owner: "org", name, integration, template: TEMPLATE_API_KEY, value: "v" },          });          const removed = yield* client.connections.remove({            params: { owner: "org", integration, name: storedName },          });          expect(removed.removed).toBe(true);        }),      );       const list = yield* asOrg(org, (client) =>        client.connections.list({ query: { integration } }),      );      expect(list.map((c) => c.name)).not.toContain(storedName);       const afterGet = yield* asOrg(org, (client) =>        client.connections          .get({ params: { owner: "org", integration, name: storedName } })          .pipe(Effect.result),      );      expect(Result.isFailure(afterGet)).toBe(true);    }),  );   it.effect("remove on an unknown connection fails with ConnectionNotFoundError", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const integration = yield* registerIntegration(org);      const missing = ConnectionName.make(`missing_${crypto.randomUUID().slice(0, 8)}`);       const result = yield* asOrg(org, (client) =>        client.connections          .remove({ params: { owner: "org", integration, name: missing } })          .pipe(Effect.result),      );      expect(Result.isFailure(result)).toBe(true);    }),  );   it.effect("create with the same (owner, integration, name) twice updates the metadata", () =>    Effect.gen(function* () {      const org = `org_${crypto.randomUUID()}`;      const integration = yield* registerIntegration(org);      const name = ConnectionName.make(`conn_${crypto.randomUUID().slice(0, 8)}`);      const storedName = canonicalConnectionName(name);       const first = yield* asOrg(org, (client) =>        Effect.gen(function* () {          yield* client.connections.create({            payload: {              owner: "org",              name,              integration,              template: TEMPLATE_API_KEY,              identityLabel: "first",              value: "first-value",            },          });          return yield* client.connections.list({ query: { integration } });        }),      );      expect(first.find((c) => c.name === storedName)?.identityLabel).toBe("first");       const second = yield* asOrg(org, (client) =>        Effect.gen(function* () {          yield* client.connections.create({            payload: {              owner: "org",              name,              integration,              template: TEMPLATE_API_KEY,              identityLabel: "updated",              value: "second-value",            },          });          return yield* client.connections.list({ query: { integration } });        }),      );      expect(second.find((c) => c.name === storedName)?.identityLabel).toBe("updated");    }),  );});