@@ -98,20 +98,20 @@ export class ServiceMethodGenerator {
9898 lines . push ( `}` ) ;
9999 } ) ;
100100
101- // 2. Path Construction
101+ // 2. Path Construction (Using generic serializer)
102102 let urlTemplate = model . urlTemplate ;
103103 model . pathParams . forEach ( p => {
104- const serializeCall = `HttpParamsBuilder .serializePathParam('${ p . originalName } ', ${ p . paramName } , '${ p . style || 'simple' } ', ${ p . explode } , ${ p . allowReserved } ${ p . serializationLink === 'json' ? ", 'json'" : "" } )` ;
104+ const serializeCall = `ParameterSerializer .serializePathParam('${ p . originalName } ', ${ p . paramName } , '${ p . style || 'simple' } ', ${ p . explode } , ${ p . allowReserved } ${ p . serializationLink === 'json' ? ", 'json'" : "" } )` ;
105105 urlTemplate = urlTemplate . replace ( `{${ p . originalName } }` , `\${${ serializeCall } }` ) ;
106106 } ) ;
107107
108- // 3. Query String Logic (Legacy)
108+ // 3. Query String Logic (Legacy) - adapted to generic serializer
109109 const qsParam = rawOp . parameters ?. find ( p => ( p . in as any ) === 'querystring' ) ;
110110 let queryStringVariable = '' ;
111111 if ( qsParam ) {
112112 const pName = camelCase ( qsParam . name ) ;
113113 const hint = ( qsParam as any ) . content ?. [ 'application/json' ] ? ", 'json'" : "" ;
114- lines . push ( `const queryString = HttpParamsBuilder .serializeRawQuerystring(${ pName } ${ hint } );` ) ;
114+ lines . push ( `const queryString = ParameterSerializer .serializeRawQuerystring(${ pName } ${ hint } );` ) ;
115115 queryStringVariable = "${queryString ? '?' + queryString : ''}" ;
116116 }
117117
@@ -123,10 +123,11 @@ export class ServiceMethodGenerator {
123123 }
124124 lines . push ( `const url = \`\${basePath}${ urlTemplate } ${ queryStringVariable } \`;` ) ;
125125
126- // 5. Query Params
126+ // 5. Query Params (Using generic serializer and adapting to Angular HttpParams)
127127 const standardQueryParams = model . queryParams . filter ( p => p . originalName !== qsParam ?. name ) ;
128128
129129 if ( standardQueryParams . length > 0 ) {
130+ // Angular HttpParams requires 'encoder'
130131 lines . push ( `let params = new HttpParams({ encoder: new ApiParameterCodec(), fromObject: options?.params ?? {} });` ) ;
131132 standardQueryParams . forEach ( p => {
132133 const configObj = JSON . stringify ( {
@@ -135,17 +136,20 @@ export class ServiceMethodGenerator {
135136 style : p . style ,
136137 explode : p . explode ,
137138 allowReserved : p . allowReserved ,
138- serialization : p . serializationLink
139+ serialization : p . serializationLink ,
140+ // Support for OAS 3.2 allowEmptyValue - passed via config object
141+ allowEmptyValue : ( p as any ) . allowEmptyValue
139142 } ) ;
140- lines . push ( `if (${ p . paramName } != null) { params = HttpParamsBuilder.serializeQueryParam(params, ${ configObj } , ${ p . paramName } ); }` ) ;
143+ lines . push ( `const serialized_${ p . paramName } = ParameterSerializer.serializeQueryParam(${ configObj } , ${ p . paramName } );` ) ;
144+ lines . push ( `serialized_${ p . paramName } .forEach(entry => params = params.append(entry.key, entry.value));` ) ;
141145 } ) ;
142146 }
143147
144148 // 6. Headers
145149 lines . push ( `let headers = options?.headers instanceof HttpHeaders ? options.headers : new HttpHeaders(options?.headers ?? {});` ) ;
146150 model . headerParams . forEach ( p => {
147151 const hint = p . serializationLink === 'json' ? ", 'json'" : "" ;
148- lines . push ( `if (${ p . paramName } != null) { headers = headers.set('${ p . originalName } ', HttpParamsBuilder .serializeHeaderParam(' ${ p . originalName } ', ${ p . paramName } , ${ p . explode } ${ hint } )); }` ) ;
152+ lines . push ( `if (${ p . paramName } != null) { headers = headers.set('${ p . originalName } ', ParameterSerializer .serializeHeaderParam(${ p . paramName } , ${ p . explode } ${ hint } )); }` ) ;
149153 } ) ;
150154
151155 // 7. Cookies
@@ -157,15 +161,12 @@ export class ServiceMethodGenerator {
157161 lines . push ( `const __cookies: string[] = [];` ) ;
158162 model . cookieParams . forEach ( p => {
159163 const hint = p . serializationLink === 'json' ? ", 'json'" : "" ;
160- lines . push ( `if (${ p . paramName } != null) { __cookies.push(HttpParamsBuilder .serializeCookieParam('${ p . originalName } ', ${ p . paramName } , '${ p . style || 'form' } ', ${ p . explode } , ${ p . allowReserved } ${ hint } )); }` ) ;
164+ lines . push ( `if (${ p . paramName } != null) { __cookies.push(ParameterSerializer .serializeCookieParam('${ p . originalName } ', ${ p . paramName } , '${ p . style || 'form' } ', ${ p . explode } , ${ p . allowReserved } ${ hint } )); }` ) ;
161165 } ) ;
162166 lines . push ( `if (__cookies.length > 0) { headers = headers.set('Cookie', __cookies.join('; ')); }` ) ;
163167 }
164168
165169 // 8. Content Negotiation Setup
166- // Detect requested media type from headers
167- // If negotiation is active, we don't default responseType blindly.
168- // We check if the user ASKED for XML.
169170 if ( hasContentNegotiation ) {
170171 lines . push ( `const acceptHeader = headers.get('Accept');` ) ;
171172 }
@@ -179,15 +180,9 @@ export class ServiceMethodGenerator {
179180 contextConstruction += `.set(EXTENSIONS_CONTEXT_TOKEN, ${ JSON . stringify ( model . extensions ) } )` ;
180181 }
181182
182- // Determine the responseType value to pass to Angular.
183- // If we are negotiating, we switch based on acceptHeader.
184- // Otherwise, use model default.
185183 let responseTypeVal = `options?.responseType` ;
186184
187185 if ( hasContentNegotiation ) {
188- // Build a condition stack
189- // if (acceptHeader?.includes('application/xml')) 'text' else default
190- // Note: JSON-Seq/XML variants ALWAYS require 'text' to allow manual parsing
191186 const xmlOrSeqCondition = model . responseVariants
192187 . filter ( v => v . serialization === 'xml' || v . serialization . startsWith ( 'json-' ) )
193188 . map ( v => `acceptHeader?.includes('${ v . mediaType } ')` )
@@ -217,7 +212,6 @@ export class ServiceMethodGenerator {
217212 // 10. Body Handling
218213 let bodyArgument = 'null' ;
219214 const body = model . body ;
220- // ... (body logic same as before)
221215 const legacyFormData = rawOp . parameters ?. filter ( p => ( p as any ) . in === 'formData' ) ;
222216 const isUrlEnc = rawOp . consumes ?. includes ( 'application/x-www-form-urlencoded' ) ;
223217
@@ -246,7 +240,10 @@ export class ServiceMethodGenerator {
246240 lines . push ( `}` ) ;
247241 }
248242 } else if ( body . type === 'urlencoded' ) {
249- lines . push ( `const formBody = HttpParamsBuilder.serializeUrlEncodedBody(${ body . paramName } , ${ JSON . stringify ( body . config ) } );` ) ;
243+ // Use generic serializer then adapt to Angular HttpParams
244+ lines . push ( `const urlParamEntries = ParameterSerializer.serializeUrlEncodedBody(${ body . paramName } , ${ JSON . stringify ( body . config ) } );` ) ;
245+ lines . push ( `let formBody = new HttpParams({ encoder: new ApiParameterCodec() });` ) ;
246+ lines . push ( `urlParamEntries.forEach(entry => formBody = formBody.append(entry.key, entry.value));` ) ;
250247 bodyArgument = 'formBody' ;
251248 } else if ( body . type === 'multipart' ) {
252249 lines . push ( `const multipartConfig = ${ JSON . stringify ( body . config ) } ;` ) ;
@@ -264,7 +261,6 @@ export class ServiceMethodGenerator {
264261 }
265262
266263 if ( isSSE ) {
267- // SSE logic remains same
268264 lines . push ( `
269265 return new Observable<${ model . responseType } >(observer => {
270266 const eventSource = new EventSource(url);
@@ -282,7 +278,6 @@ export class ServiceMethodGenerator {
282278 const isStandardBody = [ 'post' , 'put' , 'patch' , 'query' ] . includes ( httpMethod ) ;
283279 const isStandardNonBody = [ 'get' , 'delete' , 'head' , 'options' , 'jsonp' ] . includes ( httpMethod ) ;
284280
285- // Using generic <any> here because the specific mapping logic below casts it to the correct union member
286281 const returnGeneric = `any` ;
287282
288283 let httpCall = '' ;
@@ -301,13 +296,10 @@ export class ServiceMethodGenerator {
301296 }
302297
303298 // 12. Response Transformation Logic
304- // If we have content negotiation, we need runtime checks inside the map
305-
306299 if ( hasContentNegotiation ) {
307300 lines . push ( `return ${ httpCall } .pipe(` ) ;
308301 lines . push ( ` map(response => {` ) ;
309302
310- // Generate if/else blocks for each variant
311303 model . responseVariants . forEach ( v => {
312304 const check = `acceptHeader?.includes('${ v . mediaType } ')` ;
313305 lines . push ( ` // Handle ${ v . mediaType } ` ) ;
@@ -328,14 +320,12 @@ export class ServiceMethodGenerator {
328320 lines . push ( ` return response.split('${ delimiter } ').filter((p: string) => p.trim().length > 0).map((i: string) => JSON.parse(i));` ) ;
329321 lines . push ( ` }` ) ;
330322 } else if ( v . decodingConfig ) {
331- // JSON with decoding config
332323 lines . push ( ` if (${ check } ) {` ) ;
333324 lines . push ( ` return ContentDecoder.decode(response, ${ JSON . stringify ( v . decodingConfig ) } );` ) ;
334325 lines . push ( ` }` ) ;
335326 }
336327 } ) ;
337328
338- // Fallback if nothing matched but we still need default handling (e.g. default json decoding setup)
339329 const def = model . responseVariants . find ( v => v . isDefault ) ;
340330 if ( def && def . decodingConfig ) {
341331 lines . push ( ` // Default decoding` ) ;
@@ -348,7 +338,6 @@ export class ServiceMethodGenerator {
348338 lines . push ( `);` ) ;
349339
350340 } else {
351- // Legacy / Single Variant Logic
352341 const isSeq = model . responseSerialization === 'json-seq' || model . responseSerialization === 'json-lines' ;
353342 const isXmlResp = model . responseSerialization === 'xml' ;
354343
@@ -396,18 +385,14 @@ export class ServiceMethodGenerator {
396385 } ] ;
397386 }
398387
399- // 1. Strict Accept Overloads
400388 const distinctVariants = variants . filter ( ( v , i , a ) => a . findIndex ( t => t . mediaType === v . mediaType ) === i && v . mediaType !== '' ) ;
401389
402390 if ( distinctVariants . length > 1 ) {
403391 for ( const variant of distinctVariants ) {
404- // We generate a signature that requires specific headers if using this variant
405- // Note: TS Structural typing for headers inside options is complex.
406- // We approximate by specifying headers type as having the Accept key.
407392 overloads . push ( {
408393 parameters : [ ...parameters , {
409394 name : 'options' ,
410- hasQuestionToken : false , // Mandatory to select this overload
395+ hasQuestionToken : false ,
411396 type : `RequestOptions & { headers: { 'Accept': '${ variant . mediaType } ' } }`
412397 } ] ,
413398 returnType : `Observable<${ variant . type } >` ,
@@ -416,9 +401,6 @@ export class ServiceMethodGenerator {
416401 }
417402 }
418403
419- // 2. Standard Overloads (Default Priority / No explicit Accept)
420-
421- // 2a. Body
422404 overloads . push ( {
423405 parameters : [ ...parameters , {
424406 name : 'options' ,
@@ -429,7 +411,6 @@ export class ServiceMethodGenerator {
429411 docs : [ `${ methodName } . \n${ paramsDocs } \n@param options The options for this request.${ deprecationDoc } ` ]
430412 } ) ;
431413
432- // 2b. Response (Full)
433414 overloads . push ( {
434415 parameters : [ ...parameters , {
435416 name : 'options' ,
@@ -440,7 +421,6 @@ export class ServiceMethodGenerator {
440421 docs : [ `${ methodName } . \n${ paramsDocs } \n@param options The options for this request, with response observation enabled.${ deprecationDoc } ` ]
441422 } ) ;
442423
443- // 2c. Events
444424 overloads . push ( {
445425 parameters : [ ...parameters , {
446426 name : 'options' ,
0 commit comments