Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect openapi definitions from hono-zod-openapi apps #369

Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
23efcd5
Add simple openapi example app
brettimus Nov 19, 2024
318eff7
Add settings page for openapispecurl
brettimus Nov 19, 2024
1c47ddf
Add openapi lib file
brettimus Nov 19, 2024
98de114
Format
brettimus Nov 19, 2024
93c8892
Hack in a way to save a simple openapi description for a route
brettimus Nov 19, 2024
4caab50
Fix bad import
brettimus Nov 19, 2024
600e95e
Add an openapi route for creating a user in the example
brettimus Nov 19, 2024
3fee795
Implement automagical query params for openapi routes
brettimus Nov 20, 2024
03c23d8
Filter dead query params when changing routes
brettimus Nov 20, 2024
73a7bea
Dereference schema entries into route openapi definition used by studio
brettimus Nov 20, 2024
b1ff630
First pass at generating sample data from openapi schema for json bodies
brettimus Nov 20, 2024
a0dadfe
Comment out automagical bodies
brettimus Nov 20, 2024
c24788a
Add triple dot on hover
brettimus Nov 20, 2024
24699ad
Revert "Add triple dot on hover"
brettimus Nov 20, 2024
e678374
Merge branch 'main' into fp-3897-detect-openapi-definitions-directly-…
brettimus Nov 20, 2024
680c982
Write documentation for openapi dereferencing code
brettimus Nov 21, 2024
77b6716
Specify in UI that we only support JSON specs
brettimus Nov 21, 2024
ff791fe
Add error handler to the fetch to the openapi spec
brettimus Nov 21, 2024
e6014ad
Add tests for opneapi module
brettimus Nov 21, 2024
5071b9d
Add some basic caching to avoid infinite loops when fetching openapi …
brettimus Nov 21, 2024
0b10f0b
Remove comments
brettimus Nov 21, 2024
80c1b10
Add small changelog entry
brettimus Nov 21, 2024
7e59890
Fix logic that adds openapi specs to routes (fallback to routes)
brettimus Nov 21, 2024
3ee1a0d
Fix openapi params that specified numeric path param
brettimus Nov 21, 2024
b4bb733
Revert "Fix openapi params that specified numeric path param"
brettimus Nov 21, 2024
0836324
Fix openapi specification of params for user (example app)
brettimus Nov 21, 2024
97d91e0
Implement route to get user by id in openapi example
brettimus Nov 21, 2024
5ed86be
Update settings page copy
brettimus Nov 21, 2024
1db540c
Allow automatically detecting openapi schema when target service is H…
brettimus Nov 21, 2024
3cbaed8
Add a command bar component (not hooked up)
brettimus Nov 21, 2024
cf3c66b
Update fp-services to accept a user prompt
brettimus Nov 21, 2024
92e68de
Sneak in a little command bar to modify ai request payloads
brettimus Nov 21, 2024
95c05c4
Improve the schema validation for openapi schemas
brettimus Nov 22, 2024
07d4226
Add a docs tab
brettimus Nov 22, 2024
c5d1371
Add a Docs tab
brettimus Nov 22, 2024
a4e89e5
Implement request panel navigation in command menu
brettimus Nov 22, 2024
f79a257
Factor out openapi types
brettimus Nov 22, 2024
ce217a5
Fix issue where activeRoute did not update when method was manually c…
brettimus Nov 22, 2024
f1c1847
Fix openapi types and type imports
brettimus Nov 22, 2024
cfc3691
Add a query parameter description
brettimus Nov 25, 2024
7ac879e
Merge branch 'main' into fp-3897-detect-openapi-definitions-directly-…
brettimus Nov 26, 2024
99c3085
Add descriptions to openApiSpec fields in schemaProbedRoutes
brettimus Nov 29, 2024
9ce40d4
Improve documentation and typing of the spec fetching
brettimus Nov 29, 2024
5aab896
Remove dead code
brettimus Nov 29, 2024
c23c52f
Remove dead code
brettimus Nov 29, 2024
f0ce6e8
Update the hono-zod-openapi example
brettimus Nov 29, 2024
09ade02
Update comments about openapi types in the types pacakge
brettimus Nov 29, 2024
e03db0b
Update changelog
brettimus Nov 29, 2024
0a23055
Put the custom spec url feature behind a feature flag
brettimus Nov 29, 2024
d78c7dd
Fix visible tabs logic
brettimus Nov 29, 2024
b5f11ee
Stylize the docs tab a little tiny bit better
brettimus Nov 29, 2024
7a7d74b
more more styles
brettimus Nov 29, 2024
da59ce6
Add support for arrays to the routeDocumentation tab
brettimus Nov 29, 2024
1c50ca9
Update README for openapi example
brettimus Nov 29, 2024
133e1b4
Fix accessibility warnings in the command bar
brettimus Nov 29, 2024
3732bc6
Minor tweaks
brettimus Nov 29, 2024
4954572
Merge remote-tracking branch 'origin/main' into fp-3897-detect-openap…
brettimus Nov 29, 2024
94faf88
Merge branch 'main' into fp-3897-detect-openapi-definitions-directly-…
brettimus Dec 3, 2024
44c91d7
Update package json version for client lib
brettimus Dec 3, 2024
66a00c8
Merge branch 'main' into fp-3897-detect-openapi-definitions-directly-…
brettimus Dec 3, 2024
9aa2d9c
Update openapi description for new user route
brettimus Dec 3, 2024
1c84cc2
Update styles for docs tab
brettimus Dec 3, 2024
961b315
Remove dead code
brettimus Dec 3, 2024
df590aa
Fix logic for opening ai prompt guidance txt input
brettimus Dec 3, 2024
34b5105
Validate openapi operation in the requestpanel
brettimus Dec 3, 2024
00cb524
Bump api package.json
brettimus Dec 4, 2024
c101123
Merge branch 'main' into fp-3897-detect-openapi-definitions-directly-…
brettimus Dec 4, 2024
5e9bbfe
Update studio package.json
brettimus Dec 4, 2024
9bc9579
Fix type issue
brettimus Dec 4, 2024
8b7e391
Update comment and add lint and format scripts to openapi-zod example
brettimus Dec 4, 2024
e682c7b
Remove goosify references from teh openapi-zod package.json
brettimus Dec 4, 2024
f16fc7d
Remove double .cursorrules line from gitignore
brettimus Dec 4, 2024
9e38f23
Add fix for when file tree query throws an error and crashes the app
brettimus Dec 4, 2024
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ shared/dist
.envrc
.cursorrules

# Cursor
.cursorrules
brettimus marked this conversation as resolved.
Show resolved Hide resolved

# Fpx
.fpxconfig

Expand Down
3 changes: 3 additions & 0 deletions api/src/lib/ai/fp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type GenerateRequestOptions = {
path: string;
}[];
middlewareContext?: string;
prompt?: string;
};

export async function generateRequestWithFp({
Expand All @@ -29,6 +30,7 @@ export async function generateRequestWithFp({
openApiSpec,
middleware,
middlewareContext,
prompt,
}: GenerateRequestOptions) {
logger.debug(
"Generating request data with FP",
Expand Down Expand Up @@ -56,6 +58,7 @@ export async function generateRequestWithFp({
openApiSpec,
middleware,
middlewareContext,
prompt,
},
});

Expand Down
4 changes: 4 additions & 0 deletions api/src/lib/ai/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export async function generateRequestWithAiProvider({
openApiSpec,
middleware,
middlewareContext,
prompt,
}: {
fpApiKey?: string;
inferenceConfig: Settings;
Expand All @@ -80,6 +81,7 @@ export async function generateRequestWithAiProvider({
path: string;
}[];
middlewareContext?: string;
prompt?: string;
}) {
const { aiProviderConfigurations, aiProvider } = inferenceConfig;
const aiEnabled = hasValidAiConfig(inferenceConfig);
Expand Down Expand Up @@ -109,6 +111,7 @@ export async function generateRequestWithAiProvider({
persona,
method,
path,
prompt,
});
}

Expand Down Expand Up @@ -166,6 +169,7 @@ Here is some additional context for the handler source code, if you need it:
openApiSpec,
middleware,
middlewareContext,
prompt,
});

const systemPrompt = getSystemPrompt(persona, aiProvider);
Expand Down
8 changes: 8 additions & 0 deletions api/src/lib/ai/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const invokeRequestGenerationPrompt = async ({
openApiSpec,
middleware,
middlewareContext,
prompt,
}: {
persona: string;
method: string;
Expand All @@ -56,6 +57,7 @@ export const invokeRequestGenerationPrompt = async ({
path: string;
}[];
middlewareContext?: string;
prompt?: string;
}) => {
const promptTemplate =
persona === "QA" ? qaTesterPrompt : friendlyTesterPrompt;
Expand All @@ -68,6 +70,7 @@ export const invokeRequestGenerationPrompt = async ({
openApiSpec: openApiSpec ?? "NO OPENAPI SPEC",
middleware: formatMiddleware(middleware),
middlewareContext: middlewareContext ?? "NO MIDDLEWARE CONTEXT",
prompt: prompt ?? "",
});
const userPrompt = userPromptInterface.value;
return userPrompt;
Expand Down Expand Up @@ -107,6 +110,8 @@ Here is the code for the handler:
Here is some additional context for the handler source code, if you need it:
{handlerContext}

Here are some additional instructions from the user:
{prompt}
`.trim(),
);

Expand Down Expand Up @@ -139,6 +144,9 @@ Here is the code for the handler:
Here is some additional context for the handler source code, if you need it:
{handlerContext}

Here are some additional instructions from the user:
{prompt}

REMEMBER YOU ARE A QA. MISUSE THE API. BUT DO NOT MISUSE YOURSELF.
Keep your responses short-ish. Including your random data.
`.trim(),
Expand Down
10 changes: 10 additions & 0 deletions api/src/lib/app-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@ import * as schema from "../db/schema.js";
const { appRoutes } = schema;

export const schemaProbedRoutes = z.object({
openApiSpec: z
.unknown()
.nullish()
.describe("OpenAPI spec for the entire api"),
routes: z.array(
z.object({
method: z.string(),
path: z.string(),
handler: z.string(),
handlerType: z.string(),
openApiSpec: z
.string()
.nullish()
.describe("OpenAPI spec for the singular route"),
}),
),
});
Expand Down Expand Up @@ -70,6 +78,8 @@ export async function reregisterRoutes(
handler: route.handler,
currentlyRegistered: true,
registrationOrder: index,
openApiSpec:
route.handlerType === "route" ? route.openApiSpec : null,
})
.where(eq(appRoutes.id, routeToUpdate.id));
} else {
Expand Down
145 changes: 145 additions & 0 deletions api/src/lib/openapi/dereference.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { describe, expect, it } from "vitest";
import {
CircularReferenceError,
MissingReferenceError,
dereferenceSchema,
resolveRef,
} from "./dereference.js";

describe("resolveRef", () => {
const mockComponents = {
schemas: {
Pet: {
type: "object",
properties: {
name: { type: "string" },
age: { type: "number" },
},
},
Error: {
type: "object",
properties: {
code: { type: "integer" },
message: { type: "string" },
},
},
},
};

it("should resolve a simple reference", () => {
const result = resolveRef("#/components/schemas/Pet", mockComponents);
expect(result).toEqual(mockComponents.schemas.Pet);
});

it("should use cache for repeated references", () => {
const cache = new Map();
const ref = "#/components/schemas/Pet";

const result1 = resolveRef(ref, mockComponents, new Set(), cache);
const result2 = resolveRef(ref, mockComponents, new Set(), cache);

expect(result1).toEqual(result2);
expect(cache.get(ref)).toBeDefined();
});

it("should throw MissingReferenceError for non-existent reference", () => {
expect(() =>
resolveRef("#/components/schemas/NonExistent", mockComponents),
).toThrow(MissingReferenceError);
});

it("should throw CircularReferenceError for circular references", () => {
const componentsWithCircular = {
schemas: {
A: { $ref: "#/components/schemas/B" },
B: { $ref: "#/components/schemas/A" },
},
};

expect(() =>
resolveRef("#/components/schemas/A", componentsWithCircular),
).toThrow(CircularReferenceError);
});
});

describe("dereferenceSchema", () => {
const mockComponents = {
schemas: {
Pet: {
type: "object",
properties: {
name: { type: "string" },
},
},
Error: {
type: "object",
properties: {
code: { type: "integer" },
},
},
},
};

it("should dereference a simple schema", () => {
const input = {
type: "object",
properties: {
pet: { $ref: "#/components/schemas/Pet" },
},
};

const expected = {
type: "object",
properties: {
pet: mockComponents.schemas.Pet,
},
};

const result = dereferenceSchema(input, mockComponents);
expect(result).toEqual(expected);
});

it("should handle nested references", () => {
const input = {
type: "object",
properties: {
pet: { $ref: "#/components/schemas/Pet" },
error: { $ref: "#/components/schemas/Error" },
},
};

const result = dereferenceSchema(input, mockComponents);
expect(result.properties.pet).toEqual(mockComponents.schemas.Pet);
expect(result.properties.error).toEqual(mockComponents.schemas.Error);
});

it("should handle arrays of references", () => {
const input = {
type: "array",
items: [
{ $ref: "#/components/schemas/Pet" },
{ $ref: "#/components/schemas/Error" },
],
};

const result = dereferenceSchema(input, mockComponents);
expect(result.items[0]).toEqual(mockComponents.schemas.Pet);
expect(result.items[1]).toEqual(mockComponents.schemas.Error);
});

it("should return primitive values as-is", () => {
const input = {
type: "string",
format: "email",
};

const result = dereferenceSchema(input, mockComponents);
expect(result).toEqual(input);
});

it("should handle empty objects", () => {
const input = {};
const result = dereferenceSchema(input, mockComponents);
expect(result).toEqual({});
});
});
Loading
Loading