Skip to content

Commit a2b6879

Browse files
committed
fix: sdk exploded syntax - make parameter serialization optional
1 parent baa7fc6 commit a2b6879

File tree

23 files changed

+198
-85
lines changed

23 files changed

+198
-85
lines changed

packages/@ama-sdk/client-angular/src/api-angular-client.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ const DEFAULT_OPTIONS = {
5555
angularPlugins: [],
5656
requestPlugins: [],
5757
enableTokenization: false,
58-
disableFallback: false
58+
disableFallback: false,
59+
enableParameterSerialization: false
5960
} as const satisfies Omit<BaseApiAngularClientOptions, 'basePath' | 'httpClient'>;
6061

6162
/** Client to process the call to the API using Angular API */

packages/@ama-sdk/client-beacon/src/api-beacon-client.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ export interface BaseApiBeaconClientConstructor extends PartialExcept<Omit<BaseA
3535
const DEFAULT_OPTIONS = {
3636
replyPlugins: [] as never[],
3737
requestPlugins: [],
38-
enableTokenization: false
38+
enableTokenization: false,
39+
enableParameterSerialization: false
3940
} as const satisfies Omit<BaseApiBeaconClientOptions, 'basePath'>;
4041

4142
/**

packages/@ama-sdk/client-fetch/src/api-fetch-client.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ const DEFAULT_OPTIONS = {
5151
fetchPlugins: [],
5252
requestPlugins: [],
5353
enableTokenization: false,
54-
disableFallback: false
54+
disableFallback: false,
55+
enableParameterSerialization: false
5556
} as const satisfies Omit<BaseApiFetchClientOptions, 'basePath'>;
5657

5758
/** Client to process the call to the API using Fetch API */

packages/@ama-sdk/core/src/clients/api-angular-client.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ const DEFAULT_OPTIONS: Omit<BaseApiAngularClientOptions, 'basePath' | 'httpClien
8282
angularPlugins: [],
8383
requestPlugins: [],
8484
enableTokenization: false,
85-
disableFallback: false
85+
disableFallback: false,
86+
enableParameterSerialization: false
8687
};
8788

8889
/**

packages/@ama-sdk/core/src/clients/api-beacon-client.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ export interface BaseApiBeaconClientConstructor extends PartialExcept<Omit<BaseA
5252
const DEFAULT_OPTIONS: Omit<BaseApiBeaconClientOptions, 'basePath'> = {
5353
replyPlugins: [] as never[],
5454
requestPlugins: [],
55-
enableTokenization: false
55+
enableTokenization: false,
56+
enableParameterSerialization: false
5657
};
5758

5859
/**

packages/@ama-sdk/core/src/clients/api-fetch-client.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ const DEFAULT_OPTIONS: Omit<BaseApiFetchClientOptions, 'basePath'> = {
7373
fetchPlugins: [],
7474
requestPlugins: [],
7575
enableTokenization: false,
76-
disableFallback: false
76+
disableFallback: false,
77+
enableParameterSerialization: false
7778
};
7879

7980
/**

packages/@ama-sdk/core/src/fwk/api.helpers.ts

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type {
1313
* Prepares the url to be called
1414
* @param url Base url to be used
1515
* @param queryParameters Key value pair with the parameters. If the value is undefined, the key is dropped
16-
* @deprecated use {@link prepareUrlWithQueryParams} with query parameter serialization, will be removed in v14.
1716
*/
1817
export function prepareUrl(url: string, queryParameters: { [key: string]: string | undefined } = {}) {
1918
const queryPart = Object.keys(queryParameters)

packages/@ama-sdk/core/src/fwk/core/base-api-constructor.ts

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export interface BaseApiClientOptions {
2727
disableFallback?: boolean;
2828
/** Logger (optional, fallback to console logger if undefined) */
2929
logger?: Logger;
30+
/** Enable parameter serialization with exploded syntax */
31+
enableParameterSerialization?: boolean;
3032
/** Custom query parameter serialization method */
3133
serializeQueryParams?<T extends { [key: string]: SupportedParamType }>(queryParams: T, queryParamSerialization: { [p in keyof T]: ParamSerialization }): { [p in keyof T]: string };
3234
/** Custom query parameter serialization method */

packages/@ama-sdk/core/src/fwk/param-serialization.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,7 @@ export type QueryParamValueSerialization = { value: SupportedParamType } & Param
4747
* Serialize query parameters of request plugins
4848
* @param queryParams
4949
*/
50-
export function serializeRequestPluginQueryParams(queryParams: { [key: string]: string } | { [key: string]: QueryParamValueSerialization }) {
51-
// NOTE: Check if the type of the property values of `queryParams` is string
52-
if (isParamValueRecord(queryParams)) {
53-
return Object.entries(queryParams).reduce((acc, [paramKey, paramValue]) => {
54-
acc[paramKey] = `${paramKey}=${paramValue}`;
55-
return acc;
56-
}, {} as { [key: string]: string });
57-
}
50+
export function serializeRequestPluginQueryParams(queryParams: { [key: string]: QueryParamValueSerialization }) {
5851
const queryParamsValues: { [key: string]: SupportedParamType } = {};
5952
const queryParamSerialization: { [key: string]: ParamSerialization } = {};
6053
Object.entries(queryParams).forEach(([paramKey, paramValue]) => {

packages/@ama-sdk/core/src/plugins/additional-params/additional-params-sync.request.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
isParamValueRecord,
23
type QueryParamValueSerialization,
34
serializeRequestPluginQueryParams,
45
} from '../../fwk/param-serialization';
@@ -43,7 +44,11 @@ export class AdditionalParamsSyncRequest implements RequestPlugin {
4344
const body = this.additionalParams.body && isStringOrUndefined(data.body) ? this.additionalParams.body(data.body) : undefined;
4445

4546
if (queryParams) {
46-
data.queryParams = { ...data.queryParams, ...serializeRequestPluginQueryParams(queryParams) };
47+
if (data.enableParameterSerialization && !isParamValueRecord(queryParams)) {
48+
data.queryParams = { ...data.queryParams, ...serializeRequestPluginQueryParams(queryParams) };
49+
} else if (isParamValueRecord(queryParams)) {
50+
data.queryParams = { ...data.queryParams, ...queryParams };
51+
}
4752
}
4853

4954
if (body !== undefined) {

packages/@ama-sdk/core/src/plugins/additional-params/additional-params.request.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
isParamValueRecord,
23
type QueryParamValueSerialization,
34
serializeRequestPluginQueryParams,
45
} from '../../fwk/param-serialization';
@@ -52,7 +53,11 @@ export class AdditionalParamsRequest implements RequestPlugin {
5253
const body = this.additionalParams.body && isStringOrUndefined(data.body) ? this.additionalParams.body(data.body) : undefined;
5354

5455
if (queryParams) {
55-
data.queryParams = { ...data.queryParams, ...serializeRequestPluginQueryParams(queryParams) };
56+
if (data.enableParameterSerialization && !isParamValueRecord(queryParams)) {
57+
data.queryParams = { ...data.queryParams, ...serializeRequestPluginQueryParams(queryParams) };
58+
} else if (isParamValueRecord(queryParams)) {
59+
data.queryParams = { ...data.queryParams, ...queryParams };
60+
}
5661
}
5762

5863
if (body !== undefined) {

packages/@ama-sdk/core/src/plugins/additional-params/additional-params.spec.ts

+12-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('Additional Params Request Plugin', () => {
1212
const additionalGetParams = jest.fn().mockReturnValue({ test: 'ok' });
1313
const additionalBody = jest.fn().mockReturnValue('newBody');
1414

15-
const defaultGetParams = { defaultTest: 'defaultTest=ok' };
15+
const defaultGetParams = { defaultTest: 'ok' };
1616
const defaultBody = 'default';
1717
let options: RequestOptions;
1818

@@ -32,10 +32,11 @@ describe('Additional Params Request Plugin', () => {
3232

3333
const result = await runner.transform(options);
3434

35-
expect(result.queryParams.test).toBe('test=ok');
35+
expect(result.queryParams.test).toBe('ok');
3636
});
3737

3838
it('should add serialized query params', async () => {
39+
options.enableParameterSerialization = true;
3940
const plugin = new AdditionalParamsRequest({ queryParams:
4041
{
4142
primParam: { value: 'ok', explode: false, style: 'form' },
@@ -48,7 +49,7 @@ describe('Additional Params Request Plugin', () => {
4849
const result = await runner.transform(options);
4950

5051
expect(result.queryParams).toStrictEqual({
51-
defaultTest: 'defaultTest=ok',
52+
defaultTest: 'ok',
5253
primParam: 'primParam=ok',
5354
arrParam: 'arrParam=a%20b%20c',
5455
objParam: 'objParam%5Bprop1%5D=value1&objParam%5Bprop2%5D=value2'
@@ -62,7 +63,7 @@ describe('Additional Params Request Plugin', () => {
6263
const result = await runner.transform(options);
6364

6465
expect(additionalGetParams).toHaveBeenCalledWith(defaultGetParams);
65-
expect(result.queryParams.test).toBe('test=ok');
66+
expect(result.queryParams.test).toBe('ok');
6667
});
6768

6869
it('should modify body', async () => {
@@ -72,7 +73,7 @@ describe('Additional Params Request Plugin', () => {
7273
const result = await runner.transform(options);
7374

7475
expect(result.queryParams.test).toBeUndefined();
75-
expect(result.queryParams.defaultTest).toBe('defaultTest=ok');
76+
expect(result.queryParams.defaultTest).toBe('ok');
7677

7778
expect(additionalBody).toHaveBeenCalledWith(defaultBody);
7879
expect(result.body).toBe('newBody');
@@ -83,7 +84,7 @@ describe('Additional Params Request Sync Plugin', () => {
8384
const additionalGetParams = jest.fn().mockReturnValue({ test: 'ok' });
8485
const additionalBody = jest.fn().mockReturnValue('newBody');
8586

86-
const defaultGetParams = { defaultTest: 'defaultTest=ok' };
87+
const defaultGetParams = { defaultTest: 'ok' };
8788
const defaultBody = 'default';
8889
let options: RequestOptions;
8990

@@ -103,10 +104,11 @@ describe('Additional Params Request Sync Plugin', () => {
103104

104105
const result = runner.transform(options);
105106

106-
expect(result.queryParams.test).toBe('test=ok');
107+
expect(result.queryParams.test).toBe('ok');
107108
});
108109

109110
it('should add serialized query params', () => {
111+
options.enableParameterSerialization = true;
110112
const plugin = new AdditionalParamsSyncRequest({ queryParams:
111113
{
112114
primParam: { value: 'ok', explode: false, style: 'form' },
@@ -119,7 +121,7 @@ describe('Additional Params Request Sync Plugin', () => {
119121
const result = runner.transform(options);
120122

121123
expect(result.queryParams).toStrictEqual({
122-
defaultTest: 'defaultTest=ok',
124+
defaultTest: 'ok',
123125
primParam: 'primParam=ok',
124126
arrParam: 'arrParam=a%20b%20c',
125127
objParam: 'objParam%5Bprop1%5D=value1&objParam%5Bprop2%5D=value2'
@@ -133,7 +135,7 @@ describe('Additional Params Request Sync Plugin', () => {
133135
const result = runner.transform(options);
134136

135137
expect(additionalGetParams).toHaveBeenCalledWith(defaultGetParams);
136-
expect(result.queryParams.test).toBe('test=ok');
138+
expect(result.queryParams.test).toBe('ok');
137139
});
138140

139141
it('should modify body', () => {
@@ -143,7 +145,7 @@ describe('Additional Params Request Sync Plugin', () => {
143145
const result = runner.transform(options);
144146

145147
expect(result.queryParams.test).toBeUndefined();
146-
expect(result.queryParams.defaultTest).toBe('defaultTest=ok');
148+
expect(result.queryParams.defaultTest).toBe('ok');
147149

148150
expect(additionalBody).toHaveBeenCalledWith(defaultBody);
149151
expect(result.body).toBe('newBody');

packages/@ama-sdk/core/src/plugins/core/request-plugin.ts

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export interface RequestMetadata<C extends string = string, A extends string = s
4747
export interface RequestOptions extends RequestInit {
4848
/** Query Parameters */
4949
queryParams?: { [key: string]: string };
50+
/** Enable parameter serialization with exploded syntax */
51+
enableParameterSerialization?: boolean;
5052
/** Force body to string */
5153
body?: RequestBody;
5254
/** Force headers to Headers type */

packages/@ama-sdk/core/src/plugins/json-token/json-token.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('Json Token', () => {
1616
const tokenKey = 'testToken';
1717

1818
describe('request plugin', () => {
19-
const defaultGetParams = { defaultTest: 'defaultTest=ok' };
19+
const defaultGetParams = { defaultTest: 'ok' };
2020
const defaultBody = 'default';
2121
let options: RequestOptions;
2222

packages/@ama-sdk/core/src/plugins/si-token/si-token.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from './si-token.request';
77

88
describe('SI Token Request Plugin', () => {
9-
const defaultGetParams = { defaultTest: 'defaultTest=ok' };
9+
const defaultGetParams = { defaultTest: 'ok' };
1010
const defaultBody = 'default';
1111
let options: RequestOptions;
1212

@@ -26,7 +26,7 @@ describe('SI Token Request Plugin', () => {
2626

2727
const result = await runner.transform(options);
2828

29-
expect(result.queryParams.SITK).toBe('SITK=SIToken1');
30-
expect(result.queryParams.SITK2).toBe('SITK2=SIToken2');
29+
expect(result.queryParams.SITK).toBe('SIToken1');
30+
expect(result.queryParams.SITK2).toBe('SIToken2');
3131
});
3232
});

packages/@ama-sdk/core/src/plugins/url-rewrite/url-rewrite.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
describe('URL Rewrite Request Plugin', () => {
99
const urlRewriter = jest.fn().mockReturnValue('http://ok');
1010

11-
const defaultGetParams = { defaultTest: 'defaultTest=ok' };
11+
const defaultGetParams = { defaultTest: 'ok' };
1212
const defaultBody = 'default';
1313
const defaultUrl = 'http://test.com/truc';
1414
let options: RequestOptions;

packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/java/com/amadeus/codegen/ts/AbstractTypeScriptClientCodegen.java

+2
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ public AbstractTypeScriptClientCodegen() {
146146
additionalProperties.put("parseRegexp", new LambdaHelper.ParseRegexp());
147147
additionalProperties.put("plurialize", new LambdaHelper.Plurialize());
148148
additionalProperties.put("urlParamReplacer", new LambdaHelper.UrlParamReplacerLambda());
149+
additionalProperties.put("urlSerializedParamReplacer", new LambdaHelper.UrlSerializedParamReplacerLambda());
149150
additionalProperties.put("tokenizedUrlParamReplacer", new LambdaHelper.TokenizedUrlParamReplacerLambda());
151+
additionalProperties.put("tokenizedUrlSerializedParamReplacer", new LambdaHelper.TokenizedUrlSerializedParamReplacerLambda());
150152
additionalProperties.put("apiFolderName", new LambdaHelper.ApiFolderName());
151153
additionalProperties.put("removeBrackets", new LambdaHelper.RemoveText("[]"));
152154
additionalProperties.put("removeFowardslash", new LambdaHelper.RemoveText("/"));

packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/java/com/amadeus/codegen/ts/LambdaHelper.java

+35-2
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,29 @@ public String formatFragment(String fragment) {
5959
* Replaces placeholders in the URL such as
6060
* /carts/{cartId}/travelers
6161
* to
62-
* /carts/${serializedPathParams['cartId']}/travelers
62+
* /carts/${data['cartId']}/travelers
6363
*/
6464
public static class UrlParamReplacerLambda extends CustomLambda {
6565

6666
public UrlParamReplacerLambda() {}
6767

68+
@Override
69+
public String formatFragment(String fragment) {
70+
return fragment.replaceAll("\\{([\\w_-]+)\\}", "\\${data['$1']}");
71+
}
72+
73+
}
74+
75+
/**
76+
* Replaces placeholders in the URL such as
77+
* /carts/{cartId}/travelers
78+
* to
79+
* /carts/${serializedPathParams['cartId']}/travelers
80+
*/
81+
public static class UrlSerializedParamReplacerLambda extends CustomLambda {
82+
83+
public UrlSerializedParamReplacerLambda() {}
84+
6885
@Override
6986
public String formatFragment(String fragment) {
7087
return fragment.replaceAll("\\{([\\w_-]+)\\}", "\\${serializedPathParams['$1']}");
@@ -76,12 +93,28 @@ public String formatFragment(String fragment) {
7693
* Replaces placeholders in the URL such as
7794
* /carts/{cartId}/travelers
7895
* to
79-
* /carts/${this.piiParamTokens['$1'] || serializedPathParams['$1']}/travelers
96+
* /carts/${this.piiParamTokens['$1'] || data['$1']}/travelers
8097
*/
8198
public static class TokenizedUrlParamReplacerLambda extends CustomLambda {
8299

83100
public TokenizedUrlParamReplacerLambda() {}
84101

102+
@Override
103+
public String formatFragment(String fragment) {
104+
return fragment.replaceAll("\\{([\\w_-]+)\\}", "\\${this.piiParamTokens['$1'] || data['$1']}");
105+
}
106+
}
107+
108+
/**
109+
* Replaces placeholders in the URL such as
110+
* /carts/{cartId}/travelers
111+
* to
112+
* /carts/${this.piiParamTokens['$1'] || serializedPathParams['$1']}/travelers
113+
*/
114+
public static class TokenizedUrlSerializedParamReplacerLambda extends CustomLambda {
115+
116+
public TokenizedUrlSerializedParamReplacerLambda() {}
117+
85118
@Override
86119
public String formatFragment(String fragment) {
87120
return fragment.replaceAll("\\{([\\w_-]+)\\}", "\\${this.piiParamTokens['$1'] || serializedPathParams['$1']}");

packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/api.mustache

+16-3
Original file line numberDiff line numberDiff line change
@@ -108,25 +108,38 @@ export class {{classname}} implements Api {
108108
const pathParamsProperties = this.client.getPropertiesFromData(data, [{{#trimComma}}{{#pathParams}}'{{baseName}}', {{/pathParams}}{{/trimComma}}]);
109109
const pathParamSerialization = { {{#trimComma}}{{#pathParams}}{{baseName}}: { explode: {{isExplode}}, style: '{{style}}' }, {{/pathParams}}{{/trimComma}} }
110110
const serializedPathParams = this.client.serializePathParams(pathParamsProperties, pathParamSerialization);
111+
const basePath = this.client.options.enableParameterSerialization
112+
? `${this.client.options.basePath}{{#urlSerializedParamReplacer}}{{path}}{{/urlSerializedParamReplacer}}`
113+
: `${this.client.options.basePath}{{#urlParamReplacer}}{{path}}{{/urlParamReplacer}}`;
114+
const tokenizedUrl = this.client.options.enableParameterSerialization
115+
? `${this.client.options.basePath}{{#tokenizedUrlSerializedParamReplacer}}{{path}}{{/tokenizedUrlSerializedParamReplacer}}`
116+
: `${this.client.options.basePath}{{#tokenizedUrlParamReplacer}}{{path}}{{/tokenizedUrlParamReplacer}}`;
111117
{{/hasPathParams}}
118+
{{^hasPathParams}}
112119
const basePath = `${this.client.options.basePath}{{#urlParamReplacer}}{{path}}{{/urlParamReplacer}}`;
113120
const tokenizedUrl = `${this.client.options.basePath}{{#tokenizedUrlParamReplacer}}{{path}}{{/tokenizedUrlParamReplacer}}`;
121+
{{/hasPathParams}}
114122
const tokenizedOptions = this.client.tokenizeRequestOptions(tokenizedUrl, queryParams, this.piiParamTokens, data);
115123

116124
const requestOptions = {
117125
headers,
118126
method: '{{httpMethod}}',
119127
basePath,
120-
queryParams{{#hasQueryParams}}: serializedQueryParams,
121-
queryParamSerialization{{/hasQueryParams}},
128+
{{#hasQueryParams}}
129+
...(this.client.options.enableParameterSerialization ? { queryParams: serializedQueryParams, queryParamSerialization } : { queryParams }),
130+
{{/hasQueryParams}}
131+
{{^hasQueryParams}}
132+
queryParams,
133+
{{/hasQueryParams}}
134+
enableParameterSerialization: this.client.options.enableParameterSerialization,
122135
body: body || undefined,
123136
metadata,
124137
tokenizedOptions,
125138
api: this
126139
};
127140

128141
const options = await this.client.getRequestOptions(requestOptions);
129-
const url = this.client.prepareUrlWithQueryParams(options.basePath, options.queryParams);
142+
const url = this.client.options.enableParameterSerialization ? this.client.prepareUrlWithQueryParams(options.basePath, options.queryParams) : this.client.prepareUrl(options.basePath, options.queryParams);
130143

131144
const ret = this.client.processCall<{{#vendorExtensions}}{{#responses2xxReturnTypes}}{{.}}{{^-last}} | {{/-last}}{{/responses2xxReturnTypes}}{{^responses2xxReturnTypes}}never{{/responses2xxReturnTypes}}{{/vendorExtensions}}>(url, options, {{#tags.0.extensions.x-api-type}}ApiTypes.{{tags.0.extensions.x-api-type}}{{/tags.0.extensions.x-api-type}}{{^tags.0.extensions.x-api-type}}ApiTypes.DEFAULT{{/tags.0.extensions.x-api-type}}, {{classname}}.apiName,{{#keepRevivers}}{{#vendorExtensions}}{{#responses2xx}}{{#-first}} { {{/-first}}{{code}}: {{^primitiveType}}revive{{baseType}}{{/primitiveType}}{{#primitiveType}}undefined{{/primitiveType}}{{^-last}}, {{/-last}}{{#-last}} } {{/-last}}{{/responses2xx}}{{^responses2xx}} undefined{{/responses2xx}}{{/vendorExtensions}}{{/keepRevivers}}{{^keepRevivers}} undefined{{/keepRevivers}}, '{{nickname}}');
132145
return ret;

0 commit comments

Comments
 (0)