History

Migrate sources to IR catalog snapshots

Rhys Sullivan committed 8 months ago

Commit 5e6d0591 parent 31ad09078 files · +7562 −3187Browse files

Showing the changes to packages/codemode-openapi/src (8 of 78 files). Show all

Unified · Split

8 files
  1. packages/codemode-openapi/src/index.ts +3 −3
  2. packages/codemode-openapi/src/openapi-definitions.ts +2 −3
  3. packages/codemode-openapi/src/openapi-extraction.real-specs.test.ts +3 −3
  4. packages/codemode-openapi/src/openapi-schema-refs.ts +15 −25
  5. packages/codemode-openapi/src/openapi-tool-presentation.ts +71 −49
  6. packages/codemode-openapi/src/openapi-tools.test.ts +2 −2
  7. packages/codemode-openapi/src/openapi-tools.ts +23 −15
  8. packages/codemode-openapi/src/openapi-types.ts +2 −2
packages/codemode-openapi/src/index.ts+3−3
@@ -8,13 +8,13 @@ export { · 7 unmodified lines
8} from "./openapi-extraction";8} from "./openapi-extraction";
9export {9export {
10 compileOpenApiToolDefinitions,10 compileOpenApiToolDefinitions,
11 openApiProviderDataJsonFromDefinition,11 openApiProviderDataFromDefinition,
12 OpenApiToolDefinitionSchema,12 OpenApiToolDefinitionSchema,
13 type OpenApiToolDefinition,13 type OpenApiToolDefinition,
14} from "./openapi-definitions";14} from "./openapi-definitions";
15export {15export {
16 buildOpenApiToolPresentation,16 buildOpenApiToolPresentation,
17 openApiOutputTypeSignatureFromSchemaJson,17 openApiOutputTypeSignatureFromSchema,
18 type OpenApiToolPresentation,18 type OpenApiToolPresentation,
19} from "./openapi-tool-presentation";19} from "./openapi-tool-presentation";
20export {20export {
@@ -23,7 +23,7 @@ export { · 2 unmodified lines
23 createOpenApiToolsFromManifest,23 createOpenApiToolsFromManifest,
24 createOpenApiToolsFromSpec,24 createOpenApiToolsFromSpec,
25} from "./openapi-tools";25} from "./openapi-tools";
26export { resolveSchemaJsonWithRefHints, resolveTypingSchemasWithRefHints } from "./openapi-schema-refs";26export { resolveSchemaWithRefHints, resolveTypingSchemasWithRefHints } from "./openapi-schema-refs";
27export {27export {
28 OPEN_API_HTTP_METHODS,28 OPEN_API_HTTP_METHODS,
29 OPEN_API_PARAMETER_LOCATIONS,29 OPEN_API_PARAMETER_LOCATIONS,
packages/codemode-openapi/src/openapi-definitions.ts+2−3
@@ -236,10 +236,9 @@ export const compileOpenApiToolDefinitions = ( · 235 unmodified lines
236 );236 );
237};237};
238 238
239export const openApiProviderDataJsonFromDefinition = (239export const openApiProviderDataFromDefinition = (
240 definition: OpenApiToolDefinition,240 definition: OpenApiToolDefinition,
241): string =>241): typeof OpenApiToolProviderDataSchema.Type => ({
242 JSON.stringify({
243 kind: "openapi",242 kind: "openapi",
244 toolId: definition.toolId,243 toolId: definition.toolId,
245 rawToolId: definition.rawToolId,244 rawToolId: definition.rawToolId,
packages/codemode-openapi/src/openapi-extraction.real-specs.test.ts+3−3
@@ -26,8 +26,8 @@ type ParsedInputSchema = { · 25 unmodified lines
26 required?: Array<string>;26 required?: Array<string>;
27};27};
28 28
29const parseInputSchema = (inputSchemaJson: string | undefined): ParsedInputSchema | null =>29const parseInputSchema = (inputSchema: unknown): ParsedInputSchema | null =>
30 inputSchemaJson ? (JSON.parse(inputSchemaJson) as ParsedInputSchema) : null;30 inputSchema ? (inputSchema as ParsedInputSchema) : null;
31 31
32const expectedTools = [32const expectedTools = [
33 {33 {
@@ -89,7 +89,7 @@ describe("openapi-extraction real specs", () => { · 55 unmodified lines
89 expect(tool?.path).toBe(expectedTool.path);89 expect(tool?.path).toBe(expectedTool.path);
90 expect(tool?.tags).toEqual(expectedTool.tags);90 expect(tool?.tags).toEqual(expectedTool.tags);
91 91
92 const inputSchema = parseInputSchema(tool?.typing?.inputSchemaJson);92 const inputSchema = parseInputSchema(tool?.typing?.inputSchema);
93 expect(inputSchema?.type).toBe("object");93 expect(inputSchema?.type).toBe("object");
94 expect(Object.keys(inputSchema?.properties ?? {})).toEqual(94 expect(Object.keys(inputSchema?.properties ?? {})).toEqual(
95 expect.arrayContaining([...expectedTool.requiredInputs]),95 expect.arrayContaining([...expectedTool.requiredInputs]),
packages/codemode-openapi/src/openapi-schema-refs.ts+15−25
@@ -2,8 +2,8 @@ type JsonObject = Record<string, unknown>; · 1 unmodified line
2type RefHintValue = string | JsonObject;2type RefHintValue = string | JsonObject;
3 3
4type DiscoveryTypingLike = {4type DiscoveryTypingLike = {
5 inputSchemaJson?: string;5 inputSchema?: unknown;
6 outputSchemaJson?: string;6 outputSchema?: unknown;
7};7};
8 8
9const isJsonObject = (value: unknown): value is JsonObject =>9const isJsonObject = (value: unknown): value is JsonObject =>
@@ -92,50 +92,40 @@ const resolveSchemaNode = ( · 82 unmodified lines
92 return next;92 return next;
93};93};
94 94
95export const resolveSchemaJsonWithRefHints = (95export const resolveSchemaWithRefHints = (
96 schemaJson: string | undefined,96 schema: unknown,
97 refHintTable: Readonly<Record<string, RefHintValue>> | undefined,97 refHintTable: Readonly<Record<string, RefHintValue>> | undefined,
98): string | null => {98): unknown | null => {
99 if (!schemaJson) {99 if (schema === undefined || schema === null) {
100 return null;100 return null;
101 }101 }
102 102
103 const parsedSchema = parseJson(schemaJson);
104 if (parsedSchema === null) {
105 return schemaJson;
106 }
107
108 if (!refHintTable || Object.keys(refHintTable).length === 0) {103 if (!refHintTable || Object.keys(refHintTable).length === 0) {
109 return schemaJson;104 return schema;
110 }105 }
111 106
112 const resolved = resolveSchemaNode(107 const resolved = resolveSchemaNode(
113 parsedSchema,108 schema,
114 refHintTable,109 refHintTable,
115 new Map<string, unknown>(),110 new Map<string, unknown>(),
116 new Set<string>(),111 new Set<string>(),
117 );112 );
118 113 return resolved;
119 try {
120 return JSON.stringify(resolved);
121 } catch {
122 return schemaJson;
123 }
124};114};
125 115
126export const resolveTypingSchemasWithRefHints = (116export const resolveTypingSchemasWithRefHints = (
127 typing: DiscoveryTypingLike | undefined,117 typing: DiscoveryTypingLike | undefined,
128 refHintTable: Readonly<Record<string, RefHintValue>> | undefined,118 refHintTable: Readonly<Record<string, RefHintValue>> | undefined,
129): {119): {
130 inputSchemaJson: string | null;120 inputSchema: unknown | null;
131 outputSchemaJson: string | null;121 outputSchema: unknown | null;
132} => ({122} => ({
133 inputSchemaJson: resolveSchemaJsonWithRefHints(123 inputSchema: resolveSchemaWithRefHints(
134 typing?.inputSchemaJson,124 typing?.inputSchema,
135 refHintTable,125 refHintTable,
136 ),126 ),
137 outputSchemaJson: resolveSchemaJsonWithRefHints(127 outputSchema: resolveSchemaWithRefHints(
138 typing?.outputSchemaJson,128 typing?.outputSchema,
139 refHintTable,129 refHintTable,
140 ),130 ),
141});131});
packages/codemode-openapi/src/openapi-tool-presentation.ts+71−49
@@ -1,22 +1,16 @@
1import { typeSignatureFromSchemaJson } from "@executor/codemode-core";1import { typeSignatureFromSchema } from "@executor/codemode-core";
2 2
3import type {3import type {
4 OpenApiExample,4 OpenApiExample,
5 OpenApiInvocationPayload,
5 OpenApiToolDocumentation,6 OpenApiToolDocumentation,
7 OpenApiToolProviderData,
6} from "./openapi-types";8} from "./openapi-types";
7import {9import {
8 openApiProviderDataJsonFromDefinition,10 openApiProviderDataFromDefinition,
9 type OpenApiToolDefinition,11 type OpenApiToolDefinition,
10} from "./openapi-definitions";12} from "./openapi-definitions";
11 13
12const parseJson = (value: string): unknown | undefined => {
13 try {
14 return JSON.parse(value) as unknown;
15 } catch {
16 return undefined;
17 }
18};
19
20const asRecord = (value: unknown): Record<string, unknown> =>14const asRecord = (value: unknown): Record<string, unknown> =>
21 typeof value === "object" && value !== null && !Array.isArray(value)15 typeof value === "object" && value !== null && !Array.isArray(value)
22 ? (value as Record<string, unknown>)16 ? (value as Record<string, unknown>)
@@ -32,33 +26,62 @@ const isStrictEmptyObjectSchema = (value: unknown): boolean => { · 9 unmodified lines
32 return Object.keys(properties).length === 0 && schema.additionalProperties === false;26 return Object.keys(properties).length === 0 && schema.additionalProperties === false;
33};27};
34 28
35export const openApiOutputTypeSignatureFromSchemaJson = (29export const openApiOutputTypeSignatureFromSchema = (
36 schemaJson: string | undefined,30 schema: unknown,
37 maxLength: number = 320,31 maxLength: number = 320,
38 ): string => {32 ): string => {
39 if (!schemaJson) {33 if (schema === undefined || schema === null) {
40 return "void";34 return "void";
41 }35 }
42 36
43 const parsed = parseJson(schemaJson);37 if (isStrictEmptyObjectSchema(schema)) {
44 if (parsed === undefined) {
45 return "unknown";
46 }
47
48 if (isStrictEmptyObjectSchema(parsed)) {
49 return "{}";38 return "{}";
50 }39 }
51 40
52 return typeSignatureFromSchemaJson(schemaJson, "unknown", maxLength);41 return typeSignatureFromSchema(schema, "unknown", maxLength);
53};42};
54 43
55const firstExample = (44const firstExample = (
56 examples: ReadonlyArray<OpenApiExample> | undefined,45 examples: ReadonlyArray<OpenApiExample> | undefined,
57): OpenApiExample | undefined => examples?.[0];46): OpenApiExample | undefined => examples?.[0];
58 47
59const buildExampleInputJson = (48const fallbackInputSchemaFromInvocation = (
49 invocation: OpenApiInvocationPayload,
50): Record<string, unknown> | undefined => {
51 const properties: Record<string, unknown> = {};
52 const required: string[] = [];
53
54 for (const parameter of invocation.parameters) {
55 properties[parameter.name] = {
56 type: "string",
57 };
58 if (parameter.required) {
59 required.push(parameter.name);
60 }
61 }
62
63 if (invocation.requestBody) {
64 properties.body = {
65 type: "object",
66 };
67 if (invocation.requestBody.required) {
68 required.push("body");
69 }
70 }
71
72 return Object.keys(properties).length > 0
73 ? {
74 type: "object",
75 properties,
76 ...(required.length > 0 ? { required } : {}),
77 additionalProperties: false,
78 }
79 : undefined;
80};
81
82const buildExampleInput = (
60 documentation: OpenApiToolDocumentation | undefined,83 documentation: OpenApiToolDocumentation | undefined,
61): string | undefined => {84): Record<string, unknown> | undefined => {
62 if (!documentation) {85 if (!documentation) {
63 return undefined;86 return undefined;
64 }87 }
@@ -71,52 +94,51 @@ const buildExampleInputJson = ( · 6 unmodified lines
71 continue;94 continue;
72 }95 }
73 96
74 const parsedValue = parseJson(example.valueJson);97 input[parameter.name] = JSON.parse(example.valueJson) as unknown;
75 if (parsedValue !== undefined) {
76 input[parameter.name] = parsedValue;
77 }
78 }98 }
79 99
80 const requestBodyExample = firstExample(documentation.requestBody?.examples);100 const requestBodyExample = firstExample(documentation.requestBody?.examples);
81 if (requestBodyExample) {101 if (requestBodyExample) {
82 const parsedValue = parseJson(requestBodyExample.valueJson);102 input.body = JSON.parse(requestBodyExample.valueJson) as unknown;
83 if (parsedValue !== undefined) {
84 input.body = parsedValue;
85 }
86 }103 }
87 104
88 return Object.keys(input).length > 0 ? JSON.stringify(input) : undefined;105 return Object.keys(input).length > 0 ? input : undefined;
89};106};
90 107
91const buildExampleOutputJson = (108const buildExampleOutput = (
92 documentation: OpenApiToolDocumentation | undefined,109 documentation: OpenApiToolDocumentation | undefined,
93): string | undefined => firstExample(documentation?.response?.examples)?.valueJson;110): unknown | undefined => {
111 const example = firstExample(documentation?.response?.examples)?.valueJson;
112 return example ? JSON.parse(example) as unknown : undefined;
113};
94 114
95export type OpenApiToolPresentation = {115export type OpenApiToolPresentation = {
96 inputType: string;116 inputType: string;
97 outputType: string;117 outputType: string;
98 inputSchemaJson?: string;118 inputSchema?: unknown;
99 outputSchemaJson?: string;119 outputSchema?: unknown;
100 exampleInputJson?: string;120 exampleInput?: unknown;
101 exampleOutputJson?: string;121 exampleOutput?: unknown;
102 providerDataJson: string;122 providerData: OpenApiToolProviderData;
103};123};
104 124
105export const buildOpenApiToolPresentation = (input: {125export const buildOpenApiToolPresentation = (input: {
106 definition: OpenApiToolDefinition;126 definition: OpenApiToolDefinition;
107}): OpenApiToolPresentation => {127}): OpenApiToolPresentation => {
108 const inputSchemaJson = input.definition.typing?.inputSchemaJson;128 const inputSchema =
109 const outputSchemaJson = input.definition.typing?.outputSchemaJson;129 input.definition.typing?.inputSchema
110 const exampleInputJson = buildExampleInputJson(input.definition.documentation);130 ?? fallbackInputSchemaFromInvocation(input.definition.invocation);
111 const exampleOutputJson = buildExampleOutputJson(input.definition.documentation);131 const outputSchema = input.definition.typing?.outputSchema;
132 const exampleInput = buildExampleInput(input.definition.documentation);
133 const exampleOutput = buildExampleOutput(input.definition.documentation);
112 134
113 return {135 return {
114 inputType: typeSignatureFromSchemaJson(inputSchemaJson, "unknown", Infinity),136 inputType: typeSignatureFromSchema(inputSchema, "unknown", Infinity),
115 outputType: openApiOutputTypeSignatureFromSchemaJson(outputSchemaJson, Infinity),137 outputType: openApiOutputTypeSignatureFromSchema(outputSchema, Infinity),
116 ...(inputSchemaJson ? { inputSchemaJson } : {}),138 ...(inputSchema !== undefined ? { inputSchema } : {}),
117 ...(outputSchemaJson ? { outputSchemaJson } : {}),139 ...(outputSchema !== undefined ? { outputSchema } : {}),
118 ...(exampleInputJson ? { exampleInputJson } : {}),140 ...(exampleInput !== undefined ? { exampleInput } : {}),
119 ...(exampleOutputJson ? { exampleOutputJson } : {}),141 ...(exampleOutput !== undefined ? { exampleOutput } : {}),
120 providerDataJson: openApiProviderDataJsonFromDefinition(input.definition),142 providerData: openApiProviderDataFromDefinition(input.definition),
121 };143 };
122};144};
packages/codemode-openapi/src/openapi-tools.test.ts+2−2
@@ -291,8 +291,8 @@ describe("openapi-tools", () => { · 290 unmodified lines
291 const resolvedGet = resolveToolDefinition(tools["source.demo.repos.getRepo"]!);291 const resolvedGet = resolveToolDefinition(tools["source.demo.repos.getRepo"]!);
292 expect(resolvedGet.metadata?.sourceKey).toBe("api.demo");292 expect(resolvedGet.metadata?.sourceKey).toBe("api.demo");
293 expect(resolvedGet.metadata?.providerKind).toBe("openapi");293 expect(resolvedGet.metadata?.providerKind).toBe("openapi");
294 expect(resolvedGet.metadata?.providerDataJson).toContain('"group":"repos"');294 expect(resolvedGet.metadata?.providerData).toMatchObject({ group: "repos" });
295 expect(resolvedGet.metadata?.inputSchemaJson).toBeDefined();295 expect(resolvedGet.metadata?.inputSchema).toBeDefined();
296 }),296 }),
297 );297 );
298 298
packages/codemode-openapi/src/openapi-tools.ts+23−15
@@ -23,7 +23,7 @@ import { · 22 unmodified lines
23 type OpenApiToolDefinition,23 type OpenApiToolDefinition,
24} from "./openapi-definitions";24} from "./openapi-definitions";
25import { buildOpenApiToolPresentation } from "./openapi-tool-presentation";25import { buildOpenApiToolPresentation } from "./openapi-tool-presentation";
26import { resolveSchemaJsonWithRefHints } from "./openapi-schema-refs";26import { resolveSchemaWithRefHints } from "./openapi-schema-refs";
27import {27import {
28 extractOpenApiManifest,28 extractOpenApiManifest,
29 type OpenApiExtractionError,29 type OpenApiExtractionError,
@@ -262,21 +262,21 @@ const summarizeHttpResponseBody = (body: unknown): string | null => { · 232 unmodified lines
262 }262 }
263};263};
264 264
265const inputSchemaFromTypingJson = (input: {265const inputSchemaFromTyping = (input: {
266 inputSchemaJson: string | undefined;266 inputSchema: unknown;
267 refHintTable?: Readonly<OpenApiRefHintTable>;267 refHintTable?: Readonly<OpenApiRefHintTable>;
268}) => {268}) => {
269 const resolvedSchemaJson = resolveSchemaJsonWithRefHints(269 const resolvedSchema = resolveSchemaWithRefHints(
270 input.inputSchemaJson,270 input.inputSchema,
271 input.refHintTable,271 input.refHintTable,
272 ) ?? input.inputSchemaJson;272 ) ?? input.inputSchema;
273 273
274 if (!resolvedSchemaJson) {274 if (resolvedSchema === undefined || resolvedSchema === null) {
275 return unknownInputSchema;275 return unknownInputSchema;
276 }276 }
277 277
278 try {278 try {
279 return standardSchemaFromJsonSchema(JSON.parse(resolvedSchemaJson), {279 return standardSchemaFromJsonSchema(resolvedSchema, {
280 vendor: "openapi",280 vendor: "openapi",
281 fallback: unknownInputSchema,281 fallback: unknownInputSchema,
282 });282 });
@@ -469,8 +469,8 @@ export const createOpenApiToolFromDefinition = ( · 186 unmodified lines
469 return toTool({469 return toTool({
470 tool: {470 tool: {
471 description: input.definition.description,471 description: input.definition.description,
472 inputSchema: inputSchemaFromTypingJson({472 inputSchema: inputSchemaFromTyping({
473 inputSchemaJson: presentation.inputSchemaJson ?? input.definition.typing?.inputSchemaJson,473 inputSchema: presentation.inputSchema ?? input.definition.typing?.inputSchema,
474 refHintTable: input.refHintTable,474 refHintTable: input.refHintTable,
475 }),475 }),
476 execute: async (args: unknown) => {476 execute: async (args: unknown) => {
@@ -526,12 +526,20 @@ export const createOpenApiToolFromDefinition = ( · 49 unmodified lines
526 sourceKey: input.sourceKey,526 sourceKey: input.sourceKey,
527 inputType: presentation.inputType,527 inputType: presentation.inputType,
528 outputType: presentation.outputType,528 outputType: presentation.outputType,
529 inputSchemaJson: presentation.inputSchemaJson,529 ...((presentation.inputSchema ?? input.definition.typing?.inputSchema) !== undefined
530 outputSchemaJson: presentation.outputSchemaJson,530 ? { inputSchema: presentation.inputSchema ?? input.definition.typing?.inputSchema }
531 exampleInputJson: presentation.exampleInputJson,531 : {}),
532 exampleOutputJson: presentation.exampleOutputJson,532 ...((presentation.outputSchema ?? input.definition.typing?.outputSchema) !== undefined
533 ? { outputSchema: presentation.outputSchema ?? input.definition.typing?.outputSchema }
534 : {}),
535 ...(presentation.exampleInput !== undefined
536 ? { exampleInput: presentation.exampleInput }
537 : {}),
538 ...(presentation.exampleOutput !== undefined
539 ? { exampleOutput: presentation.exampleOutput }
540 : {}),
533 providerKind: "openapi",541 providerKind: "openapi",
534 providerDataJson: presentation.providerDataJson,542 providerData: presentation.providerData,
535 },543 },
536 });544 });
537};545};
packages/codemode-openapi/src/openapi-types.ts+2−2
@@ -58,8 +58,8 @@ export const OpenApiInvocationPayloadSchema = Schema.Struct({ · 57 unmodified lines
58});58});
59 59
60export const DiscoveryTypingPayloadSchema = Schema.Struct({60export const DiscoveryTypingPayloadSchema = Schema.Struct({
61 inputSchemaJson: Schema.optional(Schema.String),61 inputSchema: Schema.optional(Schema.Unknown),
62 outputSchemaJson: Schema.optional(Schema.String),62 outputSchema: Schema.optional(Schema.Unknown),
63 refHintKeys: Schema.optional(Schema.Array(Schema.String)),63 refHintKeys: Schema.optional(Schema.Array(Schema.String)),
64});64});
65 65