ng-openapi 0.2.22-pr-91-bugfix-nested-objects-parameter-fb5e988.0 → 0.2.22-pr-92-feature-single-request-parameter-0b340a2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/cli.cjs +221 -55
- package/index.d.ts +3 -1
- package/index.js +215 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,6 +113,7 @@ ng-openapi -i ./swagger.json -o ./src/api --date-type string
|
|
|
113
113
|
- `customHeaders` - Headers to add to all HTTP requests
|
|
114
114
|
- `responseTypeMapping` - Map content types to Angular HttpClient response types
|
|
115
115
|
- `customizeMethodName` - Function to customize generated method names
|
|
116
|
+
- `useSingleRequestParameter` - Generate one request object parameter per method instead of positional parameters (default: `false`)
|
|
116
117
|
- `compilerOptions` - TypeScript compiler options for code generation
|
|
117
118
|
|
|
118
119
|
## Generated Files Structure
|
package/cli.cjs
CHANGED
|
@@ -41,7 +41,7 @@ __name(isUrl, "isUrl");
|
|
|
41
41
|
// src/lib/cli.ts
|
|
42
42
|
var import_commander = require("commander");
|
|
43
43
|
var fs4 = __toESM(require("fs"));
|
|
44
|
-
var
|
|
44
|
+
var path13 = __toESM(require("path"));
|
|
45
45
|
|
|
46
46
|
// package.json
|
|
47
47
|
var version = "0.2.21";
|
|
@@ -51,11 +51,11 @@ var import_ts_morph7 = require("ts-morph");
|
|
|
51
51
|
|
|
52
52
|
// ../shared/src/utils/string.utils.ts
|
|
53
53
|
function camelCase(str) {
|
|
54
|
-
return str.replace(/[-_
|
|
54
|
+
return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
|
|
55
55
|
}
|
|
56
56
|
__name(camelCase, "camelCase");
|
|
57
57
|
function pascalCase(str) {
|
|
58
|
-
return str.replace(/[-_
|
|
58
|
+
return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
|
|
59
59
|
}
|
|
60
60
|
__name(pascalCase, "pascalCase");
|
|
61
61
|
function pascalCaseForEnums(str) {
|
|
@@ -166,12 +166,12 @@ function extractPaths(swaggerPaths = {}, methods = [
|
|
|
166
166
|
"head"
|
|
167
167
|
]) {
|
|
168
168
|
const paths = [];
|
|
169
|
-
Object.entries(swaggerPaths).forEach(([
|
|
169
|
+
Object.entries(swaggerPaths).forEach(([path14, pathItem]) => {
|
|
170
170
|
methods.forEach((method) => {
|
|
171
171
|
if (pathItem[method]) {
|
|
172
172
|
const operation = pathItem[method];
|
|
173
173
|
paths.push({
|
|
174
|
-
path:
|
|
174
|
+
path: path14,
|
|
175
175
|
method: method.toUpperCase(),
|
|
176
176
|
operationId: operation.operationId,
|
|
177
177
|
summary: operation.summary,
|
|
@@ -395,6 +395,10 @@ var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) =
|
|
|
395
395
|
* Do not edit this file manually
|
|
396
396
|
*/
|
|
397
397
|
`, "SERVICE_GENERATOR_HEADER_COMMENT");
|
|
398
|
+
var REQUEST_PARAMS_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated request parameter interfaces
|
|
399
|
+
* Do not edit this file manually
|
|
400
|
+
*/
|
|
401
|
+
`;
|
|
398
402
|
var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
|
|
399
403
|
* Do not edit this file manually
|
|
400
404
|
*/
|
|
@@ -2045,7 +2049,7 @@ return httpParams.append(key, value);`
|
|
|
2045
2049
|
|
|
2046
2050
|
// src/lib/generators/service/service.generator.ts
|
|
2047
2051
|
var import_ts_morph6 = require("ts-morph");
|
|
2048
|
-
var
|
|
2052
|
+
var path10 = __toESM(require("path"));
|
|
2049
2053
|
|
|
2050
2054
|
// src/lib/generators/service/service-method/service-method-body.generator.ts
|
|
2051
2055
|
var ServiceMethodBodyGenerator = class {
|
|
@@ -2299,6 +2303,73 @@ return this.httpClient.${httpMethod}(url, requestOptions)${parseResponse};`;
|
|
|
2299
2303
|
}
|
|
2300
2304
|
};
|
|
2301
2305
|
|
|
2306
|
+
// src/lib/generators/service/service-method/service-method-request-object.generator.ts
|
|
2307
|
+
var ServiceMethodRequestObjectGenerator = class {
|
|
2308
|
+
static {
|
|
2309
|
+
__name(this, "ServiceMethodRequestObjectGenerator");
|
|
2310
|
+
}
|
|
2311
|
+
/** First-occurrence-wins dedupe, shared by the flat and single-request parameter builders. */
|
|
2312
|
+
static dedupe(params) {
|
|
2313
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2314
|
+
return params.filter((param) => {
|
|
2315
|
+
if (seen.has(param.name)) {
|
|
2316
|
+
return false;
|
|
2317
|
+
}
|
|
2318
|
+
seen.add(param.name);
|
|
2319
|
+
return true;
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
static createEntry(interfaceName, parameters) {
|
|
2323
|
+
return {
|
|
2324
|
+
interfaceName,
|
|
2325
|
+
parameters,
|
|
2326
|
+
varName: this.resolveVarName(parameters),
|
|
2327
|
+
isOptional: parameters.every((param) => param.hasQuestionToken)
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
static toRequestParameter(entry) {
|
|
2331
|
+
return {
|
|
2332
|
+
name: entry.varName,
|
|
2333
|
+
type: entry.interfaceName,
|
|
2334
|
+
hasQuestionToken: entry.isOptional
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
static toInterfaceProperties(entry) {
|
|
2338
|
+
return entry.parameters.map((param) => ({
|
|
2339
|
+
name: param.name,
|
|
2340
|
+
type: param.type,
|
|
2341
|
+
hasQuestionToken: param.hasQuestionToken
|
|
2342
|
+
}));
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* The destructuring statement placed at the top of the method body so the
|
|
2346
|
+
* existing body templates keep referencing plain local identifiers.
|
|
2347
|
+
*/
|
|
2348
|
+
static toDestructureStatement(entry) {
|
|
2349
|
+
const names = entry.parameters.map((param) => param.name).join(", ");
|
|
2350
|
+
const source = entry.isOptional ? `${entry.varName} ?? {}` : entry.varName;
|
|
2351
|
+
return `const { ${names} } = ${source};`;
|
|
2352
|
+
}
|
|
2353
|
+
/** Destructured properties share the method scope, so the request variable must not collide with them. */
|
|
2354
|
+
static resolveVarName(parameters) {
|
|
2355
|
+
const used = new Set(parameters.map((param) => param.name));
|
|
2356
|
+
for (const candidate of [
|
|
2357
|
+
"request",
|
|
2358
|
+
"requestParams",
|
|
2359
|
+
"requestParameters"
|
|
2360
|
+
]) {
|
|
2361
|
+
if (!used.has(candidate)) {
|
|
2362
|
+
return candidate;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
let suffix = 2;
|
|
2366
|
+
while (used.has(`requestParameters${suffix}`)) {
|
|
2367
|
+
suffix++;
|
|
2368
|
+
}
|
|
2369
|
+
return `requestParameters${suffix}`;
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
|
|
2302
2373
|
// src/lib/generators/service/service-method/service-method-params.generator.ts
|
|
2303
2374
|
var ServiceMethodParamsGenerator = class {
|
|
2304
2375
|
static {
|
|
@@ -2313,19 +2384,10 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2313
2384
|
generateMethodParameters(operation) {
|
|
2314
2385
|
const params = this.generateApiParameters(operation);
|
|
2315
2386
|
const optionsParam = this.addOptionsParameter(params);
|
|
2316
|
-
|
|
2387
|
+
return ServiceMethodRequestObjectGenerator.dedupe([
|
|
2317
2388
|
...params,
|
|
2318
2389
|
...optionsParam
|
|
2319
|
-
];
|
|
2320
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2321
|
-
const uniqueParams = [];
|
|
2322
|
-
for (const param of combined) {
|
|
2323
|
-
if (!seen.has(param.name)) {
|
|
2324
|
-
seen.add(param.name);
|
|
2325
|
-
uniqueParams.push(param);
|
|
2326
|
-
}
|
|
2327
|
-
}
|
|
2328
|
-
return uniqueParams;
|
|
2390
|
+
]);
|
|
2329
2391
|
}
|
|
2330
2392
|
generateApiParameters(operation) {
|
|
2331
2393
|
const params = [];
|
|
@@ -2432,7 +2494,7 @@ var ServiceMethodOverloadsGenerator = class {
|
|
|
2432
2494
|
this.config = config;
|
|
2433
2495
|
this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
|
|
2434
2496
|
}
|
|
2435
|
-
generateMethodOverloads(operation) {
|
|
2497
|
+
generateMethodOverloads(operation, requestObject) {
|
|
2436
2498
|
const observeTypes = [
|
|
2437
2499
|
"body",
|
|
2438
2500
|
"response",
|
|
@@ -2441,38 +2503,35 @@ var ServiceMethodOverloadsGenerator = class {
|
|
|
2441
2503
|
const overloads = [];
|
|
2442
2504
|
const responseType = this.determineResponseTypeForOperation(operation);
|
|
2443
2505
|
observeTypes.forEach((observe) => {
|
|
2444
|
-
const overload = this.generateMethodOverload(operation, observe, responseType);
|
|
2506
|
+
const overload = this.generateMethodOverload(operation, observe, responseType, requestObject);
|
|
2445
2507
|
if (overload) {
|
|
2446
2508
|
overloads.push(overload);
|
|
2447
2509
|
}
|
|
2448
2510
|
});
|
|
2449
2511
|
return overloads;
|
|
2450
2512
|
}
|
|
2451
|
-
generateMethodOverload(operation, observe, responseType) {
|
|
2513
|
+
generateMethodOverload(operation, observe, responseType, requestObject) {
|
|
2452
2514
|
this.responseDataType = this.generateOverloadResponseType(operation);
|
|
2453
|
-
const params = this.generateOverloadParameters(operation, observe, responseType);
|
|
2515
|
+
const params = requestObject ? this.generateSingleRequestOverloadParameters(requestObject, observe, responseType) : this.generateOverloadParameters(operation, observe, responseType);
|
|
2454
2516
|
const returnType = this.generateOverloadReturnType(observe);
|
|
2455
2517
|
return {
|
|
2456
2518
|
parameters: params,
|
|
2457
2519
|
returnType
|
|
2458
2520
|
};
|
|
2459
2521
|
}
|
|
2522
|
+
generateSingleRequestOverloadParameters(requestObject, observe, responseType) {
|
|
2523
|
+
return [
|
|
2524
|
+
ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
|
|
2525
|
+
...this.addOverloadOptionsParameter(requestObject.parameters, observe, responseType)
|
|
2526
|
+
];
|
|
2527
|
+
}
|
|
2460
2528
|
generateOverloadParameters(operation, observe, responseType) {
|
|
2461
2529
|
const params = this.paramsGenerator.generateApiParameters(operation);
|
|
2462
2530
|
const optionsParam = this.addOverloadOptionsParameter(params, observe, responseType);
|
|
2463
|
-
|
|
2531
|
+
return ServiceMethodRequestObjectGenerator.dedupe([
|
|
2464
2532
|
...params,
|
|
2465
2533
|
...optionsParam
|
|
2466
|
-
];
|
|
2467
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2468
|
-
const uniqueParams = [];
|
|
2469
|
-
for (const param of combined) {
|
|
2470
|
-
if (!seen.has(param.name)) {
|
|
2471
|
-
seen.add(param.name);
|
|
2472
|
-
uniqueParams.push(param);
|
|
2473
|
-
}
|
|
2474
|
-
}
|
|
2475
|
-
return uniqueParams;
|
|
2534
|
+
]);
|
|
2476
2535
|
}
|
|
2477
2536
|
addOverloadOptionsParameter(params, observe, responseType) {
|
|
2478
2537
|
return [
|
|
@@ -2550,12 +2609,16 @@ var ServiceMethodGenerator = class {
|
|
|
2550
2609
|
this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config, parser);
|
|
2551
2610
|
this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
|
|
2552
2611
|
}
|
|
2553
|
-
addServiceMethod(serviceClass, operation) {
|
|
2612
|
+
addServiceMethod(serviceClass, operation, requestObject) {
|
|
2554
2613
|
const methodName = this.generateMethodName(operation);
|
|
2555
|
-
const parameters = this.paramsGenerator.generateMethodParameters(operation);
|
|
2614
|
+
const parameters = requestObject ? this.generateSingleRequestParameters(requestObject) : this.paramsGenerator.generateMethodParameters(operation);
|
|
2556
2615
|
const returnType = this.generateReturnType();
|
|
2557
|
-
|
|
2558
|
-
|
|
2616
|
+
let methodBody = this.bodyGenerator.generateMethodBody(operation);
|
|
2617
|
+
if (requestObject) {
|
|
2618
|
+
methodBody = `${ServiceMethodRequestObjectGenerator.toDestructureStatement(requestObject)}
|
|
2619
|
+
${methodBody}`;
|
|
2620
|
+
}
|
|
2621
|
+
const methodOverLoads = this.overloadsGenerator.generateMethodOverloads(operation, requestObject);
|
|
2559
2622
|
serviceClass.addMethod({
|
|
2560
2623
|
name: methodName,
|
|
2561
2624
|
parameters,
|
|
@@ -2567,6 +2630,12 @@ var ServiceMethodGenerator = class {
|
|
|
2567
2630
|
] : void 0
|
|
2568
2631
|
});
|
|
2569
2632
|
}
|
|
2633
|
+
generateSingleRequestParameters(requestObject) {
|
|
2634
|
+
return [
|
|
2635
|
+
ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
|
|
2636
|
+
...this.paramsGenerator.addOptionsParameter(requestObject.parameters)
|
|
2637
|
+
];
|
|
2638
|
+
}
|
|
2570
2639
|
generateMethodName(operation) {
|
|
2571
2640
|
if (this.config.options.customizeMethodName) {
|
|
2572
2641
|
if (operation.operationId == null) {
|
|
@@ -2593,6 +2662,97 @@ var ServiceMethodGenerator = class {
|
|
|
2593
2662
|
}
|
|
2594
2663
|
};
|
|
2595
2664
|
|
|
2665
|
+
// src/lib/generators/service/request-params.generator.ts
|
|
2666
|
+
var path9 = __toESM(require("path"));
|
|
2667
|
+
var RequestParamsGenerator = class {
|
|
2668
|
+
static {
|
|
2669
|
+
__name(this, "RequestParamsGenerator");
|
|
2670
|
+
}
|
|
2671
|
+
project;
|
|
2672
|
+
paramsGenerator;
|
|
2673
|
+
registry = /* @__PURE__ */ new Map();
|
|
2674
|
+
usedInterfaceNames = /* @__PURE__ */ new Set();
|
|
2675
|
+
constructor(parser, project, config) {
|
|
2676
|
+
this.project = project;
|
|
2677
|
+
this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
|
|
2678
|
+
}
|
|
2679
|
+
buildRegistry(controllerGroups, getMethodName) {
|
|
2680
|
+
Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
|
|
2681
|
+
operations.forEach((operation) => {
|
|
2682
|
+
const parameters = ServiceMethodRequestObjectGenerator.dedupe(this.paramsGenerator.generateApiParameters(operation));
|
|
2683
|
+
if (parameters.length === 0) {
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
2686
|
+
const reserved = parameters.find((param) => param.name === "observe" || param.name === "options");
|
|
2687
|
+
if (reserved) {
|
|
2688
|
+
throw new Error(`Parameter name '${reserved.name}' conflicts with the reserved '${reserved.name}' method parameter when useSingleRequestParameter is enabled: (${operation.method}) ${operation.path}`);
|
|
2689
|
+
}
|
|
2690
|
+
const interfaceName = this.reserveInterfaceName(controllerName, getMethodName(operation));
|
|
2691
|
+
this.registry.set(operation, ServiceMethodRequestObjectGenerator.createEntry(interfaceName, parameters));
|
|
2692
|
+
});
|
|
2693
|
+
});
|
|
2694
|
+
return this.registry;
|
|
2695
|
+
}
|
|
2696
|
+
generate(outputRoot) {
|
|
2697
|
+
if (this.registry.size === 0) {
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
2700
|
+
const filePath = path9.join(outputRoot, "models", "request-params.ts");
|
|
2701
|
+
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
2702
|
+
overwrite: true
|
|
2703
|
+
});
|
|
2704
|
+
this.registry.forEach((entry, operation) => {
|
|
2705
|
+
sourceFile.addInterface({
|
|
2706
|
+
name: entry.interfaceName,
|
|
2707
|
+
isExported: true,
|
|
2708
|
+
properties: ServiceMethodRequestObjectGenerator.toInterfaceProperties(entry),
|
|
2709
|
+
docs: operation.description ? [
|
|
2710
|
+
operation.description
|
|
2711
|
+
] : void 0
|
|
2712
|
+
});
|
|
2713
|
+
});
|
|
2714
|
+
sourceFile.fixMissingImports().formatText();
|
|
2715
|
+
sourceFile.insertText(0, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT);
|
|
2716
|
+
sourceFile.saveSync();
|
|
2717
|
+
this.addModelsBarrelExport(outputRoot);
|
|
2718
|
+
}
|
|
2719
|
+
/**
|
|
2720
|
+
* Method names are only unique per service class, so interfaces sharing the
|
|
2721
|
+
* global request-params file fall back to a controller-prefixed name on collision.
|
|
2722
|
+
*/
|
|
2723
|
+
reserveInterfaceName(controllerName, methodName) {
|
|
2724
|
+
const base = `${pascalCase(methodName)}Params`;
|
|
2725
|
+
const candidates = [
|
|
2726
|
+
base,
|
|
2727
|
+
`${pascalCase(controllerName)}${base}`
|
|
2728
|
+
];
|
|
2729
|
+
for (const candidate of candidates) {
|
|
2730
|
+
if (!this.usedInterfaceNames.has(candidate)) {
|
|
2731
|
+
this.usedInterfaceNames.add(candidate);
|
|
2732
|
+
return candidate;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
let suffix = 2;
|
|
2736
|
+
while (this.usedInterfaceNames.has(`${candidates[1]}${suffix}`)) {
|
|
2737
|
+
suffix++;
|
|
2738
|
+
}
|
|
2739
|
+
const name = `${candidates[1]}${suffix}`;
|
|
2740
|
+
this.usedInterfaceNames.add(name);
|
|
2741
|
+
return name;
|
|
2742
|
+
}
|
|
2743
|
+
addModelsBarrelExport(outputRoot) {
|
|
2744
|
+
const modelsIndex = this.project.getSourceFile(path9.join(outputRoot, "models", "index.ts"));
|
|
2745
|
+
if (!modelsIndex) {
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
modelsIndex.addExportDeclaration({
|
|
2749
|
+
moduleSpecifier: "./request-params"
|
|
2750
|
+
});
|
|
2751
|
+
modelsIndex.formatText();
|
|
2752
|
+
modelsIndex.saveSync();
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
|
|
2596
2756
|
// src/lib/generators/service/service.generator.ts
|
|
2597
2757
|
var ServiceGenerator = class {
|
|
2598
2758
|
static {
|
|
@@ -2603,6 +2763,7 @@ var ServiceGenerator = class {
|
|
|
2603
2763
|
spec;
|
|
2604
2764
|
config;
|
|
2605
2765
|
methodGenerator;
|
|
2766
|
+
requestObjects;
|
|
2606
2767
|
constructor(parser, project, config) {
|
|
2607
2768
|
this.config = config;
|
|
2608
2769
|
this.project = project;
|
|
@@ -2615,23 +2776,28 @@ var ServiceGenerator = class {
|
|
|
2615
2776
|
this.methodGenerator = new ServiceMethodGenerator(config, parser);
|
|
2616
2777
|
}
|
|
2617
2778
|
async generate(outputRoot) {
|
|
2618
|
-
const outputDir =
|
|
2779
|
+
const outputDir = path10.join(outputRoot, "services");
|
|
2619
2780
|
const paths = extractPaths(this.spec.paths);
|
|
2620
2781
|
if (paths.length === 0) {
|
|
2621
2782
|
console.warn("No API paths found in the specification");
|
|
2622
2783
|
return;
|
|
2623
2784
|
}
|
|
2624
2785
|
const controllerGroups = this.groupPathsByController(paths);
|
|
2786
|
+
if (this.config.options.useSingleRequestParameter) {
|
|
2787
|
+
const requestParamsGenerator = new RequestParamsGenerator(this.parser, this.project, this.config);
|
|
2788
|
+
this.requestObjects = requestParamsGenerator.buildRegistry(controllerGroups, (operation) => this.methodGenerator.generateMethodName(operation));
|
|
2789
|
+
requestParamsGenerator.generate(outputRoot);
|
|
2790
|
+
}
|
|
2625
2791
|
await Promise.all(Object.entries(controllerGroups).map(([controllerName, operations]) => this.generateServiceFile(controllerName, operations, outputDir)));
|
|
2626
2792
|
}
|
|
2627
2793
|
groupPathsByController(paths) {
|
|
2628
2794
|
const groups = {};
|
|
2629
|
-
paths.forEach((
|
|
2795
|
+
paths.forEach((path14) => {
|
|
2630
2796
|
let controllerName = "Default";
|
|
2631
|
-
if (
|
|
2632
|
-
controllerName =
|
|
2797
|
+
if (path14.tags && path14.tags.length > 0) {
|
|
2798
|
+
controllerName = path14.tags[0];
|
|
2633
2799
|
} else {
|
|
2634
|
-
const pathParts =
|
|
2800
|
+
const pathParts = path14.path.split("/").filter((p) => p && !p.startsWith("{"));
|
|
2635
2801
|
if (pathParts.length > 1) {
|
|
2636
2802
|
controllerName = pascalCase(pathParts[1]);
|
|
2637
2803
|
}
|
|
@@ -2640,13 +2806,13 @@ var ServiceGenerator = class {
|
|
|
2640
2806
|
if (!groups[controllerName]) {
|
|
2641
2807
|
groups[controllerName] = [];
|
|
2642
2808
|
}
|
|
2643
|
-
groups[controllerName].push(
|
|
2809
|
+
groups[controllerName].push(path14);
|
|
2644
2810
|
});
|
|
2645
2811
|
return groups;
|
|
2646
2812
|
}
|
|
2647
2813
|
async generateServiceFile(controllerName, operations, outputDir) {
|
|
2648
2814
|
const fileName = `${camelCase(controllerName)}.service.ts`;
|
|
2649
|
-
const filePath =
|
|
2815
|
+
const filePath = path10.join(outputDir, fileName);
|
|
2650
2816
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
2651
2817
|
overwrite: true
|
|
2652
2818
|
});
|
|
@@ -2747,7 +2913,7 @@ var ServiceGenerator = class {
|
|
|
2747
2913
|
return context.set(this.clientContextToken, '${this.config.clientName || "default"}');`
|
|
2748
2914
|
});
|
|
2749
2915
|
operations.forEach((operation) => {
|
|
2750
|
-
this.methodGenerator.addServiceMethod(serviceClass, operation);
|
|
2916
|
+
this.methodGenerator.addServiceMethod(serviceClass, operation, this.requestObjects?.get(operation));
|
|
2751
2917
|
});
|
|
2752
2918
|
if (hasDuplicateFunctionNames(serviceClass.getMethods())) {
|
|
2753
2919
|
throw new Error(`Duplicate method names found in service class ${className}. Please ensure unique method names for each operation.`);
|
|
@@ -2757,7 +2923,7 @@ return context.set(this.clientContextToken, '${this.config.clientName || "defaul
|
|
|
2757
2923
|
|
|
2758
2924
|
// src/lib/generators/service/service-index.generator.ts
|
|
2759
2925
|
var fs2 = __toESM(require("fs"));
|
|
2760
|
-
var
|
|
2926
|
+
var path11 = __toESM(require("path"));
|
|
2761
2927
|
var ServiceIndexGenerator = class {
|
|
2762
2928
|
static {
|
|
2763
2929
|
__name(this, "ServiceIndexGenerator");
|
|
@@ -2767,8 +2933,8 @@ var ServiceIndexGenerator = class {
|
|
|
2767
2933
|
this.project = project;
|
|
2768
2934
|
}
|
|
2769
2935
|
generateIndex(outputRoot) {
|
|
2770
|
-
const servicesDir =
|
|
2771
|
-
const indexPath =
|
|
2936
|
+
const servicesDir = path11.join(outputRoot, "services");
|
|
2937
|
+
const indexPath = path11.join(servicesDir, "index.ts");
|
|
2772
2938
|
const sourceFile = this.project.createSourceFile(indexPath, "", {
|
|
2773
2939
|
overwrite: true
|
|
2774
2940
|
});
|
|
@@ -2789,7 +2955,7 @@ var ServiceIndexGenerator = class {
|
|
|
2789
2955
|
|
|
2790
2956
|
// src/lib/core/generator.ts
|
|
2791
2957
|
var fs3 = __toESM(require("fs"));
|
|
2792
|
-
var
|
|
2958
|
+
var path12 = __toESM(require("path"));
|
|
2793
2959
|
function validateInput(inputPath) {
|
|
2794
2960
|
if (isUrl(inputPath)) {
|
|
2795
2961
|
return;
|
|
@@ -2797,7 +2963,7 @@ function validateInput(inputPath) {
|
|
|
2797
2963
|
if (!fs3.existsSync(inputPath)) {
|
|
2798
2964
|
throw new Error(`Input file not found: ${inputPath}`);
|
|
2799
2965
|
}
|
|
2800
|
-
const extension =
|
|
2966
|
+
const extension = path12.extname(inputPath).toLowerCase();
|
|
2801
2967
|
const supportedExtensions = [
|
|
2802
2968
|
".json",
|
|
2803
2969
|
".yaml",
|
|
@@ -2888,7 +3054,7 @@ __name(generateFromConfig, "generateFromConfig");
|
|
|
2888
3054
|
// src/lib/cli.ts
|
|
2889
3055
|
var program = new import_commander.Command();
|
|
2890
3056
|
async function loadConfigFile(configPath) {
|
|
2891
|
-
const resolvedPath =
|
|
3057
|
+
const resolvedPath = path13.resolve(configPath);
|
|
2892
3058
|
if (!fs4.existsSync(resolvedPath)) {
|
|
2893
3059
|
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
2894
3060
|
}
|
|
@@ -2902,12 +3068,12 @@ async function loadConfigFile(configPath) {
|
|
|
2902
3068
|
if (!config.input || !config.output) {
|
|
2903
3069
|
throw new Error('Configuration must include "input" and "output" properties');
|
|
2904
3070
|
}
|
|
2905
|
-
const configDir =
|
|
2906
|
-
if (!isUrl(config.input) && !
|
|
2907
|
-
config.input =
|
|
3071
|
+
const configDir = path13.dirname(resolvedPath);
|
|
3072
|
+
if (!isUrl(config.input) && !path13.isAbsolute(config.input)) {
|
|
3073
|
+
config.input = path13.resolve(configDir, config.input);
|
|
2908
3074
|
}
|
|
2909
|
-
if (!
|
|
2910
|
-
config.output =
|
|
3075
|
+
if (!path13.isAbsolute(config.output)) {
|
|
3076
|
+
config.output = path13.resolve(configDir, config.output);
|
|
2911
3077
|
}
|
|
2912
3078
|
return config;
|
|
2913
3079
|
} catch (error) {
|
package/index.d.ts
CHANGED
|
@@ -204,6 +204,7 @@ interface GeneratorConfig {
|
|
|
204
204
|
[contentType: string]: "json" | "blob" | "arraybuffer" | "text";
|
|
205
205
|
};
|
|
206
206
|
customizeMethodName?: (operationId: string) => string;
|
|
207
|
+
useSingleRequestParameter?: boolean;
|
|
207
208
|
};
|
|
208
209
|
compilerOptions?: {
|
|
209
210
|
declaration?: boolean;
|
|
@@ -272,6 +273,7 @@ declare const CONTENT_TYPES: {
|
|
|
272
273
|
declare const TYPE_GENERATOR_HEADER_COMMENT: string;
|
|
273
274
|
declare const SERVICE_INDEX_GENERATOR_HEADER_COMMENT: string;
|
|
274
275
|
declare const SERVICE_GENERATOR_HEADER_COMMENT: (controllerName: string) => string;
|
|
276
|
+
declare const REQUEST_PARAMS_GENERATOR_HEADER_COMMENT: string;
|
|
275
277
|
declare const MAIN_INDEX_GENERATOR_HEADER_COMMENT: string;
|
|
276
278
|
declare const PROVIDER_GENERATOR_HEADER_COMMENT: string;
|
|
277
279
|
declare const BASE_INTERCEPTOR_HEADER_COMMENT: (clientName: string) => string;
|
|
@@ -288,4 +290,4 @@ declare function validateInput(inputPath: string): void;
|
|
|
288
290
|
*/
|
|
289
291
|
declare function generateFromConfig(config: GeneratorConfig): Promise<void>;
|
|
290
292
|
|
|
291
|
-
export { BASE_INTERCEPTOR_HEADER_COMMENT, CONTENT_TYPES, type EnumValueObject, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type IPluginGenerator, type IPluginGeneratorClass, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenerationContext, type NgOpenapiClientConfig, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, type RequestBody, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeSchema, ZOD_PLUGIN_GENERATOR_HEADER_COMMENT, ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT, camelCase, escapeString, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getInterceptorsTokenName, getRequestBodyType, getResponseType, getResponseTypeFromResponse, getTypeScriptType, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, kebabCase, nullableType, pascalCase, pascalCaseForEnums, type placeHolder, screamingSnakeCase, validateInput };
|
|
293
|
+
export { BASE_INTERCEPTOR_HEADER_COMMENT, CONTENT_TYPES, type EnumValueObject, type GeneratorConfig, type GetMethodGenerationContext, HTTP_RESOURCE_GENERATOR_HEADER_COMMENT, type IPluginGenerator, type IPluginGeneratorClass, MAIN_INDEX_GENERATOR_HEADER_COMMENT, type MethodGenerationContext, type NgOpenapiClientConfig, PROVIDER_GENERATOR_HEADER_COMMENT, type Parameter, type PathInfo, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT, type RequestBody, SERVICE_GENERATOR_HEADER_COMMENT, SERVICE_INDEX_GENERATOR_HEADER_COMMENT, type SwaggerDefinition, SwaggerParser, type SwaggerResponse, type SwaggerSpec, TYPE_GENERATOR_HEADER_COMMENT, type TypeSchema, ZOD_PLUGIN_GENERATOR_HEADER_COMMENT, ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT, camelCase, escapeString, extractPaths, generateFromConfig, generateParseRequestTypeParams, getBasePathTokenName, getClientContextTokenName, getInterceptorsTokenName, getRequestBodyType, getResponseType, getResponseTypeFromResponse, getTypeScriptType, hasDuplicateFunctionNames, inferResponseTypeFromContentType, isDataTypeInterface, isPrimitiveType, kebabCase, nullableType, pascalCase, pascalCaseForEnums, type placeHolder, screamingSnakeCase, validateInput };
|
package/index.js
CHANGED
|
@@ -78,6 +78,7 @@ __export(index_exports, {
|
|
|
78
78
|
HTTP_RESOURCE_GENERATOR_HEADER_COMMENT: () => HTTP_RESOURCE_GENERATOR_HEADER_COMMENT,
|
|
79
79
|
MAIN_INDEX_GENERATOR_HEADER_COMMENT: () => MAIN_INDEX_GENERATOR_HEADER_COMMENT,
|
|
80
80
|
PROVIDER_GENERATOR_HEADER_COMMENT: () => PROVIDER_GENERATOR_HEADER_COMMENT,
|
|
81
|
+
REQUEST_PARAMS_GENERATOR_HEADER_COMMENT: () => REQUEST_PARAMS_GENERATOR_HEADER_COMMENT,
|
|
81
82
|
SERVICE_GENERATOR_HEADER_COMMENT: () => SERVICE_GENERATOR_HEADER_COMMENT,
|
|
82
83
|
SERVICE_INDEX_GENERATOR_HEADER_COMMENT: () => SERVICE_INDEX_GENERATOR_HEADER_COMMENT,
|
|
83
84
|
SwaggerParser: () => SwaggerParser,
|
|
@@ -114,7 +115,7 @@ var import_ts_morph7 = require("ts-morph");
|
|
|
114
115
|
|
|
115
116
|
// ../shared/src/utils/string.utils.ts
|
|
116
117
|
function camelCase(str) {
|
|
117
|
-
return str.replace(/[-_
|
|
118
|
+
return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toLowerCase());
|
|
118
119
|
}
|
|
119
120
|
__name(camelCase, "camelCase");
|
|
120
121
|
function kebabCase(str) {
|
|
@@ -122,7 +123,7 @@ function kebabCase(str) {
|
|
|
122
123
|
}
|
|
123
124
|
__name(kebabCase, "kebabCase");
|
|
124
125
|
function pascalCase(str) {
|
|
125
|
-
return str.replace(/[-_
|
|
126
|
+
return str.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : "").replace(/^./, (char) => char.toUpperCase());
|
|
126
127
|
}
|
|
127
128
|
__name(pascalCase, "pascalCase");
|
|
128
129
|
function screamingSnakeCase(str) {
|
|
@@ -237,12 +238,12 @@ function extractPaths(swaggerPaths = {}, methods = [
|
|
|
237
238
|
"head"
|
|
238
239
|
]) {
|
|
239
240
|
const paths = [];
|
|
240
|
-
Object.entries(swaggerPaths).forEach(([
|
|
241
|
+
Object.entries(swaggerPaths).forEach(([path13, pathItem]) => {
|
|
241
242
|
methods.forEach((method) => {
|
|
242
243
|
if (pathItem[method]) {
|
|
243
244
|
const operation = pathItem[method];
|
|
244
245
|
paths.push({
|
|
245
|
-
path:
|
|
246
|
+
path: path13,
|
|
246
247
|
method: method.toUpperCase(),
|
|
247
248
|
operationId: operation.operationId,
|
|
248
249
|
summary: operation.summary,
|
|
@@ -480,6 +481,10 @@ var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) =
|
|
|
480
481
|
* Do not edit this file manually
|
|
481
482
|
*/
|
|
482
483
|
`, "SERVICE_GENERATOR_HEADER_COMMENT");
|
|
484
|
+
var REQUEST_PARAMS_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated request parameter interfaces
|
|
485
|
+
* Do not edit this file manually
|
|
486
|
+
*/
|
|
487
|
+
`;
|
|
483
488
|
var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
|
|
484
489
|
* Do not edit this file manually
|
|
485
490
|
*/
|
|
@@ -2169,7 +2174,7 @@ var HttpParamsBuilderGenerator = _HttpParamsBuilderGenerator;
|
|
|
2169
2174
|
|
|
2170
2175
|
// src/lib/generators/service/service.generator.ts
|
|
2171
2176
|
var import_ts_morph6 = require("ts-morph");
|
|
2172
|
-
var
|
|
2177
|
+
var path10 = __toESM(require("path"));
|
|
2173
2178
|
|
|
2174
2179
|
// src/lib/generators/service/service-method/service-method-body.generator.ts
|
|
2175
2180
|
var _ServiceMethodBodyGenerator = class _ServiceMethodBodyGenerator {
|
|
@@ -2431,6 +2436,72 @@ return this.httpClient.${httpMethod}(url, requestOptions)${parseResponse};`;
|
|
|
2431
2436
|
__name(_ServiceMethodBodyGenerator, "ServiceMethodBodyGenerator");
|
|
2432
2437
|
var ServiceMethodBodyGenerator = _ServiceMethodBodyGenerator;
|
|
2433
2438
|
|
|
2439
|
+
// src/lib/generators/service/service-method/service-method-request-object.generator.ts
|
|
2440
|
+
var _ServiceMethodRequestObjectGenerator = class _ServiceMethodRequestObjectGenerator {
|
|
2441
|
+
/** First-occurrence-wins dedupe, shared by the flat and single-request parameter builders. */
|
|
2442
|
+
static dedupe(params) {
|
|
2443
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2444
|
+
return params.filter((param) => {
|
|
2445
|
+
if (seen.has(param.name)) {
|
|
2446
|
+
return false;
|
|
2447
|
+
}
|
|
2448
|
+
seen.add(param.name);
|
|
2449
|
+
return true;
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
static createEntry(interfaceName, parameters) {
|
|
2453
|
+
return {
|
|
2454
|
+
interfaceName,
|
|
2455
|
+
parameters,
|
|
2456
|
+
varName: this.resolveVarName(parameters),
|
|
2457
|
+
isOptional: parameters.every((param) => param.hasQuestionToken)
|
|
2458
|
+
};
|
|
2459
|
+
}
|
|
2460
|
+
static toRequestParameter(entry) {
|
|
2461
|
+
return {
|
|
2462
|
+
name: entry.varName,
|
|
2463
|
+
type: entry.interfaceName,
|
|
2464
|
+
hasQuestionToken: entry.isOptional
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
static toInterfaceProperties(entry) {
|
|
2468
|
+
return entry.parameters.map((param) => ({
|
|
2469
|
+
name: param.name,
|
|
2470
|
+
type: param.type,
|
|
2471
|
+
hasQuestionToken: param.hasQuestionToken
|
|
2472
|
+
}));
|
|
2473
|
+
}
|
|
2474
|
+
/**
|
|
2475
|
+
* The destructuring statement placed at the top of the method body so the
|
|
2476
|
+
* existing body templates keep referencing plain local identifiers.
|
|
2477
|
+
*/
|
|
2478
|
+
static toDestructureStatement(entry) {
|
|
2479
|
+
const names = entry.parameters.map((param) => param.name).join(", ");
|
|
2480
|
+
const source = entry.isOptional ? `${entry.varName} ?? {}` : entry.varName;
|
|
2481
|
+
return `const { ${names} } = ${source};`;
|
|
2482
|
+
}
|
|
2483
|
+
/** Destructured properties share the method scope, so the request variable must not collide with them. */
|
|
2484
|
+
static resolveVarName(parameters) {
|
|
2485
|
+
const used = new Set(parameters.map((param) => param.name));
|
|
2486
|
+
for (const candidate of [
|
|
2487
|
+
"request",
|
|
2488
|
+
"requestParams",
|
|
2489
|
+
"requestParameters"
|
|
2490
|
+
]) {
|
|
2491
|
+
if (!used.has(candidate)) {
|
|
2492
|
+
return candidate;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
let suffix = 2;
|
|
2496
|
+
while (used.has(`requestParameters${suffix}`)) {
|
|
2497
|
+
suffix++;
|
|
2498
|
+
}
|
|
2499
|
+
return `requestParameters${suffix}`;
|
|
2500
|
+
}
|
|
2501
|
+
};
|
|
2502
|
+
__name(_ServiceMethodRequestObjectGenerator, "ServiceMethodRequestObjectGenerator");
|
|
2503
|
+
var ServiceMethodRequestObjectGenerator = _ServiceMethodRequestObjectGenerator;
|
|
2504
|
+
|
|
2434
2505
|
// src/lib/generators/service/service-method/service-method-params.generator.ts
|
|
2435
2506
|
var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
|
|
2436
2507
|
constructor(config, parser) {
|
|
@@ -2442,19 +2513,10 @@ var _ServiceMethodParamsGenerator = class _ServiceMethodParamsGenerator {
|
|
|
2442
2513
|
generateMethodParameters(operation) {
|
|
2443
2514
|
const params = this.generateApiParameters(operation);
|
|
2444
2515
|
const optionsParam = this.addOptionsParameter(params);
|
|
2445
|
-
|
|
2516
|
+
return ServiceMethodRequestObjectGenerator.dedupe([
|
|
2446
2517
|
...params,
|
|
2447
2518
|
...optionsParam
|
|
2448
|
-
];
|
|
2449
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2450
|
-
const uniqueParams = [];
|
|
2451
|
-
for (const param of combined) {
|
|
2452
|
-
if (!seen.has(param.name)) {
|
|
2453
|
-
seen.add(param.name);
|
|
2454
|
-
uniqueParams.push(param);
|
|
2455
|
-
}
|
|
2456
|
-
}
|
|
2457
|
-
return uniqueParams;
|
|
2519
|
+
]);
|
|
2458
2520
|
}
|
|
2459
2521
|
generateApiParameters(operation) {
|
|
2460
2522
|
var _a, _b, _c, _d, _e;
|
|
@@ -2564,7 +2626,7 @@ var _ServiceMethodOverloadsGenerator = class _ServiceMethodOverloadsGenerator {
|
|
|
2564
2626
|
this.config = config;
|
|
2565
2627
|
this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
|
|
2566
2628
|
}
|
|
2567
|
-
generateMethodOverloads(operation) {
|
|
2629
|
+
generateMethodOverloads(operation, requestObject) {
|
|
2568
2630
|
const observeTypes = [
|
|
2569
2631
|
"body",
|
|
2570
2632
|
"response",
|
|
@@ -2573,38 +2635,35 @@ var _ServiceMethodOverloadsGenerator = class _ServiceMethodOverloadsGenerator {
|
|
|
2573
2635
|
const overloads = [];
|
|
2574
2636
|
const responseType = this.determineResponseTypeForOperation(operation);
|
|
2575
2637
|
observeTypes.forEach((observe) => {
|
|
2576
|
-
const overload = this.generateMethodOverload(operation, observe, responseType);
|
|
2638
|
+
const overload = this.generateMethodOverload(operation, observe, responseType, requestObject);
|
|
2577
2639
|
if (overload) {
|
|
2578
2640
|
overloads.push(overload);
|
|
2579
2641
|
}
|
|
2580
2642
|
});
|
|
2581
2643
|
return overloads;
|
|
2582
2644
|
}
|
|
2583
|
-
generateMethodOverload(operation, observe, responseType) {
|
|
2645
|
+
generateMethodOverload(operation, observe, responseType, requestObject) {
|
|
2584
2646
|
this.responseDataType = this.generateOverloadResponseType(operation);
|
|
2585
|
-
const params = this.generateOverloadParameters(operation, observe, responseType);
|
|
2647
|
+
const params = requestObject ? this.generateSingleRequestOverloadParameters(requestObject, observe, responseType) : this.generateOverloadParameters(operation, observe, responseType);
|
|
2586
2648
|
const returnType = this.generateOverloadReturnType(observe);
|
|
2587
2649
|
return {
|
|
2588
2650
|
parameters: params,
|
|
2589
2651
|
returnType
|
|
2590
2652
|
};
|
|
2591
2653
|
}
|
|
2654
|
+
generateSingleRequestOverloadParameters(requestObject, observe, responseType) {
|
|
2655
|
+
return [
|
|
2656
|
+
ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
|
|
2657
|
+
...this.addOverloadOptionsParameter(requestObject.parameters, observe, responseType)
|
|
2658
|
+
];
|
|
2659
|
+
}
|
|
2592
2660
|
generateOverloadParameters(operation, observe, responseType) {
|
|
2593
2661
|
const params = this.paramsGenerator.generateApiParameters(operation);
|
|
2594
2662
|
const optionsParam = this.addOverloadOptionsParameter(params, observe, responseType);
|
|
2595
|
-
|
|
2663
|
+
return ServiceMethodRequestObjectGenerator.dedupe([
|
|
2596
2664
|
...params,
|
|
2597
2665
|
...optionsParam
|
|
2598
|
-
];
|
|
2599
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2600
|
-
const uniqueParams = [];
|
|
2601
|
-
for (const param of combined) {
|
|
2602
|
-
if (!seen.has(param.name)) {
|
|
2603
|
-
seen.add(param.name);
|
|
2604
|
-
uniqueParams.push(param);
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
return uniqueParams;
|
|
2666
|
+
]);
|
|
2608
2667
|
}
|
|
2609
2668
|
addOverloadOptionsParameter(params, observe, responseType) {
|
|
2610
2669
|
return [
|
|
@@ -2684,12 +2743,16 @@ var _ServiceMethodGenerator = class _ServiceMethodGenerator {
|
|
|
2684
2743
|
this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config, parser);
|
|
2685
2744
|
this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
|
|
2686
2745
|
}
|
|
2687
|
-
addServiceMethod(serviceClass, operation) {
|
|
2746
|
+
addServiceMethod(serviceClass, operation, requestObject) {
|
|
2688
2747
|
const methodName = this.generateMethodName(operation);
|
|
2689
|
-
const parameters = this.paramsGenerator.generateMethodParameters(operation);
|
|
2748
|
+
const parameters = requestObject ? this.generateSingleRequestParameters(requestObject) : this.paramsGenerator.generateMethodParameters(operation);
|
|
2690
2749
|
const returnType = this.generateReturnType();
|
|
2691
|
-
|
|
2692
|
-
|
|
2750
|
+
let methodBody = this.bodyGenerator.generateMethodBody(operation);
|
|
2751
|
+
if (requestObject) {
|
|
2752
|
+
methodBody = `${ServiceMethodRequestObjectGenerator.toDestructureStatement(requestObject)}
|
|
2753
|
+
${methodBody}`;
|
|
2754
|
+
}
|
|
2755
|
+
const methodOverLoads = this.overloadsGenerator.generateMethodOverloads(operation, requestObject);
|
|
2693
2756
|
serviceClass.addMethod({
|
|
2694
2757
|
name: methodName,
|
|
2695
2758
|
parameters,
|
|
@@ -2701,6 +2764,12 @@ var _ServiceMethodGenerator = class _ServiceMethodGenerator {
|
|
|
2701
2764
|
] : void 0
|
|
2702
2765
|
});
|
|
2703
2766
|
}
|
|
2767
|
+
generateSingleRequestParameters(requestObject) {
|
|
2768
|
+
return [
|
|
2769
|
+
ServiceMethodRequestObjectGenerator.toRequestParameter(requestObject),
|
|
2770
|
+
...this.paramsGenerator.addOptionsParameter(requestObject.parameters)
|
|
2771
|
+
];
|
|
2772
|
+
}
|
|
2704
2773
|
generateMethodName(operation) {
|
|
2705
2774
|
if (this.config.options.customizeMethodName) {
|
|
2706
2775
|
if (operation.operationId == null) {
|
|
@@ -2729,6 +2798,96 @@ var _ServiceMethodGenerator = class _ServiceMethodGenerator {
|
|
|
2729
2798
|
__name(_ServiceMethodGenerator, "ServiceMethodGenerator");
|
|
2730
2799
|
var ServiceMethodGenerator = _ServiceMethodGenerator;
|
|
2731
2800
|
|
|
2801
|
+
// src/lib/generators/service/request-params.generator.ts
|
|
2802
|
+
var path9 = __toESM(require("path"));
|
|
2803
|
+
var _RequestParamsGenerator = class _RequestParamsGenerator {
|
|
2804
|
+
constructor(parser, project, config) {
|
|
2805
|
+
__publicField(this, "project");
|
|
2806
|
+
__publicField(this, "paramsGenerator");
|
|
2807
|
+
__publicField(this, "registry", /* @__PURE__ */ new Map());
|
|
2808
|
+
__publicField(this, "usedInterfaceNames", /* @__PURE__ */ new Set());
|
|
2809
|
+
this.project = project;
|
|
2810
|
+
this.paramsGenerator = new ServiceMethodParamsGenerator(config, parser);
|
|
2811
|
+
}
|
|
2812
|
+
buildRegistry(controllerGroups, getMethodName) {
|
|
2813
|
+
Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
|
|
2814
|
+
operations.forEach((operation) => {
|
|
2815
|
+
const parameters = ServiceMethodRequestObjectGenerator.dedupe(this.paramsGenerator.generateApiParameters(operation));
|
|
2816
|
+
if (parameters.length === 0) {
|
|
2817
|
+
return;
|
|
2818
|
+
}
|
|
2819
|
+
const reserved = parameters.find((param) => param.name === "observe" || param.name === "options");
|
|
2820
|
+
if (reserved) {
|
|
2821
|
+
throw new Error(`Parameter name '${reserved.name}' conflicts with the reserved '${reserved.name}' method parameter when useSingleRequestParameter is enabled: (${operation.method}) ${operation.path}`);
|
|
2822
|
+
}
|
|
2823
|
+
const interfaceName = this.reserveInterfaceName(controllerName, getMethodName(operation));
|
|
2824
|
+
this.registry.set(operation, ServiceMethodRequestObjectGenerator.createEntry(interfaceName, parameters));
|
|
2825
|
+
});
|
|
2826
|
+
});
|
|
2827
|
+
return this.registry;
|
|
2828
|
+
}
|
|
2829
|
+
generate(outputRoot) {
|
|
2830
|
+
if (this.registry.size === 0) {
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
const filePath = path9.join(outputRoot, "models", "request-params.ts");
|
|
2834
|
+
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
2835
|
+
overwrite: true
|
|
2836
|
+
});
|
|
2837
|
+
this.registry.forEach((entry, operation) => {
|
|
2838
|
+
sourceFile.addInterface({
|
|
2839
|
+
name: entry.interfaceName,
|
|
2840
|
+
isExported: true,
|
|
2841
|
+
properties: ServiceMethodRequestObjectGenerator.toInterfaceProperties(entry),
|
|
2842
|
+
docs: operation.description ? [
|
|
2843
|
+
operation.description
|
|
2844
|
+
] : void 0
|
|
2845
|
+
});
|
|
2846
|
+
});
|
|
2847
|
+
sourceFile.fixMissingImports().formatText();
|
|
2848
|
+
sourceFile.insertText(0, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT);
|
|
2849
|
+
sourceFile.saveSync();
|
|
2850
|
+
this.addModelsBarrelExport(outputRoot);
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Method names are only unique per service class, so interfaces sharing the
|
|
2854
|
+
* global request-params file fall back to a controller-prefixed name on collision.
|
|
2855
|
+
*/
|
|
2856
|
+
reserveInterfaceName(controllerName, methodName) {
|
|
2857
|
+
const base = `${pascalCase(methodName)}Params`;
|
|
2858
|
+
const candidates = [
|
|
2859
|
+
base,
|
|
2860
|
+
`${pascalCase(controllerName)}${base}`
|
|
2861
|
+
];
|
|
2862
|
+
for (const candidate of candidates) {
|
|
2863
|
+
if (!this.usedInterfaceNames.has(candidate)) {
|
|
2864
|
+
this.usedInterfaceNames.add(candidate);
|
|
2865
|
+
return candidate;
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
let suffix = 2;
|
|
2869
|
+
while (this.usedInterfaceNames.has(`${candidates[1]}${suffix}`)) {
|
|
2870
|
+
suffix++;
|
|
2871
|
+
}
|
|
2872
|
+
const name = `${candidates[1]}${suffix}`;
|
|
2873
|
+
this.usedInterfaceNames.add(name);
|
|
2874
|
+
return name;
|
|
2875
|
+
}
|
|
2876
|
+
addModelsBarrelExport(outputRoot) {
|
|
2877
|
+
const modelsIndex = this.project.getSourceFile(path9.join(outputRoot, "models", "index.ts"));
|
|
2878
|
+
if (!modelsIndex) {
|
|
2879
|
+
return;
|
|
2880
|
+
}
|
|
2881
|
+
modelsIndex.addExportDeclaration({
|
|
2882
|
+
moduleSpecifier: "./request-params"
|
|
2883
|
+
});
|
|
2884
|
+
modelsIndex.formatText();
|
|
2885
|
+
modelsIndex.saveSync();
|
|
2886
|
+
}
|
|
2887
|
+
};
|
|
2888
|
+
__name(_RequestParamsGenerator, "RequestParamsGenerator");
|
|
2889
|
+
var RequestParamsGenerator = _RequestParamsGenerator;
|
|
2890
|
+
|
|
2732
2891
|
// src/lib/generators/service/service.generator.ts
|
|
2733
2892
|
var _ServiceGenerator = class _ServiceGenerator {
|
|
2734
2893
|
constructor(parser, project, config) {
|
|
@@ -2737,6 +2896,7 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2737
2896
|
__publicField(this, "spec");
|
|
2738
2897
|
__publicField(this, "config");
|
|
2739
2898
|
__publicField(this, "methodGenerator");
|
|
2899
|
+
__publicField(this, "requestObjects");
|
|
2740
2900
|
this.config = config;
|
|
2741
2901
|
this.project = project;
|
|
2742
2902
|
this.parser = parser;
|
|
@@ -2749,24 +2909,29 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2749
2909
|
}
|
|
2750
2910
|
generate(outputRoot) {
|
|
2751
2911
|
return __async(this, null, function* () {
|
|
2752
|
-
const outputDir =
|
|
2912
|
+
const outputDir = path10.join(outputRoot, "services");
|
|
2753
2913
|
const paths = extractPaths(this.spec.paths);
|
|
2754
2914
|
if (paths.length === 0) {
|
|
2755
2915
|
console.warn("No API paths found in the specification");
|
|
2756
2916
|
return;
|
|
2757
2917
|
}
|
|
2758
2918
|
const controllerGroups = this.groupPathsByController(paths);
|
|
2919
|
+
if (this.config.options.useSingleRequestParameter) {
|
|
2920
|
+
const requestParamsGenerator = new RequestParamsGenerator(this.parser, this.project, this.config);
|
|
2921
|
+
this.requestObjects = requestParamsGenerator.buildRegistry(controllerGroups, (operation) => this.methodGenerator.generateMethodName(operation));
|
|
2922
|
+
requestParamsGenerator.generate(outputRoot);
|
|
2923
|
+
}
|
|
2759
2924
|
yield Promise.all(Object.entries(controllerGroups).map(([controllerName, operations]) => this.generateServiceFile(controllerName, operations, outputDir)));
|
|
2760
2925
|
});
|
|
2761
2926
|
}
|
|
2762
2927
|
groupPathsByController(paths) {
|
|
2763
2928
|
const groups = {};
|
|
2764
|
-
paths.forEach((
|
|
2929
|
+
paths.forEach((path13) => {
|
|
2765
2930
|
let controllerName = "Default";
|
|
2766
|
-
if (
|
|
2767
|
-
controllerName =
|
|
2931
|
+
if (path13.tags && path13.tags.length > 0) {
|
|
2932
|
+
controllerName = path13.tags[0];
|
|
2768
2933
|
} else {
|
|
2769
|
-
const pathParts =
|
|
2934
|
+
const pathParts = path13.path.split("/").filter((p) => p && !p.startsWith("{"));
|
|
2770
2935
|
if (pathParts.length > 1) {
|
|
2771
2936
|
controllerName = pascalCase(pathParts[1]);
|
|
2772
2937
|
}
|
|
@@ -2775,14 +2940,14 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2775
2940
|
if (!groups[controllerName]) {
|
|
2776
2941
|
groups[controllerName] = [];
|
|
2777
2942
|
}
|
|
2778
|
-
groups[controllerName].push(
|
|
2943
|
+
groups[controllerName].push(path13);
|
|
2779
2944
|
});
|
|
2780
2945
|
return groups;
|
|
2781
2946
|
}
|
|
2782
2947
|
generateServiceFile(controllerName, operations, outputDir) {
|
|
2783
2948
|
return __async(this, null, function* () {
|
|
2784
2949
|
const fileName = `${camelCase(controllerName)}.service.ts`;
|
|
2785
|
-
const filePath =
|
|
2950
|
+
const filePath = path10.join(outputDir, fileName);
|
|
2786
2951
|
const sourceFile = this.project.createSourceFile(filePath, "", {
|
|
2787
2952
|
overwrite: true
|
|
2788
2953
|
});
|
|
@@ -2884,7 +3049,8 @@ var _ServiceGenerator = class _ServiceGenerator {
|
|
|
2884
3049
|
return context.set(this.clientContextToken, '${this.config.clientName || "default"}');`
|
|
2885
3050
|
});
|
|
2886
3051
|
operations.forEach((operation) => {
|
|
2887
|
-
|
|
3052
|
+
var _a;
|
|
3053
|
+
this.methodGenerator.addServiceMethod(serviceClass, operation, (_a = this.requestObjects) == null ? void 0 : _a.get(operation));
|
|
2888
3054
|
});
|
|
2889
3055
|
if (hasDuplicateFunctionNames(serviceClass.getMethods())) {
|
|
2890
3056
|
throw new Error(`Duplicate method names found in service class ${className}. Please ensure unique method names for each operation.`);
|
|
@@ -2896,15 +3062,15 @@ var ServiceGenerator = _ServiceGenerator;
|
|
|
2896
3062
|
|
|
2897
3063
|
// src/lib/generators/service/service-index.generator.ts
|
|
2898
3064
|
var fs2 = __toESM(require("fs"));
|
|
2899
|
-
var
|
|
3065
|
+
var path11 = __toESM(require("path"));
|
|
2900
3066
|
var _ServiceIndexGenerator = class _ServiceIndexGenerator {
|
|
2901
3067
|
constructor(project) {
|
|
2902
3068
|
__publicField(this, "project");
|
|
2903
3069
|
this.project = project;
|
|
2904
3070
|
}
|
|
2905
3071
|
generateIndex(outputRoot) {
|
|
2906
|
-
const servicesDir =
|
|
2907
|
-
const indexPath =
|
|
3072
|
+
const servicesDir = path11.join(outputRoot, "services");
|
|
3073
|
+
const indexPath = path11.join(servicesDir, "index.ts");
|
|
2908
3074
|
const sourceFile = this.project.createSourceFile(indexPath, "", {
|
|
2909
3075
|
overwrite: true
|
|
2910
3076
|
});
|
|
@@ -2927,7 +3093,7 @@ var ServiceIndexGenerator = _ServiceIndexGenerator;
|
|
|
2927
3093
|
|
|
2928
3094
|
// src/lib/core/generator.ts
|
|
2929
3095
|
var fs3 = __toESM(require("fs"));
|
|
2930
|
-
var
|
|
3096
|
+
var path12 = __toESM(require("path"));
|
|
2931
3097
|
function validateInput(inputPath) {
|
|
2932
3098
|
if (isUrl(inputPath)) {
|
|
2933
3099
|
return;
|
|
@@ -2935,7 +3101,7 @@ function validateInput(inputPath) {
|
|
|
2935
3101
|
if (!fs3.existsSync(inputPath)) {
|
|
2936
3102
|
throw new Error(`Input file not found: ${inputPath}`);
|
|
2937
3103
|
}
|
|
2938
|
-
const extension =
|
|
3104
|
+
const extension = path12.extname(inputPath).toLowerCase();
|
|
2939
3105
|
const supportedExtensions = [
|
|
2940
3106
|
".json",
|
|
2941
3107
|
".yaml",
|
|
@@ -3031,6 +3197,7 @@ __name(generateFromConfig, "generateFromConfig");
|
|
|
3031
3197
|
HTTP_RESOURCE_GENERATOR_HEADER_COMMENT,
|
|
3032
3198
|
MAIN_INDEX_GENERATOR_HEADER_COMMENT,
|
|
3033
3199
|
PROVIDER_GENERATOR_HEADER_COMMENT,
|
|
3200
|
+
REQUEST_PARAMS_GENERATOR_HEADER_COMMENT,
|
|
3034
3201
|
SERVICE_GENERATOR_HEADER_COMMENT,
|
|
3035
3202
|
SERVICE_INDEX_GENERATOR_HEADER_COMMENT,
|
|
3036
3203
|
SwaggerParser,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ng-openapi",
|
|
3
|
-
"version": "0.2.22-pr-
|
|
3
|
+
"version": "0.2.22-pr-92-feature-single-request-parameter-0b340a2.0",
|
|
4
4
|
"description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ng-openapi",
|