Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support exploded syntax in sdk #2896

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sdo-1A
Copy link
Contributor

@sdo-1A sdo-1A commented Feb 24, 2025

Proposed change

Support of exploded syntax for query and path parameters: https://swagger.io/docs/specification/v3_0/serialization/
This is an alternate solution to the existing PR #2865 to avoid breaking changes.

Related issues

#640

- No issue associated -

Copy link

nx-cloud bot commented Feb 24, 2025

View your CI Pipeline Execution ↗ for commit a51e001.

Command Status Duration Result
nx run-many --target=test-int ✅ Succeeded 8m 5s View ↗
nx run-many --target=build --projects=eslint-pl... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2025-02-27 08:47:09 UTC

Copy link

codecov bot commented Feb 24, 2025

Codecov Report

Attention: Patch coverage is 81.57895% with 14 lines in your changes missing coverage. Please review.

Project coverage is 69.72%. Comparing base (d2e9a76) to head (a51e001).
Report is 13 commits behind head on main.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ges/@ama-sdk/core/src/clients/api-beacon-client.ts 0.00% 6 Missing ⚠️
...ages/@ama-sdk/core/src/clients/api-fetch-client.ts 0.00% 6 Missing ⚠️
packages/@ama-sdk/core/src/fwk/api.helpers.ts 96.87% 2 Missing ⚠️
Additional details and impacted files

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@sdo-1A sdo-1A marked this pull request as ready for review February 25, 2025 08:59
@sdo-1A sdo-1A requested a review from a team as a code owner February 25, 2025 08:59
fpaul-1A
fpaul-1A previously approved these changes Feb 26, 2025
}

/** @inheritdoc */
public prepareUrl(url: string, data: { [key: string]: string | undefined }): string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public prepareUrl(url: string, data: { [key: string]: string | undefined }): string;
public prepareUrl(url: string, data?: { [key: string]: string | undefined }): string;

if (queryParamSerialization) {
return prepareUrl(url, data as T, queryParamSerialization);
}
return prepareUrl(url, data as { [key: string]: string | undefined });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return prepareUrl(url, data as { [key: string]: string | undefined });
return prepareUrl(url, (data || {}) as { [key: string]: string | undefined });

/** @inheritdoc */
public prepareUrl<T extends { [key: string]: any }>(url: string, data?: { [key: string]: string | undefined } | T, queryParamSerialization?: { [K in keyof T]?: ParamSerialization }): string {
if (queryParamSerialization) {
return prepareUrl(url, data as T, queryParamSerialization);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could import it with another name to avoid confusion? (prepareUrl as ....)

}

/** @inheritdoc */
public prepareUrl(url: string, data: { [key: string]: string | undefined }): string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for api-angular clients

@@ -92,8 +94,20 @@ export class ApiFetchClient implements ApiClient {
}

/** @inheritdoc */
public prepareUrl(url: string, queryParameters: { [key: string]: string | undefined } = {}) {
return prepareUrl(url, queryParameters);
public serializePathParams<T extends { [key: string]: any }>(data: T, pathParameters: { [K in keyof T]?: ParamSerialization }): { [p in keyof T]: string } {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as for api angular client

}

/** @inheritdoc */
public prepareUrl(url: string, data: { [key: string]: string | undefined }): string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as the other client angular

public prepareUrl(url: string, queryParameters?: { [key: string]: string }): string {
return prepareUrl(url, queryParameters);
public serializePathParams<T extends { [key: string]: any }>(data: T, pathParameters: { [K in keyof T]?: ParamSerialization }): { [p in keyof T]: string } {
return serializePathParams(data, pathParameters);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as the client angular

@@ -115,8 +117,20 @@ export class ApiFetchClient implements ApiClient {
}

/** @inheritdoc */
public prepareUrl(url: string, queryParameters: { [key: string]: string | undefined } = {}) {
return prepareUrl(url, queryParameters);
public serializePathParams<T extends { [key: string]: any }>(data: T, pathParameters: { [K in keyof T]?: ParamSerialization }): { [p in keyof T]: string } {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as the client angular

const queryParamExplode = paramSerialization.explode;
const queryParamStyle = paramSerialization.style;
const value = (() => {
if (Array.isArray(queryParamValue)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB but I would have extracted the logic until line in another function for more readability

* prepares the url to be called
* @param url base url to be used
* @param queryParameters key value pair with the parameters. If the value is undefined, the key is dropped
* Serialize query parameters based on the values of exploded and style
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be nice to have the reference to the parameter serialization

const value = (() => {
if (Array.isArray(queryParamValue)) {
if (queryParamExplode) {
switch (queryParamStyle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why a switch case if it is the same logic for everyone?

const pathParamValue = data[pathParamName];
if (typeof pathParamValue !== 'undefined' && pathParamValue !== null && !!pathParamSerialization) {
const value = (() => {
if (Array.isArray(pathParamValue)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same remark for the extraction of the code

* @param data Query parameters key value pair. If the value is undefined, the key is dropped.
* @deprecated use `prepareUrl` with query parameter serialization, will be removed in v14.
*/
export function prepareUrl(url: string, data: { [key: string]: string | undefined }): string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export function prepareUrl(url: string, data: { [key: string]: string | undefined }): string;
export function prepareUrl(url: string, data?: { [key: string]: string | undefined }): string;


const queryPart = Object.keys(data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const queryPart = Object.keys(data)
const queryPart = Object.keys(data || {})

*/
public static class TokenizedUrlParamReplacerLambda extends CustomLambda {

public TokenizedUrlParamReplacerLambda() {}

@Override
public String formatFragment(String fragment) {
return fragment.replaceAll("\\{([\\w_-]+)\\}", "\\${this.piiParamTokens['$1'] || data['$1']}");
return fragment.replaceAll("\\{([\\w_-]+)\\}", "\\${this.piiParamTokens['$1'] || pathParams['$1']}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to change that?

} else if (typeof queryParamValue === 'object') {
if (queryParamStyle === 'form') {
return queryParamExplode
? Object.keys(queryParamValue).map((prop) => `${prop}=${queryParamValue[prop]}`).join('&')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
? Object.keys(queryParamValue).map((prop) => `${prop}=${queryParamValue[prop]}`).join('&')
? Object.entries(queryParamValue).map(([propName, propValue]) => `${propName}=${propValue}`).join('&')

if (queryParamStyle === 'form') {
return queryParamExplode
? Object.keys(queryParamValue).map((prop) => `${prop}=${queryParamValue[prop]}`).join('&')
: `${name}=` + Object.keys(queryParamValue).map((prop) => `${prop},${queryParamValue[prop]}`).join(',');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: `${name}=` + Object.keys(queryParamValue).map((prop) => `${prop},${queryParamValue[prop]}`).join(',');
: `${name}=` + Object.entries(queryParamValue).map(([propName, propValue]) => `${propName},${propValue}`).join(',');

? Object.keys(queryParamValue).map((prop) => `${prop}=${queryParamValue[prop]}`).join('&')
: `${name}=` + Object.keys(queryParamValue).map((prop) => `${prop},${queryParamValue[prop]}`).join(',');
} else {
return Object.keys(queryParamValue).map((prop) => `${name}[${prop}]=${queryParamValue[prop]}`).join('&');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Object.keys(queryParamValue).map((prop) => `${name}[${prop}]=${queryParamValue[prop]}`).join('&');
return Object.entries(queryParamValue).map(([propName, propValue]) => `${name}[${propName}]=${propValue}`).join('&');

if (queryParamSerialization) {
if (Object.keys(queryParamSerialization).length > 0) {
const serializedQueryParams = serializeQueryParams(data as T, queryParamSerialization);
return url + paramsPrefix + Object.values(serializedQueryParams).join('&');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your data is empty (because you don't want to pass parameters) but you have optional parameters in your queryParamSerialization, you will have an extra ?
for example : 'test.com?' instead of 'test.com'

switch (pathParamSerialization.style) {
case 'simple': {
return pathParamSerialization.explode
? Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join(',')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
? Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join(',')
? Object.entries(pathParamValue).map(([propName, propValue]) => `${propName}=${propValue}`).join(',')

case 'simple': {
return pathParamSerialization.explode
? Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join(',')
: Object.keys(pathParamValue).map((property) => `${property},${pathParamValue[property]}`).join(',');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: Object.keys(pathParamValue).map((property) => `${property},${pathParamValue[property]}`).join(',');
: Object.entries(pathParamValue).map(([propName, propValue]) => `${propName},${propValue}`).join(',');

}
case 'label': {
return pathParamSerialization.explode
? '.' + Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join('.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
? '.' + Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join('.')
? '.' + Object.entries(pathParamValue).map(([propName, propValue]) => `${propName}=${propValue}`).join('.')

case 'label': {
return pathParamSerialization.explode
? '.' + Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join('.')
: '.' + Object.keys(pathParamValue).map((property) => `${property},${pathParamValue[property]}`).join(',');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: '.' + Object.keys(pathParamValue).map((property) => `${property},${pathParamValue[property]}`).join(',');
: '.' + Object.entries(pathParamValue).map(([propName, propValue]) => `${propName},${propValue}`).join(',');

}
case 'matrix': {
return pathParamSerialization.explode
? ';' + Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join(';')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
? ';' + Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join(';')
? ';' + Object.entries(pathParamValue).map(([propName, propValue]) => `${propName}=${propValue}`).join(';')

case 'matrix': {
return pathParamSerialization.explode
? ';' + Object.keys(pathParamValue).map((property) => `${property}=${pathParamValue[property]}`).join(';')
: `;${pathParamName}=` + Object.keys(pathParamValue).map((property) => `${property},${pathParamValue[property]}`).join(',');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: `;${pathParamName}=` + Object.keys(pathParamValue).map((property) => `${property},${pathParamValue[property]}`).join(',');
: `;${pathParamName}=` + Object.entries(pathParamValue).map(([propName, propValue]) => `${propName},${propValue}`).join(',');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants