fix: body param and 2xx response codes in repository generation
All checks were successful
Lint / lint (pull_request) Successful in 31s

Bug 1 - Body as positional argument (api.repository.impl.mustache):
MRepository HTTP methods accept a single RequestOptions object as second
argument. The template was incorrectly passing body as a separate positional
argument (e.g. this.post('/url', body)), causing:
  'Type X has no properties in common with type RequestOptions'
Fix: merge body into the options object using ES6 shorthand { body }, and
introduce hasOptions / hasBothParamsAndBody flags to build a single unified
options literal covering all scenarios:
  - no options    → this.post('/url')
  - params only   → this.get('/url', { params: { search } })
  - body only     → this.post('/url', { body })
  - params + body → this.post('/url', { params: { search }, body })

Bug 2 - Only 200 responses read (clean-arch.generator.ts):
The generator was hardcoded to read op.responses['200'], silently ignoring
201 Created, 202 Accepted, etc. POST endpoints returning 201 were generated
as Observable<void> instead of their actual return type.
Fix: resolve the first available success code from [200, 201, 202, 203].

New fields added to TagOperation type:
  - uppercaseHttpMethod: string
  - hasOptions: boolean
  - hasBothParamsAndBody: boolean

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
didavila
2026-03-26 11:17:37 +01:00
parent 463626da0c
commit d47afb6ff1
3 changed files with 17 additions and 12 deletions

View File

@@ -32,26 +32,20 @@ export class {{classname}}RepositoryImpl extends MRepository implements {{classn
{{#operation}}
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{^-last}}, {{/-last}}{{/allParams}}): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
{{#isListContainer}}
return this.{{httpMethod}}<{{{returnBaseType}}}Dto>('{{path}}'{{#hasQueryParams}}, {
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}})
return this.{{httpMethod}}<{{{returnBaseType}}}Dto>('{{path}}'{{#hasOptions}}, { {{#hasQueryParams}}params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }{{/hasQueryParams}}{{#hasBothParamsAndBody}}, {{/hasBothParamsAndBody}}{{#hasBodyParam}}body{{/hasBodyParam}} }{{/hasOptions}})
.pipe(
map((response) => response.{{#vendorExtensions}}{{x-response-property}}{{/vendorExtensions}}{{^vendorExtensions}}items{{/vendorExtensions}}.map({{{returnBaseTypeVarName}}}Mapper))
);
{{/isListContainer}}
{{^isListContainer}}
{{#returnType}}
return this.{{httpMethod}}<{{{returnType}}}Dto>('{{path}}'{{#hasQueryParams}}, {
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}})
return this.{{httpMethod}}<{{{returnType}}}Dto>('{{path}}'{{#hasOptions}}, { {{#hasQueryParams}}params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }{{/hasQueryParams}}{{#hasBothParamsAndBody}}, {{/hasBothParamsAndBody}}{{#hasBodyParam}}body{{/hasBodyParam}} }{{/hasOptions}})
.pipe(
map({{{returnTypeVarName}}}Mapper)
);
{{/returnType}}
{{^returnType}}
return this.{{httpMethod}}<void>('{{path}}'{{#hasQueryParams}}, {
params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }
}{{/hasQueryParams}}{{#hasBodyParam}}, {{bodyParam}}{{/hasBodyParam}});
return this.{{httpMethod}}<void>('{{path}}'{{#hasOptions}}, { {{#hasQueryParams}}params: { {{#queryParams}}{{paramName}}{{^-last}}, {{/-last}}{{/queryParams}} }{{/hasQueryParams}}{{#hasBothParamsAndBody}}, {{/hasBothParamsAndBody}}{{#hasBodyParam}}body{{/hasBodyParam}} }{{/hasOptions}});
{{/returnType}}
{{/isListContainer}}
}