From 37e3928b43990108e1640590c2b5d94d214abde7 Mon Sep 17 00:00:00 2001
From: Kilian Panot <kilian.panot@amadeus.com>
Date: Mon, 1 Apr 2024 17:07:53 +0900
Subject: [PATCH] feat(sdk): provide a way to request a custom URL feat(sdk):
 add apiName in the request plugin context

---
 .../core/src/clients/api-angular-client.ts    | 43 +++++++-----
 .../core/src/clients/api-beacon-client.ts     | 43 +++++++-----
 .../core/src/clients/api-fetch-client.ts      | 41 +++++++-----
 packages/@ama-sdk/core/src/fwk/api.helpers.ts |  4 +-
 .../@ama-sdk/core/src/fwk/core/api-client.ts  | 35 +++++++++-
 .../@ama-sdk/core/src/plugins/core/plugin.ts  |  7 +-
 .../core/src/plugins/core/reply-plugin.ts     |  3 -
 .../@ama-sdk/core/src/utils/generic-api.ts    | 66 +++++++++++++++++++
 packages/@ama-sdk/core/src/utils/index.ts     |  1 +
 .../typescriptFetch/api/api.mustache          | 22 +++++--
 .../api/fixture.jasmine.mustache              |  4 +-
 .../typescriptFetch/api/fixture.jest.mustache |  4 +-
 .../typescriptFetch/api/fixture.mustache      |  4 +-
 .../typescriptFetch/model/enums.mustache      |  2 +-
 14 files changed, 213 insertions(+), 66 deletions(-)
 create mode 100644 packages/@ama-sdk/core/src/utils/generic-api.ts

diff --git a/packages/@ama-sdk/core/src/clients/api-angular-client.ts b/packages/@ama-sdk/core/src/clients/api-angular-client.ts
index 4840b2e22c..67d21a10d6 100644
--- a/packages/@ama-sdk/core/src/clients/api-angular-client.ts
+++ b/packages/@ama-sdk/core/src/clients/api-angular-client.ts
@@ -4,8 +4,8 @@ import { ExceptionReply } from '../plugins/exception';
 import { ReviverReply } from '../plugins/reviver';
 import { ApiTypes } from '../fwk/api';
 import { extractQueryParams, filterUndefinedValues, getResponseReviver, prepareUrl, processFormData, tokenizeRequestOptions } from '../fwk/api.helpers';
-import type { PartialExcept } from '../fwk/api.interface';
-import { ApiClient } from '../fwk/core/api-client';
+import type { Api, PartialExcept } from '../fwk/api.interface';
+import type { ApiClient,RequestOptionsParameters } from '../fwk/core/api-client';
 import { BaseApiClientOptions } from '../fwk/core/base-api-constructor';
 import { EmptyResponseError } from '../fwk/errors';
 import { ReviverType } from '../fwk/Reviver';
@@ -48,28 +48,41 @@ export class ApiAngularClient implements ApiClient {
   public extractQueryParams<T extends { [key: string]: any }>(data: T, names: (keyof T)[]): { [p in keyof T]: string; } {
     return extractQueryParams(data, names);
   }
-  public async prepareOptions(url: string, method: string, queryParams: { [key: string]: string | undefined }, headers: { [key: string]: string | undefined }, body?: RequestBody | undefined,
-    tokenizedOptions?: TokenizedOptions | undefined, metadata?: RequestMetadata<string, string> | undefined): Promise<RequestOptions> {
-    const options: RequestOptions = {
-      method,
-      headers: new Headers(filterUndefinedValues(headers)),
-      body,
-      queryParams: filterUndefinedValues(queryParams),
-      basePath: url,
-      tokenizedOptions,
-      metadata
-    };
 
-    let opts = options;
+  /** @inheritdoc */
+  public async getRequestOptions(requestOptionsParameters: RequestOptionsParameters): Promise<RequestOptions> {
+    let opts: RequestOptions = {
+      ...requestOptionsParameters,
+      headers: new Headers(filterUndefinedValues(requestOptionsParameters.headers)),
+      queryParams: filterUndefinedValues(requestOptionsParameters.queryParams)
+    };
     if (this.options.requestPlugins) {
       for (const plugin of this.options.requestPlugins) {
-        opts = await plugin.load({logger: this.options.logger}).transform(opts);
+        opts = await plugin.load({
+          logger: this.options.logger,
+          apiName: requestOptionsParameters.api?.apiName
+        }).transform(opts);
       }
     }
 
     return opts;
   }
 
+  /** @inheritdoc */
+  public async prepareOptions(url: string, method: string, queryParams: { [key: string]: string | undefined }, headers: { [key: string]: string | undefined }, body?: RequestBody,
+    tokenizedOptions?: TokenizedOptions, metadata?: RequestMetadata, api?: Api) {
+    return this.getRequestOptions({
+      headers,
+      method,
+      basePath: url,
+      queryParams,
+      body,
+      metadata,
+      tokenizedOptions,
+      api
+    });
+  }
+
   /** @inheritdoc */
   public prepareUrl(url: string, queryParameters: { [key: string]: string | undefined } = {}) {
     return prepareUrl(url, queryParameters);
diff --git a/packages/@ama-sdk/core/src/clients/api-beacon-client.ts b/packages/@ama-sdk/core/src/clients/api-beacon-client.ts
index 61223358bc..a038d0a933 100644
--- a/packages/@ama-sdk/core/src/clients/api-beacon-client.ts
+++ b/packages/@ama-sdk/core/src/clients/api-beacon-client.ts
@@ -1,8 +1,8 @@
 import type { RequestBody, RequestMetadata, RequestOptions, TokenizedOptions } from '../plugins';
 import type { ApiTypes } from '../fwk/api';
 import { extractQueryParams, filterUndefinedValues, prepareUrl, processFormData, tokenizeRequestOptions } from '../fwk/api.helpers';
-import type { PartialExcept } from '../fwk/api.interface';
-import type { ApiClient } from '../fwk/core/api-client';
+import type { Api, PartialExcept } from '../fwk/api.interface';
+import type { ApiClient, RequestOptionsParameters } from '../fwk/core/api-client';
 import type { BaseApiClientOptions } from '../fwk/core/base-api-constructor';
 
 /** @see BaseApiClientOptions */
@@ -25,6 +25,7 @@ const DEFAULT_OPTIONS: Omit<BaseApiBeaconClientOptions, 'basePath'> = {
  * Determine if the given value is a promise
  * @param value The value to test
  */
+// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 const isPromise = <T>(value: T | Promise<T>): value is Promise<T> => value && typeof (value as any).then === 'function';
 
 /**
@@ -58,27 +59,20 @@ export class ApiBeaconClient implements ApiClient {
   }
 
   /** @inheritdoc */
-  public prepareOptions(url: string, method: string, queryParams: { [key: string]: string }, headers: { [key: string]: string }, body?: RequestBody,
-    tokenizedOptions?: TokenizedOptions, metadata?: RequestMetadata): Promise<RequestOptions> {
+  public getRequestOptions(options: RequestOptionsParameters): Promise<RequestOptions> {
 
-    if (method.toUpperCase() !== 'POST') {
-      throw new Error(`Unsupported method: ${method}. The beacon API only supports POST.`);
+    if (options.method.toUpperCase() !== 'POST') {
+      throw new Error(`Unsupported method: ${options.method}. The beacon API only supports POST.`);
     }
 
-    const options: RequestOptions = {
-      method,
-      headers: new Headers(filterUndefinedValues(headers)),
-      body,
-      queryParams: filterUndefinedValues(queryParams),
-      basePath: url,
-      tokenizedOptions,
-      metadata
+    let opts: RequestOptions = {
+      ...options,
+      headers: new Headers(filterUndefinedValues(options.headers)),
+      queryParams: filterUndefinedValues(options.queryParams)
     };
-
-    let opts = options;
     if (this.options.requestPlugins) {
       for (const plugin of this.options.requestPlugins) {
-        const changedOpt = plugin.load({logger: this.options.logger}).transform(opts);
+        const changedOpt = plugin.load({ logger: this.options.logger, apiName: options.api?.apiName }).transform(opts);
         if (isPromise(changedOpt)) {
           throw new Error(`Request plugin ${plugin.constructor.name} has async transform method. Only sync methods are supported with the Beacon client.`);
         } else {
@@ -90,6 +84,21 @@ export class ApiBeaconClient implements ApiClient {
     return Promise.resolve(opts);
   }
 
+  /** @inheritdoc */
+  public prepareOptions(url: string, method: string, queryParams: { [key: string]: string | undefined }, headers: { [key: string]: string | undefined }, body?: RequestBody,
+    tokenizedOptions?: TokenizedOptions, metadata?: RequestMetadata, api?: Api) {
+    return this.getRequestOptions({
+      headers,
+      method,
+      basePath: url,
+      queryParams,
+      body,
+      metadata,
+      tokenizedOptions,
+      api
+    });
+  }
+
   /** @inheritdoc */
   public prepareUrl(url: string, queryParameters?: { [key: string]: string }): string {
     return prepareUrl(url, queryParameters);
diff --git a/packages/@ama-sdk/core/src/clients/api-fetch-client.ts b/packages/@ama-sdk/core/src/clients/api-fetch-client.ts
index 2d7df1636c..77439d47c2 100644
--- a/packages/@ama-sdk/core/src/clients/api-fetch-client.ts
+++ b/packages/@ama-sdk/core/src/clients/api-fetch-client.ts
@@ -12,8 +12,8 @@ import {ExceptionReply} from '../plugins/exception';
 import {ReviverReply} from '../plugins/reviver';
 import {ApiTypes} from '../fwk/api';
 import {extractQueryParams, filterUndefinedValues, getResponseReviver, prepareUrl, processFormData, tokenizeRequestOptions} from '../fwk/api.helpers';
-import type {PartialExcept} from '../fwk/api.interface';
-import {ApiClient} from '../fwk/core/api-client';
+import type {Api, PartialExcept} from '../fwk/api.interface';
+import type {ApiClient, RequestOptionsParameters} from '../fwk/core/api-client';
 import {BaseApiClientOptions} from '../fwk/core/base-api-constructor';
 import {CanceledCallError, EmptyResponseError, ResponseJSONParseError} from '../fwk/errors';
 import {ReviverType} from '../fwk/Reviver';
@@ -64,28 +64,39 @@ export class ApiFetchClient implements ApiClient {
   }
 
   /** @inheritdoc */
-  public async prepareOptions(url: string, method: string, queryParams: { [key: string]: string | undefined }, headers: { [key: string]: string | undefined }, body?: RequestBody,
-    tokenizedOptions?: TokenizedOptions, metadata?: RequestMetadata) {
-    const options: RequestOptions = {
-      method,
-      headers: new Headers(filterUndefinedValues(headers)),
-      body,
-      queryParams: filterUndefinedValues(queryParams),
-      basePath: url,
-      tokenizedOptions,
-      metadata
+  public async getRequestOptions(requestOptionsParameters: RequestOptionsParameters): Promise<RequestOptions> {
+    let opts: RequestOptions = {
+      ...requestOptionsParameters,
+      headers: new Headers(filterUndefinedValues(requestOptionsParameters.headers)),
+      queryParams: filterUndefinedValues(requestOptionsParameters.queryParams)
     };
-
-    let opts = options;
     if (this.options.requestPlugins) {
       for (const plugin of this.options.requestPlugins) {
-        opts = await plugin.load({logger: this.options.logger}).transform(opts);
+        opts = await plugin.load({
+          logger: this.options.logger,
+          apiName: requestOptionsParameters.api?.apiName
+        }).transform(opts);
       }
     }
 
     return opts;
   }
 
+  /** @inheritdoc */
+  public async prepareOptions(url: string, method: string, queryParams: { [key: string]: string | undefined }, headers: { [key: string]: string | undefined }, body?: RequestBody,
+    tokenizedOptions?: TokenizedOptions, metadata?: RequestMetadata, api?: Api) {
+    return this.getRequestOptions({
+      headers,
+      method,
+      basePath: url,
+      queryParams,
+      body,
+      metadata,
+      tokenizedOptions,
+      api
+    });
+  }
+
   /** @inheritdoc */
   public prepareUrl(url: string, queryParameters: { [key: string]: string | undefined } = {}) {
     return prepareUrl(url, queryParameters);
diff --git a/packages/@ama-sdk/core/src/fwk/api.helpers.ts b/packages/@ama-sdk/core/src/fwk/api.helpers.ts
index 56c2326dc8..9b45f9276f 100644
--- a/packages/@ama-sdk/core/src/fwk/api.helpers.ts
+++ b/packages/@ama-sdk/core/src/fwk/api.helpers.ts
@@ -37,8 +37,8 @@ export function extractQueryParams<T extends { [key: string]: any }>(data: T, na
  * @param object JSON object to filter
  * @returns an object without undefined values
  */
-export function filterUndefinedValues(object: { [key: string]: string | undefined }): { [key: string]: string } {
-  return Object.keys(object)
+export function filterUndefinedValues(object?: { [key: string]: string | undefined }): { [key: string]: string } {
+  return !object ? {} : Object.keys(object)
     .filter((objectKey) => typeof object[objectKey] !== 'undefined')
     .reduce<{ [key: string]: string }>((acc, objectKey) => {
       acc[objectKey] = object[objectKey] as string;
diff --git a/packages/@ama-sdk/core/src/fwk/core/api-client.ts b/packages/@ama-sdk/core/src/fwk/core/api-client.ts
index 8acffd592e..66c48a6605 100644
--- a/packages/@ama-sdk/core/src/fwk/core/api-client.ts
+++ b/packages/@ama-sdk/core/src/fwk/core/api-client.ts
@@ -1,8 +1,32 @@
 import {RequestBody, RequestMetadata, RequestOptions, TokenizedOptions} from '../../plugins/index';
 import {ApiTypes} from '../api';
+import type { Api } from '../api.interface';
 import {ReviverType} from '../Reviver';
 import {BaseApiClientOptions} from './base-api-constructor';
 
+/** Parameters to the request the call options */
+export interface RequestOptionsParameters {
+  /** URL of the call to process (without the query parameters) */
+  basePath: string;
+  /** Query Parameters */
+  queryParams?: { [key: string]: string | undefined };
+  /** Force body to string */
+  body?: RequestBody;
+  /** Force headers to Headers type */
+  headers: { [key: string]: string | undefined };
+  /** Tokenized options to replace URL and query parameters */
+  tokenizedOptions?: TokenizedOptions;
+  /** Request metadata */
+  metadata?: RequestMetadata;
+  /** HTTP Method used for the request */
+  method: NonNullable<RequestInit['method']>;
+  /**
+   * API initializing the call
+   * @todo this field will be turned as mandatory in v11
+   */
+  api?: Api;
+}
+
 /**
  * API Client used by the SDK's APIs to call the server
  */
@@ -18,10 +42,19 @@ export interface ApiClient {
    */
   extractQueryParams<T extends { [key: string]: any }>(data: T, names: (keyof T)[]): { [p in keyof T]: string; };
 
-  /** Prepare Options */
+  /**
+   * Prepare Options
+   * @deprecated use getRequestOptions instead, will be removed in v11
+   */
   prepareOptions(url: string, method: string, queryParams: { [key: string]: string | undefined }, headers: { [key: string]: string | undefined }, body?: RequestBody,
     tokenizedOptions?: TokenizedOptions, metadata?: RequestMetadata): Promise<RequestOptions>;
 
+  /**
+   * Retrieve the option to process the HTTP Call
+   * @todo turn this function mandatory when `prepareOptions` will be removed
+   */
+  getRequestOptions?(requestOptionsParameters: RequestOptionsParameters): Promise<RequestOptions>;
+
   /**
    * prepares the url to be called
    * @param url base url to be used
diff --git a/packages/@ama-sdk/core/src/plugins/core/plugin.ts b/packages/@ama-sdk/core/src/plugins/core/plugin.ts
index 2235dc8a3b..1a7f997f0a 100644
--- a/packages/@ama-sdk/core/src/plugins/core/plugin.ts
+++ b/packages/@ama-sdk/core/src/plugins/core/plugin.ts
@@ -30,8 +30,13 @@ export interface PluginSyncRunner<T, V> {
 export interface PluginContext {
   /** Plugin context properties */
   [key: string]: any;
-  /** Logger (optional, fallback to console logger if undefined) */
+  /**
+   * Logger
+   * (optional, fallback to console logger if undefined)
+   */
   logger?: Logger;
+  /** Name of the API */
+  apiName?: string;
 }
 
 /**
diff --git a/packages/@ama-sdk/core/src/plugins/core/reply-plugin.ts b/packages/@ama-sdk/core/src/plugins/core/reply-plugin.ts
index 60eee3e43a..3709359161 100644
--- a/packages/@ama-sdk/core/src/plugins/core/reply-plugin.ts
+++ b/packages/@ama-sdk/core/src/plugins/core/reply-plugin.ts
@@ -19,9 +19,6 @@ export interface ReplyPluginContext<T> extends PluginContext {
   /** Type of the API */
   apiType: ApiTypes | string;
 
-  /** Name of the API */
-  apiName?: string;
-
   /** Exception thrown during call/parse of the response */
   exception?: Error;
 
diff --git a/packages/@ama-sdk/core/src/utils/generic-api.ts b/packages/@ama-sdk/core/src/utils/generic-api.ts
new file mode 100644
index 0000000000..643d8c3153
--- /dev/null
+++ b/packages/@ama-sdk/core/src/utils/generic-api.ts
@@ -0,0 +1,66 @@
+import { type Api, type ApiClient, ApiTypes, type RequestOptionsParameters, type ReviverType } from '../fwk';
+
+/**
+ * Generic request to the API
+ */
+export interface GenericRequestOptions<T> extends Omit<RequestOptionsParameters, 'api' | 'headers'> {
+  /** API used to identify the call */
+  api?: RequestOptionsParameters['api'];
+  /** Custom headers to provide to the request */
+  headers?: RequestOptionsParameters['headers'];
+  /** Custom operation ID to identify the request */
+  operationId?: string;
+  /** Custom reviver to revive the response of the call */
+  revivers?: ReviverType<T> | undefined | { [key: number]: ReviverType<T> | undefined };
+}
+
+/**
+ * Generic request to the API
+ */
+export class GenericApi implements Api {
+  /** API name */
+  public static readonly apiName = 'GenericApi';
+
+  /** @inheritDoc */
+  public readonly apiName = GenericApi.apiName;
+
+
+  /** @inheritDoc */
+  public client: ApiClient;
+
+  /**
+   * Initialize your interface
+   * @param apiClient
+   * @params apiClient Client used to process call to the API
+   */
+  constructor(apiClient: ApiClient) {
+    this.client = apiClient;
+  }
+
+  /**
+   * Process to request to the API in the context of the SDK
+   * @param requestOptions Option to provide to process to the call
+   */
+  public async request<T>(requestOptions: GenericRequestOptions<T>): Promise<T> {
+    const metadataHeaderAccept = requestOptions.metadata?.headerAccept || 'application/json';
+    const headers: { [key: string]: string | undefined } = {
+      // eslint-disable-next-line @typescript-eslint/naming-convention
+      'Content-Type': requestOptions.metadata?.headerContentType || 'application/json',
+      // eslint-disable-next-line @typescript-eslint/naming-convention
+      ...(metadataHeaderAccept ? { 'Accept': metadataHeaderAccept } : {})
+    };
+
+    const requestParameters: RequestOptionsParameters = {
+      api: this,
+      headers,
+      ...requestOptions
+    };
+    const options = this.client.getRequestOptions ?
+      await this.client.getRequestOptions(requestParameters) :
+      await this.client.prepareOptions(requestParameters.basePath, requestParameters.method, requestParameters.queryParams || {}, requestParameters.headers);
+    const url = this.client.prepareUrl(options.basePath, options.queryParams);
+
+    const ret = this.client.processCall<T>(url, options, ApiTypes.DEFAULT, requestOptions.api!.apiName, requestOptions.revivers, requestOptions.operationId);
+    return ret;
+  }
+}
diff --git a/packages/@ama-sdk/core/src/utils/index.ts b/packages/@ama-sdk/core/src/utils/index.ts
index 02bf120a81..7a0a0420c8 100644
--- a/packages/@ama-sdk/core/src/utils/index.ts
+++ b/packages/@ama-sdk/core/src/utils/index.ts
@@ -3,3 +3,4 @@ export * from './encoder';
 export * from './ie11';
 export * from './json-token';
 export * from './mime-types';
+export * from './generic-api';
diff --git a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/api.mustache b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/api.mustache
index 521d34b466..872acb5524 100644
--- a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/api.mustache
+++ b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/api.mustache
@@ -52,7 +52,7 @@ export class {{classname}} implements Api {
   /**
    * Initialize your interface
    *
-   * @params apiClient Client used to process call to the API
+   * @param apiClient Client used to process call to the API
    */
   constructor(apiClient: ApiClient) {
     this.client = apiClient;
@@ -66,6 +66,7 @@ export class {{classname}} implements Api {
    * @deprecated
   {{/isDeprecated}}
    * @param data Data to provide to the API call
+   * @param metadata Metadata to pass to the API call
    */
   public async {{nickname}}(data: {{#uppercaseFirst}}{{nickname}}{{/uppercaseFirst}}RequestData, metadata?: RequestMetadata<{{#consumes}}'{{mediaType}}'{{^-last}} | {{/-last}}{{/consumes}}{{^consumes}}string{{/consumes}}, {{#produces}}'{{mediaType}}'{{^-last}} | {{/-last}}{{/produces}}{{^produces}}string{{/produces}}>): Promise<{{#vendorExtensions}}{{#responses2xxReturnTypes}}{{.}}{{^-last}} | {{/-last}}{{/responses2xxReturnTypes}}{{^responses2xxReturnTypes}}never{{/responses2xxReturnTypes}}{{/vendorExtensions}}> {
     {{#allParams}}
@@ -73,7 +74,7 @@ export class {{classname}} implements Api {
         data['{{baseName}}'] = data['{{baseName}}'] !== undefined ? data['{{baseName}}'] : {{#isString}}'{{/isString}}{{defaultValue}}{{#isString}}'{{/isString}};
       {{/defaultValue}}
     {{/allParams}}
-    const getParams = this.client.extractQueryParams<{{#uppercaseFirst}}{{nickname}}{{/uppercaseFirst}}RequestData>(data, [{{#trimComma}}{{#queryParams}}'{{baseName}}', {{/queryParams}}{{/trimComma}}]{{^queryParams}} as never[]{{/queryParams}});
+    const queryParams = this.client.extractQueryParams<{{#uppercaseFirst}}{{nickname}}{{/uppercaseFirst}}RequestData>(data, [{{#trimComma}}{{#queryParams}}'{{baseName}}', {{/queryParams}}{{/trimComma}}]{{^queryParams}} as never[]{{/queryParams}});
     const metadataHeaderAccept = metadata?.headerAccept || '{{#headerJsonMimeType}}{{#produces}}{{mediaType}}{{^-last}}, {{/-last}}{{/produces}}{{/headerJsonMimeType}}';
     const headers: { [key: string]: string | undefined } = { {{#trimComma}}
       'Content-Type': metadata?.headerContentType || '{{#headerJsonMimeType}}{{#consumes}}{{mediaType}}{{^-last}}, {{/-last}}{{/consumes}}{{/headerJsonMimeType}}',
@@ -100,11 +101,22 @@ export class {{classname}} implements Api {
       body = data['{{baseName}}'] as any;
     }
     {{/bodyParam}}
-    const basePathUrl = `${this.client.options.basePath}{{#urlParamReplacer}}{{path}}{{/urlParamReplacer}}`;
+    const basePath = `${this.client.options.basePath}{{#urlParamReplacer}}{{path}}{{/urlParamReplacer}}`;
     const tokenizedUrl = `${this.client.options.basePath}{{#tokenizedUrlParamReplacer}}{{path}}{{/tokenizedUrlParamReplacer}}`;
-    const tokenizedOptions = this.client.tokenizeRequestOptions(tokenizedUrl, getParams,  this.piiParamTokens, data);
+    const tokenizedOptions = this.client.tokenizeRequestOptions(tokenizedUrl, queryParams,  this.piiParamTokens, data);
 
-    const options = await this.client.prepareOptions(basePathUrl, '{{httpMethod}}', getParams, headers, body || undefined, tokenizedOptions, metadata);
+    const requestOptions = {
+      headers,
+      method: '{{httpMethod}}',
+      basePath,
+      queryParams,
+      body: body || undefined,
+      metadata,
+      tokenizedOptions,
+      api: this
+    };
+
+    const options = this.client.getRequestOptions ? await this.client.getRequestOptions(requestOptions) : await this.client.prepareOptions(basePath, '{{httpMethod}}', queryParams, headers, body || undefined, tokenizedOptions, metadata);
     const url = this.client.prepareUrl(options.basePath, options.queryParams);
 
     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}}');
diff --git a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jasmine.mustache b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jasmine.mustache
index 0c3d005e00..5c2f1da45b 100644
--- a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jasmine.mustache
+++ b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jasmine.mustache
@@ -9,9 +9,9 @@ import { {{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}} } from './{{#kebabC
 export class {{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}}Fixture implements Partial<Readonly<{{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}}>> {
 
   /** @inheritDoc */
-  public readonly apiName = "{{classname}}";
+  public readonly apiName = '{{classname}}';
 
-  {{#operation}}/**
+  {{#operation}}  /**
    * Fixture associated to function {{nickname}}
    */
   public {{nickname}}: jasmine.Spy = jasmine.createSpy('{{nickname}}');
diff --git a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jest.mustache b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jest.mustache
index 5ee1261b4c..30533de4b2 100644
--- a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jest.mustache
+++ b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.jest.mustache
@@ -12,9 +12,9 @@ import { {{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}}{{#operation}}, {{#u
 export class {{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}}Fixture implements Partial<Readonly<{{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}}>> {
 
   /** @inheritDoc */
-  public readonly apiName = "{{classname}}";
+  public readonly apiName = '{{classname}}';
 
-  {{#operation}}/**
+  {{#operation}}  /**
    * Fixture associated to function {{nickname}}
    */
   public {{nickname}}: jest.Mock<Promise<{{#vendorExtensions}}{{#responses2xxReturnTypes}}{{.}}{{^-last}} | {{/-last}}{{/responses2xxReturnTypes}}{{^responses2xxReturnTypes}}never{{/responses2xxReturnTypes}}{{/vendorExtensions}}>, [{{#uppercaseFirst}}{{nickname}}{{/uppercaseFirst}}RequestData]> = jest.fn();
diff --git a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.mustache b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.mustache
index a3d43eeed0..7a8292805a 100644
--- a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.mustache
+++ b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/api/fixture.mustache
@@ -10,9 +10,9 @@ import { {{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}} } from './{{#kebabC
 export class {{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}}Fixture implements Partial<Readonly<{{#uppercaseFirst}}{{classname}}{{/uppercaseFirst}}>> {
 
   /** @inheritDoc */
-  public readonly apiName = "{{classname}}";
+  public readonly apiName = '{{classname}}';
 
-  {{#operation}}/**
+  {{#operation}}  /**
    * Fixture associated to function {{nickname}}
    */
   public {{nickname}}: jasmine.Spy = jasmine.createSpy('{{nickname}}');
diff --git a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/model/enums.mustache b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/model/enums.mustache
index 336772b9a3..150a55e7f9 100644
--- a/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/model/enums.mustache
+++ b/packages/@ama-sdk/schematics/schematics/typescript/core/openapi-codegen-typescript/src/main/resources/typescriptFetch/model/enums.mustache
@@ -1,5 +1,5 @@
 {{#replaceWithEmptyExportIfNeeded}}
 {{#noEmptyExport}}
-  {{#models}}{{#model}}{{#hasEnums}}{{#removeBrackets}}export type { {{#trimComma}}{{#vars}}{{#isEnum}}{{{datatypeWithEnum}}} as {{vendorExtensions.x-exposed-classname}}{{{datatypeWithEnum}}}, {{/isEnum}}{{/vars}}{{/trimComma}} } from './{{#kebabCase}}{{classname}}{{/kebabCase}}/{{#kebabCase}}{{classname}}{{/kebabCase}}';
+{{#models}}{{#model}}{{#hasEnums}}{{#removeBrackets}}export type { {{#trimComma}}{{#vars}}{{#isEnum}}{{{datatypeWithEnum}}} as {{vendorExtensions.x-exposed-classname}}{{{datatypeWithEnum}}}, {{/isEnum}}{{/vars}}{{/trimComma}} } from './{{#kebabCase}}{{classname}}{{/kebabCase}}/{{#kebabCase}}{{classname}}{{/kebabCase}}';
 {{/removeBrackets}}{{/hasEnums}}{{/model}}{{/models}}{{/noEmptyExport}}
 {{/replaceWithEmptyExportIfNeeded}}