import { expect, it } from "@effect/vitest";import { Effect, Layer } from "effect";import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";import { createExecutor, IntegrationSlug } from "@executor-js/sdk";import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing";import { openApiPlugin, parse } from "@executor-js/plugin-openapi";import type { AuthenticationInput } from "@executor-js/plugin-openapi"; import { deriveGoogleDiscoveryIdentity, googleDiscoveryAdapter } from "./spec-format-adapter";import { googleCatalog } from "./presets"; const TASKS_URL = "https://www.googleapis.com/discovery/v1/apis/tasks/v1/rest"; const tasksDiscoveryDoc = { name: "tasks", version: "v1", title: "Google Tasks API", description: "Manage your tasks and task lists.", rootUrl: "https://tasks.googleapis.com/", servicePath: "", auth: { oauth2: { scopes: { "https://www.googleapis.com/auth/tasks": { description: "Create, edit, organize, and delete all your tasks.", }, }, }, }, methods: { tasklistsList: { id: "tasks.tasklists.list", httpMethod: "GET", path: "tasks/v1/users/@me/lists", scopes: ["https://www.googleapis.com/auth/tasks"], response: { $ref: "TaskLists" }, }, }, schemas: { TaskLists: { type: "object", properties: { items: { type: "array", items: { $ref: "TaskList" }, }, }, }, TaskList: { type: "object", properties: { id: { type: "string" }, title: { type: "string" }, }, }, },}; const discoveryHttpClientLayer = Layer.succeed(HttpClient.HttpClient)( HttpClient.make((request: HttpClientRequest.HttpClientRequest) => Effect.succeed( HttpClientResponse.fromWeb( request, new Response(JSON.stringify(tasksDiscoveryDoc), { status: request.url === TASKS_URL ? 200 : 404, headers: { "content-type": "application/json" }, }), ), ), ),); it.effect("fetches and converts a Google Discovery document", () => Effect.gen(function* () { const converted = yield* googleDiscoveryAdapter.fetch({ urls: [TASKS_URL], httpClientLayer: discoveryHttpClientLayer, }); const parsed = yield* parse(converted.specText); expect(parsed.info.title).toBe("Google"); expect(Object.keys(parsed.paths ?? {})).toContain("/tasks/v1/users/@me/lists"); expect(converted.authenticationTemplate?.[0]?.kind).toBe("oauth2"); }),); it("derives Google Discovery identity from the raw document", () => { expect(deriveGoogleDiscoveryIdentity(tasksDiscoveryDoc)).toEqual({ slug: "google_tasks", name: "Google Tasks API", description: "Manage your tasks and task lists.", });}); it.effect("adds a Google Discovery URL through the OpenAPI plugin with derived identity", () => Effect.gen(function* () { const executor = yield* createExecutor( makeTestConfig({ plugins: [ openApiPlugin({ httpClientLayer: discoveryHttpClientLayer, specFormats: [googleDiscoveryAdapter], }), memoryCredentialsPlugin(), ], }), ); const added = yield* executor.openapi.addSpec({ spec: { kind: "url", url: TASKS_URL }, specFormat: "google-discovery", }); const integration = yield* executor.openapi.getIntegration("google_tasks"); expect(String(added.slug)).toBe("google_tasks"); expect(integration?.slug).toEqual(IntegrationSlug.make("google_tasks")); expect(added.toolCount).toBe(1); }),); it.effect( "adds a Google catalog preset through OpenAPI with family, format, and default slug", () => Effect.gen(function* () { const tasksPreset = googleCatalog.find((preset) => preset.defaultSlug === "google_tasks")!; const authTemplate: readonly AuthenticationInput[] = (tasksPreset.authTemplate ?? []).flatMap( (template) => (template.kind === "oauth2" ? [template] : []), ); const executor = yield* createExecutor( makeTestConfig({ plugins: [ openApiPlugin({ httpClientLayer: discoveryHttpClientLayer, presets: [tasksPreset], specFormats: [googleDiscoveryAdapter], }), memoryCredentialsPlugin(), ], }), ); const added = yield* executor.openapi.addSpec({ spec: { kind: "url", url: tasksPreset.url! }, slug: tasksPreset.defaultSlug, specFormat: tasksPreset.specFormat, family: tasksPreset.family, authenticationTemplate: authTemplate, }); const config = yield* executor.openapi.getConfig("google_tasks"); expect(String(added.slug)).toBe("google_tasks"); expect(config?.family).toBe("google"); expect(config?.specFormat).toBe("google-discovery"); expect(config?.authenticationTemplate?.[0]?.kind).toBe("oauth2"); const storedOAuthTemplates = (config?.authenticationTemplate ?? []).filter( (template) => template.kind === "oauth2", ); expect(storedOAuthTemplates[0]?.scopes).toEqual( expect.arrayContaining([ "openid", "email", "profile", "https://www.googleapis.com/auth/tasks", ]), ); }),);
import { expect, it } from "@effect/vitest";
import { Effect, Layer } from "effect";
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
import { createExecutor, IntegrationSlug } from "@executor-js/sdk";
import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing";
import { openApiPlugin, parse } from "@executor-js/plugin-openapi";
import type { AuthenticationInput } from "@executor-js/plugin-openapi";
import { deriveGoogleDiscoveryIdentity, googleDiscoveryAdapter } from "./spec-format-adapter";
import { googleCatalog } from "./presets";
const TASKS_URL = "https://www.googleapis.com/discovery/v1/apis/tasks/v1/rest";
const tasksDiscoveryDoc = {
name: "tasks",
version: "v1",
title: "Google Tasks API",
description: "Manage your tasks and task lists.",
rootUrl: "https://tasks.googleapis.com/",
servicePath: "",
auth: {
oauth2: {
scopes: {
"https://www.googleapis.com/auth/tasks": {
description: "Create, edit, organize, and delete all your tasks.",
},
},
},
},
methods: {
tasklistsList: {
id: "tasks.tasklists.list",
httpMethod: "GET",
path: "tasks/v1/users/@me/lists",
scopes: ["https://www.googleapis.com/auth/tasks"],
response: { $ref: "TaskLists" },
},
},
schemas: {
TaskLists: {
type: "object",
properties: {
items: {
type: "array",
items: { $ref: "TaskList" },
},
},
},
TaskList: {
type: "object",
properties: {
id: { type: "string" },
title: { type: "string" },
},
},
},
};
const discoveryHttpClientLayer = Layer.succeed(HttpClient.HttpClient)(
HttpClient.make((request: HttpClientRequest.HttpClientRequest) =>
Effect.succeed(
HttpClientResponse.fromWeb(
request,
new Response(JSON.stringify(tasksDiscoveryDoc), {
status: request.url === TASKS_URL ? 200 : 404,
headers: { "content-type": "application/json" },
}),
),
),
),
);
it.effect("fetches and converts a Google Discovery document", () =>
Effect.gen(function* () {
const converted = yield* googleDiscoveryAdapter.fetch({
urls: [TASKS_URL],
httpClientLayer: discoveryHttpClientLayer,
});
const parsed = yield* parse(converted.specText);
expect(parsed.info.title).toBe("Google");
expect(Object.keys(parsed.paths ?? {})).toContain("/tasks/v1/users/@me/lists");
expect(converted.authenticationTemplate?.[0]?.kind).toBe("oauth2");
}),
);
it("derives Google Discovery identity from the raw document", () => {
expect(deriveGoogleDiscoveryIdentity(tasksDiscoveryDoc)).toEqual({
slug: "google_tasks",
name: "Google Tasks API",
description: "Manage your tasks and task lists.",
});
});
it.effect("adds a Google Discovery URL through the OpenAPI plugin with derived identity", () =>
Effect.gen(function* () {
const executor = yield* createExecutor(
makeTestConfig({
plugins: [
openApiPlugin({
httpClientLayer: discoveryHttpClientLayer,
specFormats: [googleDiscoveryAdapter],
}),
memoryCredentialsPlugin(),
],
}),
);
const added = yield* executor.openapi.addSpec({
spec: { kind: "url", url: TASKS_URL },
specFormat: "google-discovery",
});
const integration = yield* executor.openapi.getIntegration("google_tasks");
expect(String(added.slug)).toBe("google_tasks");
expect(integration?.slug).toEqual(IntegrationSlug.make("google_tasks"));
expect(added.toolCount).toBe(1);
}),
);
it.effect(
"adds a Google catalog preset through OpenAPI with family, format, and default slug",
() =>
Effect.gen(function* () {
const tasksPreset = googleCatalog.find((preset) => preset.defaultSlug === "google_tasks")!;
const authTemplate: readonly AuthenticationInput[] = (tasksPreset.authTemplate ?? []).flatMap(
(template) => (template.kind === "oauth2" ? [template] : []),
);
const executor = yield* createExecutor(
makeTestConfig({
plugins: [
openApiPlugin({
httpClientLayer: discoveryHttpClientLayer,
presets: [tasksPreset],
specFormats: [googleDiscoveryAdapter],
}),
memoryCredentialsPlugin(),
],
}),
);
const added = yield* executor.openapi.addSpec({
spec: { kind: "url", url: tasksPreset.url! },
slug: tasksPreset.defaultSlug,
specFormat: tasksPreset.specFormat,
family: tasksPreset.family,
authenticationTemplate: authTemplate,
});
const config = yield* executor.openapi.getConfig("google_tasks");
expect(String(added.slug)).toBe("google_tasks");
expect(config?.family).toBe("google");
expect(config?.specFormat).toBe("google-discovery");
expect(config?.authenticationTemplate?.[0]?.kind).toBe("oauth2");
const storedOAuthTemplates = (config?.authenticationTemplate ?? []).filter(
(template) => template.kind === "oauth2",
);
expect(storedOAuthTemplates[0]?.scopes).toEqual(
expect.arrayContaining([
"openid",
"email",
"profile",
"https://www.googleapis.com/auth/tasks",
]),
);
}),
);