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
52 changes: 23 additions & 29 deletions apps/cloud/src/services/sources-api.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,10 @@ const MinimalSourceApi = HttpApi.make("sourcesApiTest")
.annotateMerge(OpenApi.annotations({ title: "Sources API Test", version: "1.0.0" }));

const makeMinimalOpenApiSourcePayload = (
targetScope: ScopeId,
namespace: string,
options: Omit<
Parameters<typeof makeOpenApiHttpApiTestAddSpecPayload>[1],
"targetScope" | "namespace"
> = {},
options: Omit<Parameters<typeof makeOpenApiHttpApiTestAddSpecPayload>[1], "namespace"> = {},
) =>
makeOpenApiHttpApiTestAddSpecPayload(MinimalSourceApi, {
targetScope,
namespace,
...options,
});
Expand Down Expand Up @@ -72,7 +67,7 @@ describe("sources api (HTTP)", () => {
Effect.gen(function* () {
const result = yield* client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: makeMinimalOpenApiSourcePayload(ScopeId.make(org), namespace),
payload: makeMinimalOpenApiSourcePayload(namespace),
});
expect(result.namespace).toBe(namespace);
expect(result.toolCount).toBeGreaterThan(0);
Expand All @@ -94,7 +89,7 @@ describe("sources api (HTTP)", () => {
yield* asOrg(org, (client) =>
client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: makeMinimalOpenApiSourcePayload(ScopeId.make(org), namespace),
payload: makeMinimalOpenApiSourcePayload(namespace),
}),
);

Expand Down Expand Up @@ -136,11 +131,9 @@ describe("sources api (HTTP)", () => {
const result = yield* asOrg(org, (client) =>
client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: makeMinimalOpenApiSourcePayload(
ScopeId.make(org),
`ns_${crypto.randomUUID().replace(/-/g, "_")}`,
{ baseUrl: "http://example.com" },
),
payload: makeMinimalOpenApiSourcePayload(`ns_${crypto.randomUUID().replace(/-/g, "_")}`, {
baseUrl: "http://example.com",
}),
}),
);

Expand All @@ -167,8 +160,9 @@ describe("sources api (HTTP)", () => {
client.openapi.addSpec({
params: { scopeId },
payload: {
targetScope: scopeId,
spec: server.specJson,
spec: { kind: "blob", value: server.specJson },
name: "Invocable Source API",
baseUrl: server.baseUrl,
namespace,
},
}),
Expand Down Expand Up @@ -414,7 +408,7 @@ describe("sources api (HTTP)", () => {
Effect.gen(function* () {
yield* client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: makeMinimalOpenApiSourcePayload(ScopeId.make(org), namespace),
payload: makeMinimalOpenApiSourcePayload(namespace),
});
yield* client.sources.remove({
params: { scopeId: ScopeId.make(org), sourceId: namespace },
Expand Down Expand Up @@ -468,7 +462,7 @@ describe("sources api (HTTP)", () => {
Effect.gen(function* () {
yield* client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: makeMinimalOpenApiSourcePayload(ScopeId.make(org), namespace),
payload: makeMinimalOpenApiSourcePayload(namespace),
});
yield* client.openapi.updateSource({
params: { scopeId: ScopeId.make(org), namespace },
Expand Down Expand Up @@ -502,11 +496,10 @@ describe("sources api (HTTP)", () => {
client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgId) },
payload: {
...makeMinimalOpenApiSourcePayload(ScopeId.make(orgId), namespace),
...makeMinimalOpenApiSourcePayload(namespace),
headers: {
Authorization: {
kind: "binding",
slot: "auth:personal-token",
kind: "secret",
prefix: "Bearer ",
},
},
Expand All @@ -530,7 +523,7 @@ describe("sources api (HTTP)", () => {
sourceId: namespace,
sourceScope: ScopeId.make(orgId),
scope: ScopeId.make(aliceScope),
slot: "auth:personal-token",
slot: "header:authorization",
value: {
kind: "secret",
secretId: SecretId.make("alice_pat"),
Expand All @@ -541,7 +534,7 @@ describe("sources api (HTTP)", () => {
sourceId: namespace,
sourceScopeId: ScopeId.make(orgId),
scopeId: ScopeId.make(aliceScope),
slot: "auth:personal-token",
slot: "header:authorization",
value: {
kind: "secret",
secretId: SecretId.make("alice_pat"),
Expand All @@ -568,7 +561,7 @@ describe("sources api (HTTP)", () => {
sourceId: namespace,
sourceScope: ScopeId.make(orgId),
scope: ScopeId.make(bobScope),
slot: "auth:personal-token",
slot: "header:authorization",
value: {
kind: "secret",
secretId: SecretId.make("bob_pat"),
Expand All @@ -590,7 +583,7 @@ describe("sources api (HTTP)", () => {
expect(aliceBindings).toContainEqual(
expect.objectContaining({
scopeId: ScopeId.make(aliceScope),
slot: "auth:personal-token",
slot: "header:authorization",
value: {
kind: "secret",
secretId: SecretId.make("alice_pat"),
Expand All @@ -601,7 +594,7 @@ describe("sources api (HTTP)", () => {
expect(
aliceBindings.some(
(binding) =>
binding.slot === "auth:personal-token" &&
binding.slot === "header:authorization" &&
binding.value.kind === "secret" &&
binding.value.secretId === SecretId.make("bob_pat"),
),
Expand All @@ -619,7 +612,7 @@ describe("sources api (HTTP)", () => {
expect(bobBindings).toContainEqual(
expect.objectContaining({
scopeId: ScopeId.make(bobScope),
slot: "auth:personal-token",
slot: "header:authorization",
value: {
kind: "secret",
secretId: SecretId.make("bob_pat"),
Expand All @@ -630,7 +623,7 @@ describe("sources api (HTTP)", () => {
expect(
bobBindings.some(
(binding) =>
binding.slot === "auth:personal-token" &&
binding.slot === "header:authorization" &&
binding.value.kind === "secret" &&
binding.value.secretId === SecretId.make("alice_pat"),
),
Expand All @@ -654,8 +647,9 @@ describe("sources api (HTTP)", () => {
client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: {
targetScope: ScopeId.make(org),
spec: CLOUDFLARE_SPEC,
spec: { kind: "blob", value: CLOUDFLARE_SPEC },
name: namespace,
baseUrl: "https://api.cloudflare.com/client/v4",
namespace,
},
}),
Expand Down
10 changes: 6 additions & 4 deletions apps/cloud/src/services/sources-refresh.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ describe("sources.refresh (HTTP)", () => {
client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: {
targetScope: ScopeId.make(org),
spec: server.specUrl,
spec: { kind: "url", url: server.specUrl },
name: namespace,
baseUrl: server.baseUrl,
namespace,
},
}),
Expand Down Expand Up @@ -107,8 +108,9 @@ describe("sources.refresh (HTTP)", () => {
client.openapi.addSpec({
params: { scopeId: ScopeId.make(org) },
payload: {
targetScope: ScopeId.make(org),
spec: makeRefreshSpecText(),
spec: { kind: "blob", value: makeRefreshSpecText() },
name: namespace,
baseUrl: "https://api.example.test",
namespace,
},
}),
Expand Down
35 changes: 10 additions & 25 deletions apps/cloud/src/services/tenant-isolation.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@ const TenantIsolationApi = HttpApi.make("tenantIsolationTest")
.annotateMerge(OpenApi.annotations({ title: "Tenant Test API", version: "1.0.0" }));

const makeTenantOpenApiSourcePayload = (
targetScope: ScopeId,
namespace: string,
options: Omit<
Parameters<typeof makeOpenApiHttpApiTestAddSpecPayload>[1],
"targetScope" | "namespace"
> = {},
options: Omit<Parameters<typeof makeOpenApiHttpApiTestAddSpecPayload>[1], "namespace"> = {},
) =>
makeOpenApiHttpApiTestAddSpecPayload(TenantIsolationApi, {
targetScope,
namespace,
...options,
});
Expand Down Expand Up @@ -116,7 +111,7 @@ describe("tenant isolation (HTTP)", () => {
yield* asOrg(orgA, (client) =>
client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgA) },
payload: makeTenantOpenApiSourcePayload(ScopeId.make(orgA), namespaceA),
payload: makeTenantOpenApiSourcePayload(namespaceA),
}),
);

Expand All @@ -136,7 +131,7 @@ describe("tenant isolation (HTTP)", () => {
yield* asOrg(orgA, (client) =>
client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgA) },
payload: makeTenantOpenApiSourcePayload(ScopeId.make(orgA), namespaceA),
payload: makeTenantOpenApiSourcePayload(namespaceA),
}),
);

Expand All @@ -159,7 +154,7 @@ describe("tenant isolation (HTTP)", () => {
yield* asOrg(orgA, (client) =>
client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgA) },
payload: makeTenantOpenApiSourcePayload(ScopeId.make(orgA), namespaceA),
payload: makeTenantOpenApiSourcePayload(namespaceA),
}),
);

Expand Down Expand Up @@ -259,11 +254,10 @@ describe("tenant isolation (HTTP)", () => {
yield* client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgA) },
payload: {
...makeTenantOpenApiSourcePayload(ScopeId.make(orgA), namespaceA),
...makeTenantOpenApiSourcePayload(namespaceA),
headers: {
Authorization: {
kind: "binding",
slot: "auth:token",
kind: "secret",
prefix: "Bearer ",
},
},
Expand All @@ -275,7 +269,7 @@ describe("tenant isolation (HTTP)", () => {
sourceId: namespaceA,
sourceScope: ScopeId.make(orgA),
scope: ScopeId.make(orgA),
slot: "auth:token",
slot: "header:authorization",
value: { kind: "secret", secretId: secretIdA },
},
});
Expand Down Expand Up @@ -303,16 +297,7 @@ describe("tenant isolation (HTTP)", () => {
Effect.gen(function* () {
yield* client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgA) },
payload: {
...makeTenantOpenApiSourcePayload(ScopeId.make(orgA), namespaceA),
headers: {
Authorization: {
kind: "binding",
slot: "auth:conn",
prefix: "Bearer ",
},
},
},
payload: makeTenantOpenApiSourcePayload(namespaceA),
});
yield* client.openapi.setSourceBinding({
params: { scopeId: ScopeId.make(orgA) },
Expand Down Expand Up @@ -346,7 +331,7 @@ describe("tenant isolation (HTTP)", () => {
yield* asOrg(orgA, (client) =>
client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgA) },
payload: makeTenantOpenApiSourcePayload(ScopeId.make(orgA), namespace, {
payload: makeTenantOpenApiSourcePayload(namespace, {
name: "Org A API",
baseUrl: "https://org-a.example.com",
}),
Expand All @@ -355,7 +340,7 @@ describe("tenant isolation (HTTP)", () => {
yield* asOrg(orgB, (client) =>
client.openapi.addSpec({
params: { scopeId: ScopeId.make(orgB) },
payload: makeTenantOpenApiSourcePayload(ScopeId.make(orgB), namespace, {
payload: makeTenantOpenApiSourcePayload(namespace, {
name: "Org B API",
baseUrl: "https://org-b.example.com",
}),
Expand Down
4 changes: 3 additions & 1 deletion apps/marketing/src/pages/api/detect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ export const POST: APIRoute = async ({ request }) => {
// Add source to register its tools (Google Discovery needs auth so skip)
if (match.kind === "openapi") {
yield* executor.openapi.addSpec({
spec: match.endpoint,
spec: { kind: "url", url: match.endpoint },
name: match.name,
namespace: match.namespace,
baseUrl: match.endpoint,
scope: "test-scope",
});
} else if (match.kind === "graphql") {
Expand Down
3 changes: 2 additions & 1 deletion examples/all-plugins/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,9 @@ const program = Effect.gen(function* () {
console.log("-".repeat(72));

const addSpecResult = yield* executor.openapi.addSpec({
spec: exampleOpenApiSpec,
spec: { kind: "blob", value: exampleOpenApiSpec },
namespace: "example-api",
name: "Example API",
baseUrl: "https://example.com/api",
scope: "example-scope",
});
Expand Down
7 changes: 6 additions & 1 deletion examples/docs-sdk-quickstart/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ const executor = await createExecutor({
await executor.openapi.addSpec({
namespace: "inventory",
scope: "docs-workspace",
spec: JSON.stringify(inventoryApi),
name: "Inventory API",
baseUrl: "https://inventory.example.com",
spec: {
kind: "blob",
value: JSON.stringify(inventoryApi),
},
});
// docs:end add-source

Expand Down
7 changes: 6 additions & 1 deletion examples/promise-sdk/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,14 @@ await executor.mcp.addSource({
// ---------------------------------------------------------------------------

await executor.openapi.addSpec({
spec: "https://petstore3.swagger.io/api/v3/openapi.json",
spec: {
kind: "url",
url: "https://petstore3.swagger.io/api/v3/openapi.json",
},
namespace: "petstore",
scope: "my-app",
name: "Petstore",
baseUrl: "https://petstore3.swagger.io/api/v3",
});

// With auth headers (static or secret-backed)
Expand Down
Loading
Loading