Skip to content
Open
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
20 changes: 15 additions & 5 deletions src/generators/core/endpoints/getEndpointParameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import { getEnumZodSchemaCodeFromEnumNames, getZodSchema } from "@/generators/co
import { resolveZodSchemaName } from "@/generators/core/zod/resolveZodSchemaName";
import { EndpointParameter } from "@/generators/types/endpoint";
import { ParameterObject } from "@/generators/types/openapi";
import { isSchemaObject } from "@/generators/utils/openapi-schema.utils";
import { isReferenceObject, isSchemaObject } from "@/generators/utils/openapi-schema.utils";
import {
isParamMediaTypeAllowed,
getParameterEnumNames,
isSortingParameterObject,
pathParamToVariableName,
} from "@/generators/utils/openapi.utils";
import { resolveQueryFilterComponentName } from "@/generators/utils/query-filter-component-names.utils";
import {
getEnumZodSchemaName,
getParamZodSchemaName,
Expand All @@ -25,12 +26,14 @@ export function getEndpointParameter({
resolver,
param,
operationName,
operationId,
isUniqueOperationName,
tag,
}: {
resolver: SchemaResolver;
param: OpenAPIV3.ReferenceObject | ParameterObject;
operationName: string;
operationId?: string;
isUniqueOperationName: boolean;
tag: string;
}): EndpointParameter | undefined {
Expand Down Expand Up @@ -65,10 +68,17 @@ export function getEndpointParameter({
(schema as OpenAPIV3.SchemaObject).description = (paramObj.description ?? "").trim();
}

const fallbackName = getParamZodSchemaName(
getZodSchemaOperationName(operationName, isUniqueOperationName, tag),
paramObj.name,
);
const zodSchemaOperationName = getZodSchemaOperationName(operationName, isUniqueOperationName, tag);
const legacyFilterComponentName =
paramObj.name === resolver.options.filterParamName &&
!isReferenceObject(schema) &&
resolver.options.queryFilterComponentNames
? resolveQueryFilterComponentName(operationId, resolver.options.queryFilterComponentNames)
: undefined;

const fallbackName =
legacyFilterComponentName ??
getParamZodSchemaName(zodSchemaOperationName, paramObj.name);

let parameterSortingEnumSchemaName: string | undefined = undefined;
if (isSortingParameterObject(paramObj, schema, resolver)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export function getEndpointsFromOpenAPIDoc(resolver: SchemaResolver) {
resolver,
param,
operationName,
operationId: operation.operationId,
isUniqueOperationName,
tag,
});
Expand Down
13 changes: 12 additions & 1 deletion src/generators/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ interface BuilderConfigsGenerateOptions {
dynamicColumnsImportPath: string;
}

interface QueryFilterComponentNameOptions {
/**
* Optional map of OpenAPI operationId to stable filter schema names.
* Used for inline `filter` query objects when migrating away from named filter DTO refs.
*/
queryFilterComponentNames?: Record<string, string>;
}

interface GenerateConfig {
outputFileNameSuffix: string;
namespaceSuffix: string;
Expand All @@ -70,6 +78,8 @@ interface BaseGenerateOptions {
clearOutput?: boolean;
incremental?: boolean;
splitByTags: boolean;
/** When true, non-default tags emit proxy models that re-export from CommonModels. */
modelsInCommon?: boolean;
defaultTag: string;
includeTags: string[];
excludeTags: string[];
Expand All @@ -92,4 +102,5 @@ export interface GenerateOptions
QueriesGenerateOptions,
InfiniteQueriesGenerateOptions,
ACLGenerateOptions,
BuilderConfigsGenerateOptions {}
BuilderConfigsGenerateOptions,
QueryFilterComponentNameOptions {}
4 changes: 2 additions & 2 deletions src/generators/utils/generate/generate.imports.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { getImportPath } from "./generate.imports.utils";

describe("Utils: generate imports", () => {
test("getImportPath", () => {
expect(getImportPath({ output: "output", importPath: "ts", tsPath: "@/data" })).toEqual("@/data/");
expect(getImportPath({ output: "output", importPath: "ts", tsPath: "@/data" })).toEqual("../");
expect(getImportPath({ output: "output", importPath: "ts", tsPath: "@/output" })).toEqual("@/output/");
expect(getImportPath({ output: "output", importPath: "relative", tsPath: "@/data" })).toEqual("../");
expect(getImportPath({ output: "output", importPath: "absolute", tsPath: "@/data" })).toEqual("output/");
expect(getImportPath({ output: "some-folder", importPath: "ts", tsPath: "@/data" })).toEqual("@/data/");
expect(getImportPath({ output: "some-folder", importPath: "ts", tsPath: "@/data" })).toEqual("../");
expect(getImportPath({ output: "some-folder", importPath: "ts", tsPath: "@/output" })).toEqual("@/output/");
expect(getImportPath({ output: "some-folder", importPath: "relative", tsPath: "@/data" })).toEqual("../");
expect(getImportPath({ output: "some-folder", importPath: "absolute", tsPath: "@/data" })).toEqual("some-folder/");
Expand Down
42 changes: 35 additions & 7 deletions src/generators/utils/generate/generate.imports.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,44 @@ export function getEntityImports({
return Array.from(imports.values());
}

function normalizeOutputPath(output: string) {
return output.replace(/\\/g, "/").replace(/\/$/, "");
}

function outputMatchesTsPath(options: Pick<GenerateOptions, "output" | "tsPath">) {
const normalizedOutput = normalizeOutputPath(options.output);

if (options.tsPath.startsWith("@/")) {
const physicalPath = `src/${options.tsPath.slice(2)}`;
return normalizedOutput === physicalPath || normalizedOutput.endsWith(`/${physicalPath}`);
}

return normalizedOutput === options.tsPath;
}

function shouldUseRelativeImports(options: Pick<GenerateOptions, "output" | "importPath" | "tsPath">) {
return options.importPath === "ts" && options.tsPath === "@/data" && !outputMatchesTsPath(options);
}

export function getImportPath(options: Pick<GenerateOptions, "output" | "importPath" | "tsPath">, fromRoot = false) {
let importPath = options.tsPath;
if (options.importPath === "relative") {
importPath = fromRoot ? "./" : "../";
} else if (options.importPath === "absolute") {
importPath = options.output;
} else if (new RegExp(`${TEMPLATE_DATA_FILE_PATH}`, "g").test(options.output)) {
importPath = options.output.replace(new RegExp(`.*${TEMPLATE_DATA_FILE_PATH}`, "g"), options.tsPath);
return `${fromRoot ? "./" : "../"}`.replace(/\/\//g, "/");
}

if (options.importPath === "absolute") {
return `${options.output}/`.replace(/\/\//g, "/");
}

if (new RegExp(`${TEMPLATE_DATA_FILE_PATH}`, "g").test(options.output)) {
const importPath = options.output.replace(new RegExp(`.*${TEMPLATE_DATA_FILE_PATH}`, "g"), options.tsPath);
return `${importPath}/`.replace(/\/\//g, "/");
}

if (shouldUseRelativeImports(options)) {
return `${fromRoot ? "./" : "../"}`.replace(/\/\//g, "/");
}
return `${importPath}/`.replace(/\/\//g, "/");

return `${options.tsPath}/`.replace(/\/\//g, "/");
}

function getImports<T>({
Expand Down
25 changes: 25 additions & 0 deletions src/generators/utils/query-filter-component-names.utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";

import { resolveQueryFilterComponentName } from "./query-filter-component-names.utils";

describe("resolveQueryFilterComponentName", () => {
const queryFilterComponentNames = {
BusinessPartnersController_paginate: "BusinessPartnerFilterDto",
VatRuleController_list: "VatRuleFilterDto",
};

it("maps operation ids to configured filter component names", () => {
expect(
resolveQueryFilterComponentName("BusinessPartnersController_paginate", queryFilterComponentNames),
).toBe("BusinessPartnerFilterDto");
expect(resolveQueryFilterComponentName("VatRuleController_list", queryFilterComponentNames)).toBe(
"VatRuleFilterDto",
);
});

it("returns undefined when the map or operation id is missing", () => {
expect(resolveQueryFilterComponentName("UnknownController_action", queryFilterComponentNames)).toBeUndefined();
expect(resolveQueryFilterComponentName("VatRuleController_list", undefined)).toBeUndefined();
expect(resolveQueryFilterComponentName(undefined, queryFilterComponentNames)).toBeUndefined();
});
});
10 changes: 10 additions & 0 deletions src/generators/utils/query-filter-component-names.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function resolveQueryFilterComponentName(
operationId: string | undefined,
queryFilterComponentNames: Record<string, string> | undefined,
): string | undefined {
if (!operationId || !queryFilterComponentNames) {
return undefined;
}

return queryFilterComponentNames[operationId];
}