11{ {> licenseInfo}  }
2- import {  HttpHeaders, HttpParams, HttpParameterCodec }   from '@angular/common/http';
3- import {  CustomHttpParameterCodec }   from './encoder';
2+ import { HttpHeaders, HttpParams, HttpParameterCodec}   from '@angular/common/http';
43import {  {{configurationClassName}  } } from './configuration';
4+ import { CustomHttpParameterCodec, NoOpHttpParameterCodec}   from './encoder';
5+ 
6+ export enum QueryParamStyle { 
7+     Json, 
8+     Form, 
9+     DeepObject, 
10+     SpaceDelimited, 
11+     PipeDelimited, 
12+ } 
13+ 
14+ export type Delimiter = "," | " " | "|" | "\t";
15+ 
16+ export interface ParamOptions { 
17+     /** When true , serialized as multiple repeated key= value pairs. When false , serialized as a single key with joined values using `delimiter`. */ 
18+     explode?: boolean; 
19+     /** Delimiter used when explode= false . The delimiter itself is inserted unencoded between encoded values. */ 
20+     delimiter?: Delimiter; 
21+ } 
22+ 
23+ interface ParamEntry { 
24+     values: string[]; 
25+     options: Required< ParamOptions> ; 
26+ } 
27+ 
28+ export class OpenApiHttpParams { 
29+     private params: Map< string, ParamEntry>  = new Map(); 
30+     private defaults: Required< ParamOptions> ; 
31+     private encoder: HttpParameterCodec; 
32+ 
33+     /** 
34+      * @param encoder  Parameter serializer 
35+      * @param defaults Global defaults used when a specific parameter has no explicit options. 
36+      *                 By OpenAPI default , explode is true  for query params with style= form. 
37+      */ 
38+     constructor(encoder?: HttpParameterCodec, defaults?: { explode?: boolean; delimiter?: Delimiter }  ) { 
39+         this.encoder = encoder ||  new CustomHttpParameterCodec(); 
40+         this.defaults = { 
41+             explode: defaults?.explode ?? true , 
42+             delimiter: defaults?.delimiter ?? " ,"  , 
43+         }  ;
44+     }
45+ 
46+     private resolveOptions(local?: ParamOptions): Required<ParamOptions > { 
47+         return { 
48+             explode: local?.explode ?? this.defaults.explode, 
49+             delimiter: local?.delimiter ?? this.defaults.delimiter, 
50+         }  ;
51+     }
52+ 
53+     /**
54+      * Replace the parameter's values and (optionally) its options.
55+      * Options are stored per-parameter (not global).
56+      */
57+     set(key: string, values: string[] | string, options?: ParamOptions): this { 
58+         const arr = Array.isArray(values) ? values.slice() : [values]; 
59+         const opts = this.resolveOptions(options); 
60+         this.params.set(key, {values: arr, options: opts}  );
61+         return this;
62+     }
63+ 
64+     /**
65+      * Append a single value to the parameter. If the parameter didn't exist it will be created
66+      * and use resolved options (global defaults merged with any provided options).
67+      */
68+     append(key: string, value: string, options?: ParamOptions): this { 
69+         const entry = this.params.get(key); 
70+         if  (entry) { 
71+             // If new options provided, override the stored options for subsequent serialization 
72+             if  (options) { 
73+                 entry.options = this.resolveOptions({...entry.options, ...options}  );
74+             }
75+             entry.values.push(value);
76+         } else { 
77+             this.set(key, [value], options); 
78+         }  
79+         return this;
80+     }
81+ 
82+     /**
83+      * Serialize to a query string according to per-parameter OpenAPI options.
84+      * - If explode=true for that parameter → repeated key=value pairs (each value encoded).
85+      * - If explode=false for that parameter → single key=value where values are individually encoded
86+      *   and joined using the configured delimiter. The delimiter character is inserted AS-IS
87+      *   (not percent-encoded).
88+      */
89+     toString(): string { 
90+         const records = this.toRecord(); 
91+         const parts: string[] = []; 
92+ 
93+         for (const key in records) { 
94+             parts.push(`${key}  =${ records[key]}  `);
95+         }
96+ 
97+         return parts.join("& ");
98+     }
99+ 
100+     /**
101+      * Return parameters as a plain record.
102+      * - If a parameter has exactly one value, returns that value directly.
103+      * - If a parameter has multiple values, returns a readonly array of values.
104+      */
105+     toRecord(): Record<string ,  string  |  number  |  boolean  |  ReadonlyArray <string  |  number  |  boolean >> { 
106+         const parts: Record< string, string | number | boolean | ReadonlyArray< string | number | boolean>>  = {}  ;
107+ 
108+         for (const [key, entry] of this.params.entries()) { 
109+             const encodedKey = this.encoder.encodeKey(key); 
110+ 
111+             if  (entry.options.explode) { 
112+                 parts[encodedKey] = entry.values.map((v) =>  this.encoder.encodeValue(v)); 
113+             }   else { 
114+                 const encodedValues = entry.values.map((v) =>  this.encoder.encodeValue(v)); 
115+ 
116+                 // join with the delimiter *unencoded* 
117+                 parts[encodedKey] = encodedValues.join(entry.options.delimiter); 
118+             }  
119+         }
120+ 
121+         return parts;
122+     }
123+ 
124+     /**
125+      * Return an Angular's HttpParams with a NoOp parameter codec as the parameters are already encoded.
126+      */
127+     toHttpParams(): HttpParams { 
128+         const records = this.toRecord(); 
129+ 
130+         let httpParams = new HttpParams({encoder: new NoOpHttpParameterCodec()}  );
131+ 
132+         return httpParams.appendAll(records);
133+     }
134+ }
135+ 
136+ function concatHttpParamsObject(httpParams: OpenApiHttpParams, key: string, item: { 
137+     [index: string]: any 
138+ }  , delimiter: Delimiter): OpenApiHttpParams { 
139+     let keyAndValues: string[] = []; 
140+ 
141+     for (const k in item) { 
142+         keyAndValues.push(k); 
143+         keyAndValues.push(item[k].toString()); 
144+     }  
145+ 
146+     return httpParams.set(key, keyAndValues, { explode: false , delimiter: delimiter}  );
147+ }
5148
6149export class BaseService { 
7-     protected basePath = ' {{{basePath}}} '  ; 
150+     protected basePath = ' http://localhost '  ; 
8151    public defaultHeaders = new HttpHeaders(); 
9-     public configuration: {{configurationClassName }  } ;
152+     public configuration: Configuration ; 
10153    public encoder: HttpParameterCodec; 
11154
12-     constructor(basePath?: string| string[], configuration?: { {configurationClassName } } ) { 
13-         this.configuration = configuration ||  new {{configurationClassName }  } ();
155+     constructor(basePath?: string |  string[], configuration?: Configuration ) { 
156+         this.configuration = configuration ||  new Configuration (); 
14157        if  (typeof this.configuration.basePath !==  ' string'  ) { 
15158            const firstBasePath = Array.isArray(basePath) ? basePath[0] : undefined; 
16159            if  (firstBasePath !=  undefined) { 
@@ -29,47 +172,58 @@ export class BaseService {
29172        return consumes.indexOf(' multipart/form-data'  ) !==  -1; 
30173    }  
31174
32-     protected addToHttpParams(httpParams: HttpParams, value: any, key?: string, isDeep: boolean = false): HttpParams { 
33-         // If the value is an object (but not  a Date), recursively add its keys. 
34-         if  (typeof value ===  ' object'   &&  ! (value instanceof Date)) { 
35-             return this.addToHttpParamsRecursive(httpParams, value, isDeep ? key : undefined, isDeep); 
36-         }  
37-         return this.addToHttpParamsRecursive(httpParams, value, key);
38-     }
39- 
40-     protected addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string, isDeep: boolean = false): HttpParams { 
175+     protected addToHttpParams(httpParams: OpenApiHttpParams, key: string, value: any | null | undefined, paramStyle: QueryParamStyle, explode: boolean): OpenApiHttpParams { 
41176        if  (value ===  null ||  value ===  undefined) { 
42177            return httpParams; 
43178        }  
44-         if (typeof value === 'object') { 
45-             // If JSON format is preferred, key must be provided. 
46-             if  (key !=  null) { 
47-                 return isDeep 
48-                     ? Object.keys(value as Record< string, any> ).reduce( 
49-                         (hp, k) =>  hp.append(`${key}  [${ k}  ]`, value[k]),
50-                         httpParams,
51-                     )
52-                     : httpParams.append(key, JSON.stringify(value));
179+ 
180+         if (paramStyle === QueryParamStyle.DeepObject) { 
181+             if  (typeof value !==  ' object'  ) { 
182+                 throw Error(`An object must be provided for key ${key}   as it is a deep object`);
53183            }
54-             // Otherwise, if it's an array, add each element.
55-             if (Array.isArray(value)) { 
56-                 value.forEach(elem =>  httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); 
184+ 
185+             return Object.keys(value as Record<string ,  any >).reduce(
186+                 (hp, k) => hp.append(`${ key}  [${ k}  ]`, value[k]),
187+                 httpParams,
188+             );
189+         } else if (paramStyle === QueryParamStyle.Json) { 
190+             return httpParams.append(key, JSON.stringify(value)); 
191+         }   else { 
192+             // Form-style, SpaceDelimited or  PipeDelimited 
193+ 
194+             if  (Object(value) !==  value) { 
195+                 // If it is a primitive type, add its string representation 
196+                 return httpParams.append(key, value.toString()); 
57197            }   else if (value instanceof Date) { 
58-                 if  (key !=  null) { 
59-                     httpParams = httpParams.append(key, value.toISOString()); 
198+                 return httpParams.append(key, value.toISOString()); 
199+             }   else if (Array.isArray(value)) { 
200+                 // Otherwise, if  it' s an array, add each element. 
201+                 if (paramStyle === QueryParamStyle.Form) { 
202+                     return httpParams.set(key, value, {explode: explode, delimiter: '  ,' });
203+                 } else if (paramStyle === QueryParamStyle.SpaceDelimited) { 
204+                     return httpParams.set(key, value, {explode: explode, delimiter: '   ' });
60205                } else { 
61-                     throw Error(" key may not be null if value is Date"  ); 
206+                     // PipeDelimited 
207+                     return httpParams.set(key, value, {explode: explode, delimiter: '  |' });
62208                } 
63209            } else { 
64-                 Object.keys(value).forEach(k =>  { 
65-                     const paramKey = key ? `${key}  .${ k}  ` : k;
66-                     httpParams = this.addToHttpParamsRecursive(httpParams, value[k], paramKey);
67-                 });
210+                 // Otherwise, if it'  s an object, add each field.
211+                 if  (paramStyle ===  QueryParamStyle.Form) { 
212+                     if  (explode) { 
213+                         Object.keys(value).forEach(k =>  { 
214+                             httpParams = httpParams.append(k, value[k]); 
215+                         }  );
216+                         return httpParams;
217+                     } else { 
218+                         return concatHttpParamsObject(httpParams, key, value, ' ,'  ); 
219+                     }  
220+                 } else if (paramStyle === QueryParamStyle.SpaceDelimited) { 
221+                     return concatHttpParamsObject(httpParams, key, value, '  '  ); 
222+                 }   else { 
223+                     // PipeDelimited 
224+                     return concatHttpParamsObject(httpParams, key, value, ' |'  ); 
225+                 }  
68226            }
69-             return httpParams;
70-         } else if (key != null) { 
71-             return httpParams.append(key, value); 
72227        }
73-         throw Error("key may not be null if value is not object or array");
74228    }
75229}
0 commit comments