@agravity/public
Version:
The Agravity GlobalDAM API which allowes API key authenticated access the Agravity GlobalDAM Backend
166 lines (139 loc) • 5.16 kB
text/typescript
import { HttpParams, HttpParameterCodec } from '@angular/common/http';
import { CustomHttpParameterCodec, IdentityHttpParameterCodec } from './encoder';
export enum QueryParamStyle {
Json,
Form,
DeepObject,
SpaceDelimited,
PipeDelimited
}
export type Delimiter = ',' | ' ' | '|' | '\t';
export interface ParamOptions {
/** When true, serialized as multiple repeated key=value pairs. When false, serialized as a single key with joined values using `delimiter`. */
explode?: boolean;
/** Delimiter used when explode=false. The delimiter itself is inserted unencoded between encoded values. */
delimiter?: Delimiter;
}
interface ParamEntry {
values: string[];
options: Required<ParamOptions>;
}
export class OpenApiHttpParams {
private params: Map<string, ParamEntry> = new Map();
private defaults: Required<ParamOptions>;
private encoder: HttpParameterCodec;
/**
* @param encoder Parameter serializer
* @param defaults Global defaults used when a specific parameter has no explicit options.
* By OpenAPI default, explode is true for query params with style=form.
*/
constructor(encoder?: HttpParameterCodec, defaults?: { explode?: boolean; delimiter?: Delimiter }) {
this.encoder = encoder || new CustomHttpParameterCodec();
this.defaults = {
explode: defaults?.explode ?? true,
delimiter: defaults?.delimiter ?? ','
};
}
private resolveOptions(local?: ParamOptions): Required<ParamOptions> {
return {
explode: local?.explode ?? this.defaults.explode,
delimiter: local?.delimiter ?? this.defaults.delimiter
};
}
/**
* Replace the parameter's values and (optionally) its options.
* Options are stored per-parameter (not global).
*/
set(key: string, values: string[] | string, options?: ParamOptions): this {
const arr = Array.isArray(values) ? values.slice() : [values];
const opts = this.resolveOptions(options);
this.params.set(key, { values: arr, options: opts });
return this;
}
/**
* Append a single value to the parameter. If the parameter didn't exist it will be created
* and use resolved options (global defaults merged with any provided options).
*/
append(key: string, value: string, options?: ParamOptions): this {
const entry = this.params.get(key);
if (entry) {
// If new options provided, override the stored options for subsequent serialization
if (options) {
entry.options = this.resolveOptions({ ...entry.options, ...options });
}
entry.values.push(value);
} else {
this.set(key, [value], options);
}
return this;
}
/**
* Serialize to a query string according to per-parameter OpenAPI options.
* - If explode=true for that parameter → repeated key=value pairs (each value encoded).
* - If explode=false for that parameter → single key=value where values are individually encoded
* and joined using the configured delimiter. The delimiter character is inserted AS-IS
* (not percent-encoded).
*/
toString(): string {
const records = this.toRecord();
const parts: string[] = [];
for (const key in records) {
parts.push(`${key}=${records[key]}`);
}
return parts.join('&');
}
/**
* Return parameters as a plain record.
* - If a parameter has exactly one value, returns that value directly.
* - If a parameter has multiple values, returns a readonly array of values.
*/
toRecord(): Record<string, string | number | boolean | ReadonlyArray<string | number | boolean>> {
const parts: Record<string, string | number | boolean | ReadonlyArray<string | number | boolean>> = {};
for (const [key, entry] of this.params.entries()) {
const encodedKey = this.encoder.encodeKey(key);
if (entry.options.explode) {
parts[encodedKey] = entry.values.map((v) => this.encoder.encodeValue(v));
} else {
const encodedValues = entry.values.map((v) => this.encoder.encodeValue(v));
// join with the delimiter *unencoded*
parts[encodedKey] = encodedValues.join(entry.options.delimiter);
}
}
return parts;
}
/**
* Return an Angular's HttpParams with an identity parameter codec as the parameters are already encoded.
*/
toHttpParams(): HttpParams {
const records = this.toRecord();
let httpParams = new HttpParams({ encoder: new IdentityHttpParameterCodec() });
return httpParams.appendAll(records);
}
}
export function concatHttpParamsObject(
httpParams: OpenApiHttpParams,
key: string,
item: {
[index: string]: any;
},
delimiter: Delimiter
): OpenApiHttpParams {
let keyAndValues: string[] = [];
for (const k in item) {
keyAndValues.push(k);
const value = item[k];
if (Array.isArray(value)) {
keyAndValues.push(...value.map(convertToString));
} else {
keyAndValues.push(convertToString(value));
}
}
return httpParams.set(key, keyAndValues, { explode: false, delimiter: delimiter });
}
function convertToString(value: any): string {
if (value instanceof Date) {
return value.toISOString();
} else {
return value.toString();
}
}