Skip to content

Commit

Permalink
feat(code-gen): always expose the Compas structure in OpenAPI format
Browse files Browse the repository at this point in the history
This allows interop with a new OpenAPI based code-generator
  • Loading branch information
dirkdev98 committed Aug 22, 2024
1 parent b395789 commit 18cc26f
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 3 deletions.
43 changes: 43 additions & 0 deletions examples/default/src/structure.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { mainTestFn, test } from "@compas/cli";
import { closeTestApp, createTestAppAndClient } from "@compas/server";
import axios from "axios";
import { axiosInterceptErrorAndWrapWithAppError } from "./generated/application/common/api-client.js";
import { apiCompasStructure } from "./generated/application/compas/apiClient.js";
import { app } from "./services/core.js";

mainTestFn(import.meta);

test("structure/controller", async (t) => {
// Run the Koa instance on a random port and create an Axios instance that uses that as
// the baseUrl.
const axiosInstance = axios.create();
await createTestAppAndClient(app, axiosInstance);
axiosInterceptErrorAndWrapWithAppError(axiosInstance);

t.test("compas structure", async (t) => {
const results = await Promise.all([
apiCompasStructure(axiosInstance, {}),
apiCompasStructure(axiosInstance, { format: "compas" }),
]);

t.deepEqual(results[0], results[1]);
});

t.test("openapi structure", async (t) => {
const results = await Promise.all([
apiCompasStructure(axiosInstance, {}),
apiCompasStructure(axiosInstance, { format: "openapi" }),
]);

t.notEqual(JSON.stringify(results[0]), JSON.stringify(results[1]));
});

t.test("teardown", async (t) => {
// Since subtests run in the order they are registered, we can always do some
// teardown in the last subtest.

await closeTestApp(app);

t.pass();
});
});
2 changes: 1 addition & 1 deletion packages/code-gen/src/open-api/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function openApiGenerate(generateContext) {
/**
* @param {import("../generate.js").GenerateContext} generateContext
*/
function openApiBuildFile(generateContext) {
export function openApiBuildFile(generateContext) {
const openApiSpec = merge(
{
openapi: "3.0.3",
Expand Down
53 changes: 52 additions & 1 deletion packages/code-gen/src/processors/route-structure.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { AnyType, RouteBuilder } from "../builders/index.js";
import {
AnyType,
ObjectType,
ReferenceType,
RouteBuilder,
StringType,
} from "../builders/index.js";
import { Generator } from "../generator.js";
import { openApiBuildFile } from "../open-api/generator.js";
import { structureRoutes } from "./routes.js";
import { structureAddType, structureNamedTypes } from "./structure.js";

Expand All @@ -9,6 +16,12 @@ import { structureAddType, structureNamedTypes } from "./structure.js";
* @type {WeakMap<object, string>}
*/
const routeStructureCache = new WeakMap();
/**
* Cache the openapi structure string based on the current generate context.
*
* @type {WeakMap<object, string>}
*/
const openApiStructureCache = new WeakMap();

/**
* Extract the route structure from generate context
Expand Down Expand Up @@ -55,10 +68,38 @@ export function routeStructureCreate(generateContext) {
(v) => `\\${v}`,
),
);
openApiStructureCache.set(
generateContext,
JSON.stringify(
openApiBuildFile({
structure: routesOnlyGenerator.internalStructure,
options: {
generators: {
openApi: {
openApiExtensions: {},
openApiRouteExtensions: {},
},
},
},
}),
).replace(/([`\\])/gm, (v) => `\\${v}`),
);

structureAddType(
generateContext.structure,
new ObjectType("compas", "structureQuery")
.keys({
format: new StringType().oneOf("compas", "openapi").default(`"compas"`),
})
.build(),
{
skipReferenceExtraction: false,
},
);
structureAddType(
generateContext.structure,
new RouteBuilder("GET", "compas", "structure", "_compas/structure.json")
.query(new ReferenceType("compas", "structureQuery"))
.response(new AnyType())
.tags("_compas")
.docs("Return the full available API structure")
Expand All @@ -78,3 +119,13 @@ export function routeStructureCreate(generateContext) {
export function routeStructureGet(generateContext) {
return routeStructureCache.get(generateContext) ?? "";
}

/**
* Get the saved route structure in OpenApi format
*
* @param {import("../generate.js").GenerateContext} generateContext
* @returns {string}
*/
export function routeStructureGetOpenApi(generateContext) {
return openApiStructureCache.get(generateContext) ?? "";
}
9 changes: 8 additions & 1 deletion packages/code-gen/src/router/js-koa.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import {
fileContextSetIndent,
} from "../file/context.js";
import { fileWrite } from "../file/write.js";
import { routeStructureGet } from "../processors/route-structure.js";
import {
routeStructureGet,
routeStructureGetOpenApi,
} from "../processors/route-structure.js";
import { JavascriptImportCollector } from "../target/javascript.js";
import { typesCacheGet } from "../types/cache.js";
import {
Expand Down Expand Up @@ -433,7 +436,11 @@ export function jsKoaRegisterCompasStructureRoute(generateContext, file) {
`compasHandlers.structure = (ctx) => {
ctx.set("Content-Type", "application/json");
if (ctx.validatedQuery.format === "compas") {
ctx.body = \`${routeStructureGet(generateContext)}\`;
} else if (ctx.validatedQuery.format === "openapi") {
ctx.body = \`${routeStructureGetOpenApi(generateContext)}\`;
}
};
`,
);
Expand Down

0 comments on commit 18cc26f

Please sign in to comment.