diff --git a/.apigentools-info b/.apigentools-info index 726d87158800..90fe40c45930 100644 --- a/.apigentools-info +++ b/.apigentools-info @@ -4,13 +4,13 @@ "spec_versions": { "v1": { "apigentools_version": "1.4.1.dev6", - "regenerated": "2021-03-23 10:51:13.975619", - "spec_repo_commit": "34cf37f" + "regenerated": "2021-03-23 14:01:21.590919", + "spec_repo_commit": "7e667f1" }, "v2": { "apigentools_version": "1.4.1.dev6", - "regenerated": "2021-03-23 10:51:18.277869", - "spec_repo_commit": "34cf37f" + "regenerated": "2021-03-23 14:01:26.701173", + "spec_repo_commit": "7e667f1" } } } \ No newline at end of file diff --git a/.generator/templates/.gitignore.mustache b/.generator/templates/.gitignore.mustache new file mode 100644 index 000000000000..1521c8b7652b --- /dev/null +++ b/.generator/templates/.gitignore.mustache @@ -0,0 +1 @@ +dist diff --git a/.generator/templates/README.mustache b/.generator/templates/README.mustache new file mode 100644 index 000000000000..d1c61395920d --- /dev/null +++ b/.generator/templates/README.mustache @@ -0,0 +1,30 @@ +## {{npmName}}@{{npmVersion}} + +This generator creates TypeScript/JavaScript client that utilizes {{framework}}. + +### Building + +To build and compile the typescript sources to javascript use: +``` +npm install +npm run build +``` + +### Publishing + +First build the package then run ```npm publish``` + +### Consuming + +navigate to the folder of your consuming project and run one of the following commands. + +_published:_ + +``` +npm install {{npmName}}@{{npmVersion}} --save +``` + +_unPublished (not recommended):_ + +``` +npm install PATH_TO_GENERATED_PACKAGE --save diff --git a/.generator/templates/api/api.mustache b/.generator/templates/api/api.mustache new file mode 100644 index 000000000000..9d578db3b6ec --- /dev/null +++ b/.generator/templates/api/api.mustache @@ -0,0 +1,219 @@ +// TODO: better import syntax? +import { BaseAPIRequestFactory, RequiredError } from './baseapi{{extensionForDeno}}'; +import {Configuration, getServer } from '../configuration{{extensionForDeno}}'; +import { RequestContext, HttpMethod, ResponseContext, HttpFile} from '../http/http{{extensionForDeno}}'; +{{#platforms}} +{{#node}} +import FormData from "form-data"; +{{/node}} +{{/platforms}} +import {ObjectSerializer} from '../models/ObjectSerializer{{extensionForDeno}}'; +import {ApiException} from './exception{{extensionForDeno}}'; +import {isCodeInRange} from '../util{{extensionForDeno}}'; +{{#useInversify}} +import { injectable } from "inversify"; +{{/useInversify}} + +{{#imports}} +import { {{classname}} } from '..{{filename}}{{extensionForDeno}}'; +{{/imports}} +{{#operations}} + +/** + * {{#description}}{{{description}}}{{/description}}{{^description}}no description{{/description}} + */ +{{#useInversify}} +@injectable() +{{/useInversify}} +export class {{classname}}RequestFactory extends BaseAPIRequestFactory { + + {{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{paramName}} {{description}} + {{/allParams}} + */ + public async {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Promise { + let config = options || this.configuration; + {{#allParams}} + + {{#required}} + // verify required parameter '{{paramName}}' is not null or undefined + if ({{paramName}} === null || {{paramName}} === undefined) { + throw new RequiredError('Required parameter {{paramName}} was null or undefined when calling {{nickname}}.'); + } + + {{/required}} + {{/allParams}} + + // Path Params + const localVarPath = '{{{path}}}'{{#pathParams}} + .replace('{' + '{{baseName}}' + '}', encodeURIComponent(String({{paramName}}))){{/pathParams}}; + + // Make Request Context + const requestContext = getServer(config, '{{classname}}.{{nickname}}').makeRequestContext(localVarPath, HttpMethod.{{httpMethod}}); + requestContext.setHeaderParam("Accept", "application/json, */*;q=0.8") + + // Query Params + {{#queryParams}} + if ({{paramName}} !== undefined) { + requestContext.setQueryParam("{{baseName}}", ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}", "{{dataFormat}}")); + } + {{/queryParams}} + + // Header Params + {{#headerParams}} + requestContext.setHeaderParam("{{baseName}}", ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}", "{{dataFormat}}")); + {{/headerParams}} + + // Form Params + {{#hasFormParams}} + let localVarFormParams = new FormData(); + {{/hasFormParams}} + + {{#formParams}} + {{#isArray}} + if ({{paramName}}) { + {{#isCollectionFormatMulti}} + {{paramName}}.forEach((element) => { + localVarFormParams.append('{{baseName}}', element as any); + }) + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + // TODO: replace .append with .set + localVarFormParams.append('{{baseName}}', {{paramName}}.join(COLLECTION_FORMATS["{{collectionFormat}}"])); + {{/isCollectionFormatMulti}} + } + {{/isArray}} + {{^isArray}} + if ({{paramName}} !== undefined) { + // TODO: replace .append with .set + {{^isFile}} + localVarFormParams.append('{{baseName}}', {{paramName}} as any); + {{/isFile}} + {{#isFile}} + {{#platforms}} + {{#node}} + localVarFormParams.append('{{baseName}}', {{paramName}}.data, {{paramName}}.name); + {{/node}} + {{^node}} + localVarFormParams.append('{{baseName}}', {{paramName}}, {{paramName}}.name); + {{/node}} + {{/platforms}} + {{/isFile}} + } + {{/isArray}} + {{/formParams}} + {{#hasFormParams}} + requestContext.setBody(localVarFormParams); + {{/hasFormParams}} + + // Body Params + {{#bodyParam}} + const contentType = ObjectSerializer.getPreferredMediaType([{{#consumes}} + "{{{mediaType}}}"{{^-last}},{{/-last}} + {{/consumes}}]); + requestContext.setHeaderParam("Content-Type", contentType); + const serializedBody = ObjectSerializer.stringify( + ObjectSerializer.serialize({{paramName}}, "{{{dataType}}}", "{{dataFormat}}"), + contentType + ); + requestContext.setBody(serializedBody); + {{/bodyParam}} + + {{#hasAuthMethods}} + let authMethod = null; + {{/hasAuthMethods}} + // Apply auth methods + {{#authMethods}} + authMethod = config.authMethods["{{name}}"] + if (authMethod) { + await authMethod.applySecurityAuthentication(requestContext); + } + {{/authMethods}} + + return requestContext; + } + + {{/operation}} +} +{{/operations}} +{{#operations}} + +{{#useInversify}} +@injectable() +{{/useInversify}} +export class {{classname}}ResponseProcessor { + + {{#operation}} + /** + * Unwraps the actual response sent by the server from the response context and deserializes the response content + * to the expected objects + * + * @params response Response returned by the server for a request to {{nickname}} + * @throws ApiException if the response code was not in [200, 299] + */ + public async {{nickname}}(response: ResponseContext): Promise<{{#returnType}}{{{returnType}}}{{/returnType}} {{^returnType}}void{{/returnType}}> { + const contentType = ObjectSerializer.normalizeMediaType(response.headers["content-type"]); + {{#responses}} + if (isCodeInRange("{{code}}", response.httpStatusCode)) { + {{#dataType}} + {{#isBinary}} + const body: {{{dataType}}} = await response.getBodyAsFile() as any as {{{returnType}}}; + {{/isBinary}} + {{^isBinary}} + const body: {{{dataType}}} = ObjectSerializer.deserialize( + ObjectSerializer.parse(await response.body.text(), contentType), + "{{{dataType}}}", "{{returnFormat}}" + ) as {{{dataType}}}; + {{/isBinary}} + {{#is2xx}} + return body; + {{/is2xx}} + {{^is2xx}} + throw new ApiException<{{{dataType}}}>({{code}}, body); + {{/is2xx}} + {{/dataType}} + {{^dataType}} + {{#is2xx}} + return; + {{/is2xx}} + {{^is2xx}} + throw new ApiException(response.httpStatusCode, "{{message}}"); + {{/is2xx}} + {{/dataType}} + } + {{/responses}} + + // Work around for missing responses in specification, e.g. for petstore.yaml + if (response.httpStatusCode >= 200 && response.httpStatusCode <= 299) { + {{#returnType}} + {{#isBinary}} + const body: {{{returnType}}} = await response.getBodyAsFile() as any as {{{returnType}}}; + {{/isBinary}} + {{^isBinary}} + const body: {{{returnType}}} = ObjectSerializer.deserialize( + ObjectSerializer.parse(await response.body.text(), contentType), + "{{{returnType}}}", "{{returnFormat}}" + ) as {{{returnType}}}; + {{/isBinary}} + return body; + {{/returnType}} + {{^returnType}} + return; + {{/returnType}} + } + + let body = response.body || ""; + throw new ApiException(response.httpStatusCode, "Unknown API Status Code!\nBody: \"" + body + "\""); + } + + {{/operation}} +} +{{/operations}} diff --git a/.generator/templates/api/baseapi.mustache b/.generator/templates/api/baseapi.mustache new file mode 100644 index 000000000000..e33b699f1e76 --- /dev/null +++ b/.generator/templates/api/baseapi.mustache @@ -0,0 +1,44 @@ +import { Configuration } from '../configuration{{extensionForDeno}}' +{{#useInversify}} +import { injectable, inject } from "inversify"; +import { AbstractConfiguration } from "../services/configuration"; +{{/useInversify}} + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + + +/** + * + * @export + * @class BaseAPI + */ +{{#useInversify}} +@injectable() +{{/useInversify}} +export class BaseAPIRequestFactory { + + constructor({{#useInversify}}@inject(AbstractConfiguration) {{/useInversify}}protected configuration: Configuration) { + } +}; + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} diff --git a/.generator/templates/api/exception.mustache b/.generator/templates/api/exception.mustache new file mode 100644 index 000000000000..fb8d314eb41d --- /dev/null +++ b/.generator/templates/api/exception.mustache @@ -0,0 +1,14 @@ +/** + * Represents an error caused by an api call i.e. it has attributes for a HTTP status code + * and the returned body object. + * + * Example + * API returns a ErrorMessageObject whenever HTTP status code is not in [200, 299] + * => ApiException(404, someErrorMessageObject) + * + */ +export class ApiException extends Error { + public constructor(public code: number, public body: T) { + super("HTTP-Code: " + code + "\nMessage: " + JSON.stringify(body)) + } +} diff --git a/.generator/templates/api/middleware.mustache b/.generator/templates/api/middleware.mustache new file mode 100644 index 000000000000..578756d534dd --- /dev/null +++ b/.generator/templates/api/middleware.mustache @@ -0,0 +1,66 @@ +import {RequestContext, ResponseContext} from './http/http{{extensionForDeno}}'; +import { Observable, from } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'./rxjsStub{{extensionForDeno}}'{{/useRxJS}}; + +/** + * Defines the contract for a middleware intercepting requests before + * they are sent (but after the RequestContext was created) + * and before the ResponseContext is unwrapped. + * + */ +export interface Middleware { + /** + * Modifies the request before the request is sent. + * + * @param context RequestContext of a request which is about to be sent to the server + * @returns an observable of the updated request context + * + */ + pre(context: RequestContext): Observable; + /** + * Modifies the returned response before it is deserialized. + * + * @param context ResponseContext of a sent request + * @returns an observable of the modified response context + */ + post(context: ResponseContext): Observable; +} + +export class PromiseMiddlewareWrapper implements Middleware { + + public constructor(private middleware: PromiseMiddleware) { + + } + + pre(context: RequestContext): Observable { + return from(this.middleware.pre(context)); + } + + post(context: ResponseContext): Observable { + return from(this.middleware.post(context)); + } + +} + +/** + * Defines the contract for a middleware intercepting requests before + * they are sent (but after the RequestContext was created) + * and before the ResponseContext is unwrapped. + * + */ +export interface PromiseMiddleware { + /** + * Modifies the request before the request is sent. + * + * @param context RequestContext of a request which is about to be sent to the server + * @returns an observable of the updated request context + * + */ + pre(context: RequestContext): Promise; + /** + * Modifies the returned response before it is deserialized. + * + * @param context ResponseContext of a sent request + * @returns an observable of the modified response context + */ + post(context: ResponseContext): Promise; +} diff --git a/.generator/templates/auth/auth.mustache b/.generator/templates/auth/auth.mustache new file mode 100644 index 000000000000..0e0ae23b1f28 --- /dev/null +++ b/.generator/templates/auth/auth.mustache @@ -0,0 +1,161 @@ +// typings for btoa are incorrect +{{#platforms}} +{{#node}} +//@ts-ignore +import * as btoa from "btoa"; +{{/node}} +{{/platforms}} +import { RequestContext } from "../http/http{{extensionForDeno}}"; +{{#useInversify}} +import { injectable, inject, named } from "inversify"; +import { AbstractTokenProvider } from "../services/configuration"; +{{/useInversify}} + +/** + * Interface authentication schemes. + */ +export interface SecurityAuthentication { + /* + * @return returns the name of the security authentication as specified in OAI + */ + getName(): string; + + /** + * Applies the authentication scheme to the request context + * + * @params context the request context which should use this authentication scheme + */ + applySecurityAuthentication(context: RequestContext): void | Promise; +} + +{{#useInversify}} +export const AuthApiKey = Symbol("auth.api_key"); +export const AuthUsername = Symbol("auth.username"); +export const AuthPassword = Symbol("auth.password"); + +{{/useInversify}} +export interface TokenProvider { + getToken(): Promise | string; +} + +{{#authMethods}} +/** + * Applies {{type}} authentication to the request context. + */ +{{#useInversify}} +@injectable() +{{/useInversify}} +export class {{#lambda.pascalcase}}{{name}}{{/lambda.pascalcase}}Authentication implements SecurityAuthentication { + {{#isApiKey}} + /** + * Configures this api key authentication with the necessary properties + * + * @param apiKey: The api key to be used for every request + */ + public constructor({{#useInversify}}@inject(AuthApiKey) @named("{{name}}") {{/useInversify}}private apiKey: string) {} + {{/isApiKey}} + {{#isBasicBasic}} + /** + * Configures the http authentication with the required details. + * + * @param username username for http basic authentication + * @param password password for http basic authentication + */ + public constructor( + {{#useInversify}}@inject(AuthUsername) @named("{{name}}") {{/useInversify}}private username: string, + {{#useInversify}}@inject(AuthPassword) @named("{{name}}") {{/useInversify}}private password: string + ) {} + {{/isBasicBasic}} + {{#isBasicBearer}} + /** + * Configures the http authentication with the required details. + * + * @param tokenProvider service that can provide the up-to-date token when needed + */ + public constructor({{#useInversify}}@inject(AbstractTokenProvider) @named("{{name}}") {{/useInversify}}private tokenProvider: TokenProvider) {} + {{/isBasicBearer}} + {{#isOAuth}} + // TODO: How to handle oauth2 authentication! + public constructor() {} + {{/isOAuth}} + + public getName(): string { + return "{{name}}"; + } + + public {{#isBasicBearer}}async {{/isBasicBearer}}applySecurityAuthentication(context: RequestContext) { + {{#isApiKey}} + context.{{#isKeyInHeader}}setHeaderParam{{/isKeyInHeader}}{{#isKeyInQuery}}addCookie{{/isKeyInQuery}}{{#isKeyInCookie}}setQueryParam{{/isKeyInCookie}}("{{keyParamName}}", this.apiKey); + {{/isApiKey}} + {{#isBasicBasic}} + let comb = this.username + ":" + this.password; + context.setHeaderParam("Authorization", "Basic " + btoa(comb)); + {{/isBasicBasic}} + {{#isBasicBearer}} + context.setHeaderParam("Authorization", "Bearer " + await this.tokenProvider.getToken()); + {{/isBasicBearer}} + {{#isOAuth}} + // TODO + {{/isOAuth}} + } +} + +{{/authMethods}} + +export type AuthMethods = { + {{#authMethods}} + "{{name}}"?: SecurityAuthentication{{^-last}},{{/-last}} + {{/authMethods}} +} +{{#useInversify}} + +export const authMethodServices = { + {{#authMethods}} + "{{name}}": {{#lambda.pascalcase}}{{name}}{{/lambda.pascalcase}}Authentication{{^-last}},{{/-last}} + {{/authMethods}} +} +{{/useInversify}} + +export type ApiKeyConfiguration = string; +export type HttpBasicConfiguration = { "username": string, "password": string }; +export type HttpBearerConfiguration = { tokenProvider: TokenProvider }; +export type OAuth2Configuration = string; + +export type AuthMethodsConfiguration = { + {{#authMethods}} + "{{name}}"?: {{#isApiKey}}ApiKeyConfiguration{{/isApiKey}}{{#isBasicBasic}}HttpBasicConfiguration{{/isBasicBasic}}{{#isBasicBearer}}HttpBearerConfiguration{{/isBasicBearer}}{{#isOAuth}}OAuth2Configuration{{/isOAuth}}{{^-last}},{{/-last}} + {{/authMethods}} +} + +/** + * Creates the authentication methods from a swagger description. + * + */ +export function configureAuthMethods(config: AuthMethodsConfiguration | undefined): AuthMethods { + let authMethods: AuthMethods = {} + + if (!config) { + return authMethods; + } + + {{#authMethods}} + if (config["{{name}}"]) { + authMethods["{{name}}"] = new {{#lambda.pascalcase}}{{name}}{{/lambda.pascalcase}}Authentication( + {{#isApiKey}} + config["{{name}}"] + {{/isApiKey}} + {{#isBasicBasic}} + config["{{name}}"]["username"], + config["{{name}}"]["password"] + {{/isBasicBasic}} + {{#isBasicBearer}} + config["{{name}}"]["tokenProvider"] + {{/isBasicBearer}} + {{#isOAuth}} + {{/isOAuth}} + ); + } + + {{/authMethods}} + return authMethods; +} \ No newline at end of file diff --git a/.generator/templates/configuration.mustache b/.generator/templates/configuration.mustache new file mode 100644 index 000000000000..51237ddbc98a --- /dev/null +++ b/.generator/templates/configuration.mustache @@ -0,0 +1,95 @@ +import { HttpLibrary } from "./http/http{{extensionForDeno}}"; +import { Middleware, PromiseMiddleware, PromiseMiddlewareWrapper } from "./middleware{{extensionForDeno}}"; +{{#frameworks}} +{{#fetch-api}} +import { IsomorphicFetchHttpLibrary as DefaultHttpLibrary } from "./http/isomorphic-fetch{{extensionForDeno}}"; +{{/fetch-api}} +{{#jquery}} +import { JQueryHttpLibrary as DefaultHttpLibrary } from "./http/jquery"; +{{/jquery}} +{{/frameworks}} +import { BaseServerConfiguration, servers, operationServers } from "./servers{{extensionForDeno}}"; +import { configureAuthMethods, AuthMethods, AuthMethodsConfiguration } from "./auth/auth{{extensionForDeno}}"; + +export interface Configuration { + readonly baseServer?: BaseServerConfiguration; + readonly serverIndex: number; + readonly operationServerIndices: { [ name: string ]: number }; + readonly httpApi: HttpLibrary; + readonly middleware: Middleware[]; + readonly authMethods: AuthMethods; +} + + +/** + * Interface with which a configuration object can be configured. + */ +export interface ConfigurationParameters { + /** + * Default server to use (takes preference over serverIndex and operationServerIndices) + */ + baseServer?: BaseServerConfiguration; + /** + * Default index of a server to use from the predefined server list + */ + serverIndex?: number; + /** + * Default index of a server to use for an operation from the predefined server operation map + */ + operationServerIndices?: { [ name: string ]: number }; + /** + * HTTP library to use e.g. IsomorphicFetch + */ + httpApi?: HttpLibrary; + /** + * The middlewares which will be applied to requests and responses + */ + middleware?: Middleware[]; + /** + * Configures all middlewares using the promise api instead of observables (which Middleware uses) + */ + promiseMiddleware?: PromiseMiddleware[]; + /** + * Configuration for the available authentication methods + */ + authMethods?: AuthMethodsConfiguration +} + +/** + * Configuration factory function + * + * If a property is not included in conf, a default is used: + * - baseServer: null + * - serverIndex: 0 + * - operationServerIndices: {} + * - httpApi: IsomorphicFetchHttpLibrary + * - middleware: [] + * - promiseMiddleware: [] + * - authMethods: {} + * + * @param conf partial configuration + */ +export function createConfiguration(conf: ConfigurationParameters = {}): Configuration { + const configuration: Configuration = { + baseServer: conf.baseServer, + serverIndex: conf.serverIndex || 0, + operationServerIndices: conf.operationServerIndices || {}, + httpApi: conf.httpApi || new DefaultHttpLibrary(), + middleware: conf.middleware || [], + authMethods: configureAuthMethods(conf.authMethods) + }; + if (conf.promiseMiddleware) { + conf.promiseMiddleware.forEach( + m => configuration.middleware.push(new PromiseMiddlewareWrapper(m)) + ); + } + return configuration; +} + +export function getServer(conf: Configuration, endpoint: string): BaseServerConfiguration { + if (conf.baseServer !== undefined) { + return conf.baseServer; + } + let index = (endpoint in conf.operationServerIndices) ? conf.operationServerIndices[endpoint] : conf.serverIndex; + return (endpoint in operationServers) ? operationServers[endpoint][index] : servers[index]; +} diff --git a/.generator/templates/git_push.sh.mustache b/.generator/templates/git_push.sh.mustache new file mode 100755 index 000000000000..8a32e53995d6 --- /dev/null +++ b/.generator/templates/git_push.sh.mustache @@ -0,0 +1,52 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/.generator/templates/http/http.mustache b/.generator/templates/http/http.mustache new file mode 100644 index 000000000000..88851d4e4ee2 --- /dev/null +++ b/.generator/templates/http/http.mustache @@ -0,0 +1,306 @@ +import { userAgent } from '../../../version'; +{{#platforms}} +{{#node}} +// TODO: evaluate if we can easily get rid of this library +import FormData from "form-data"; +{{/node}} +{{/platforms}} +{{#platforms}} +{{^deno}} +// typings of url-parse are incorrect... +// @ts-ignore +import URLParse from "url-parse"; +{{/deno}} +{{/platforms}} +import { Observable, from } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub{{extensionForDeno}}'{{/useRxJS}}; + +{{#platforms}} +{{^deno}} +{{#frameworks}} +{{#fetch-api}} +export * from './isomorphic-fetch'; +{{/fetch-api}} +{{#jquery}} +export * from './jquery'; +{{/jquery}} +{{/frameworks}} +{{/deno}} +{{/platforms}} + +/** + * Represents an HTTP method. + */ +export enum HttpMethod { + GET = "GET", + HEAD = "HEAD", + POST = "POST", + PUT = "PUT", + DELETE = "DELETE", + CONNECT = "CONNECT", + OPTIONS = "OPTIONS", + TRACE = "TRACE", + PATCH = "PATCH" +} + +/** + * Represents an HTTP file which will be transferred from or to a server. + */ +{{#platforms}} +{{#node}} +export type HttpFile = { + data: {{{fileContentDataType}}}, + name: string +}; +{{/node}} +{{^node}} +export type HttpFile = {{{fileContentDataType}}} & { readonly name: string }; +{{/node}} +{{/platforms}} + +{{#platforms}} +{{#deno}} +/** + * URLParse Wrapper for Deno + */ +class URLParse { + private url: URL; + + constructor(address: string, _parser: boolean) { + this.url = new URL(address); + } + + public set(_part: 'query', obj: {[key: string]: string | undefined}) { + for (const key in obj) { + const value = obj[key]; + if (value) { + this.url.searchParams.set(key, value); + } else { + this.url.searchParams.set(key, ""); + } + } + } + + public get query() { + const obj: {[key: string]: string} = {}; + for (const [key, value] of this.url.searchParams.entries()) { + obj[key] = value; + } + return obj; + } + + public toString() { + return this.url.toString(); + } +} +{{/deno}} +{{/platforms}} + +export class HttpException extends Error { + public constructor(msg: string) { + super(msg); + } +} + +/** + * Represents the body of an outgoing HTTP request. + */ +export type RequestBody = undefined | string | FormData; + +/** + * Represents an HTTP request context + */ +export class RequestContext { + private headers: { [key: string]: string } = { 'user-agent': userAgent }; + private body: RequestBody = undefined; + private url: URLParse; + + /** + * Creates the request context using a http method and request resource url + * + * @param url url of the requested resource + * @param httpMethod http method + */ + public constructor(url: string, private httpMethod: HttpMethod) { + this.url = new URLParse(url, true); + } + + /* + * Returns the url set in the constructor including the query string + * + */ + public getUrl(): string { + return this.url.toString(); + } + + /** + * Replaces the url set in the constructor with this url. + * + */ + public setUrl(url: string) { + this.url = new URLParse(url, true); + } + + /** + * Sets the body of the http request either as a string or FormData + * + * Note that setting a body on a HTTP GET, HEAD, DELETE, CONNECT or TRACE + * request is discouraged. + * https://httpwg.org/http-core/draft-ietf-httpbis-semantics-latest.html#rfc.section.7.3.1 + * + * @param body the body of the request + */ + public setBody(body: RequestBody) { + this.body = body; + } + + public getHttpMethod(): HttpMethod { + return this.httpMethod; + } + + public getHeaders(): { [key: string]: string } { + return this.headers; + } + + public getBody(): RequestBody { + return this.body; + } + + public setQueryParam(name: string, value: string) { + let queryObj = this.url.query; + queryObj[name] = value; + this.url.set("query", queryObj); + } + + /** + * Sets a cookie with the name and value. NO check for duplicate cookies is performed + * + */ + public addCookie(name: string, value: string): void { + if (!this.headers["Cookie"]) { + this.headers["Cookie"] = ""; + } + this.headers["Cookie"] += name + "=" + value + "; "; + } + + public setHeaderParam(key: string, value: string): void { + this.headers[key] = value; + } +} + +export interface ResponseBody { + text(): Promise; + binary(): Promise<{{{fileContentDataType}}}>; +} + +/** + * Helper class to generate a `ResponseBody` from binary data + */ +export class SelfDecodingBody implements ResponseBody { + constructor(private dataSource: Promise<{{{fileContentDataType}}}>) {} + + binary(): Promise<{{{fileContentDataType}}}> { + return this.dataSource; + } + + async text(): Promise { + const data: {{{fileContentDataType}}} = await this.dataSource; + {{#platforms}} + {{#node}} + return data.toString(); + {{/node}} + {{#browser}} + // @ts-ignore + if (data.text) { + // @ts-ignore + return data.text(); + } + + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.addEventListener("load", () => resolve(reader.result as string)); + reader.addEventListener("error", () => reject(reader.error)); + reader.readAsText(data); + }); + {{/browser}} + {{#deno}} + return data.text(); + {{/deno}} + {{/platforms}} + } +} + +export class ResponseContext { + public constructor( + public httpStatusCode: number, + public headers: { [key: string]: string }, + public body: ResponseBody + ) {} + + /** + * Parse header value in the form `value; param1="value1"` + * + * E.g. for Content-Type or Content-Disposition + * Parameter names are converted to lower case + * The first parameter is returned with the key `""` + */ + public getParsedHeader(headerName: string): { [parameter: string]: string } { + const result: { [parameter: string]: string } = {}; + if (!this.headers[headerName]) { + return result; + } + + const parameters = this.headers[headerName].split(";"); + for (const parameter of parameters) { + let [key, value] = parameter.split("=", 2); + key = key.toLowerCase().trim(); + if (value === undefined) { + result[""] = key; + } else { + value = value.trim(); + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substring(1, value.length - 1); + } + result[key] = value; + } + } + return result; + } + + public async getBodyAsFile(): Promise { + const data = await this.body.binary(); + const fileName = this.getParsedHeader("content-disposition")["filename"] || ""; + {{#platforms}} + {{#node}} + return { data, name: fileName }; + {{/node}} + {{^node}} + const contentType = this.headers["content-type"] || ""; + try { + return new File([data], fileName, { type: contentType }); + } catch (error) { + /** Fallback for when the File constructor is not available */ + return Object.assign(data, { + name: fileName, + type: contentType + }); + } + {{/node}} + {{/platforms}} + } +} + +export interface HttpLibrary { + send(request: RequestContext): Observable; +} + +export interface PromiseHttpLibrary { + send(request: RequestContext): Promise; +} + +export function wrapHttpLibrary(promiseHttpLibrary: PromiseHttpLibrary): HttpLibrary { + return { + send(request: RequestContext): Observable { + return from(promiseHttpLibrary.send(request)); + } + } +} diff --git a/.generator/templates/http/isomorphic-fetch.mustache b/.generator/templates/http/isomorphic-fetch.mustache new file mode 100644 index 000000000000..7646836694b3 --- /dev/null +++ b/.generator/templates/http/isomorphic-fetch.mustache @@ -0,0 +1,53 @@ +import {HttpLibrary, RequestContext, ResponseContext} from './http{{extensionForDeno}}'; +import { from, Observable } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub{{extensionForDeno}}'{{/useRxJS}}; +{{#platforms}} +{{#node}} +import fetch from "node-fetch"; +{{/node}} +{{#browser}} +import "whatwg-fetch"; +{{/browser}} +{{/platforms}} + +export class IsomorphicFetchHttpLibrary implements HttpLibrary { + + public send(request: RequestContext): Observable { + let method = request.getHttpMethod().toString(); + let body = request.getBody(); + + const resultPromise = fetch(request.getUrl(), { + method: method, + body: body as any, + headers: request.getHeaders(), + {{#platforms}} + {{#browser}} + credentials: "same-origin" + {{/browser}} + {{/platforms}} + }).then((resp: any) => { + const headers: { [name: string]: string } = {}; + resp.headers.forEach((value: string, name: string) => { + headers[name] = value; + }); + + {{#platforms}} + {{#node}} + const body = { + text: () => resp.text(), + binary: () => resp.buffer() + }; + {{/node}} + {{^node}} + const body = { + text: () => resp.text(), + binary: () => resp.blob() + }; + {{/node}} + {{/platforms}} + return new ResponseContext(resp.status, headers, body); + }); + + return from>(resultPromise); + + } +} diff --git a/.generator/templates/http/jquery.mustache b/.generator/templates/http/jquery.mustache new file mode 100644 index 000000000000..9d1427b977fb --- /dev/null +++ b/.generator/templates/http/jquery.mustache @@ -0,0 +1,86 @@ +import { HttpLibrary, RequestContext, ResponseContext, HttpException, SelfDecodingBody } from './http'; +import * as e6p from 'es6-promise' +import { from, Observable } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub'{{/useRxJS}}; +e6p.polyfill(); +import * as $ from 'jquery'; + + +export class JQueryHttpLibrary implements HttpLibrary { + + public send(request: RequestContext): Observable { + let method = request.getHttpMethod().toString(); + let body = request.getBody(); + let headerParams = request.getHeaders() + + let requestOptions: any = { + url: request.getUrl(), + type: method, + headers: request.getHeaders(), + processData: false, + xhrFields: { withCredentials: true }, + data: body + }; + + // If we want a blob, we have to set the xhrFields' responseType AND add a + // custom converter to overwrite the default deserialization of JQuery... + requestOptions["xhrFields"] = { responseType: 'blob' }; + requestOptions["converters"] = {} + requestOptions["converters"]["* blob"] = (result:any) => result; + requestOptions["dataType"] = "blob"; + + if (request.getHeaders()['Content-Type']) { + requestOptions.contentType = headerParams['Content-Type']; + } + requestOptions.dataFilter = ((headerParams: { [key:string]: string}) => { + return (data: string, type: string) => { + if (headerParams["Accept"] == "application/json" && data == "") { + return "{}" + } else { + return data + } + } + })(headerParams); + + if (request.getHeaders()["Cookie"]) { + throw new HttpException("Setting the \"Cookie\"-Header field is blocked by every major browser when using jquery.ajax requests. Please switch to another library like fetch to enable this option"); + } + + if (body && body.constructor.name == "FormData") { + requestOptions.contentType = false; + } + + const sentRequest = $.ajax(requestOptions); + + const resultPromise = new Promise((resolve, reject) => { + sentRequest.done((data, _, jqXHR) => { + const result = new ResponseContext( + jqXHR.status, + this.getResponseHeaders(jqXHR), + new SelfDecodingBody(Promise.resolve(data)) + ); + resolve(result); + }) + sentRequest.fail((jqXHR: any) => { + const headers = this.getResponseHeaders(jqXHR) + const result = new ResponseContext(jqXHR.status, headers, jqXHR.responseText); + resolve(result); + }) + }) + return from(resultPromise); + } + + private getResponseHeaders(jqXHR: any): { [key: string]: string } { + const responseHeaders: { [key: string]: string } = {}; + var headers = jqXHR.getAllResponseHeaders(); + headers = headers.split("\n"); + headers.forEach(function (header: any) { + header = header.split(": "); + var key = header.shift(); + if (key.length == 0) return + // chrome60+ force lowercase, other browsers can be different + key = key.toLowerCase(); + responseHeaders[key] = header.join(": "); + }); + return responseHeaders + } +} diff --git a/.generator/templates/http/servers.mustache b/.generator/templates/http/servers.mustache new file mode 100644 index 000000000000..5f465d34df9d --- /dev/null +++ b/.generator/templates/http/servers.mustache @@ -0,0 +1,83 @@ +import { RequestContext, HttpMethod } from "./http/http{{extensionForDeno}}"; + +export interface BaseServerConfiguration { + makeRequestContext(endpoint: string, httpMethod: HttpMethod): RequestContext; +} + +/** + * + * Represents the configuration of a server including its + * url template and variable configuration based on the url. + * + */ +export class ServerConfiguration implements BaseServerConfiguration { + public constructor(private url: string, private variableConfiguration: T) {} + + /** + * Sets the value of the variables of this server. + * + * @param variableConfiguration a partial variable configuration for the variables contained in the url + */ + public setVariables(variableConfiguration: Partial) { + Object.assign(this.variableConfiguration, variableConfiguration); + } + + public getConfiguration(): T { + return this.variableConfiguration + } + + private getUrl() { + let replacedUrl = this.url; + for (const key in this.variableConfiguration) { + var re = new RegExp("{" + key + "}","g"); + replacedUrl = replacedUrl.replace(re, this.variableConfiguration[key]); + } + return replacedUrl + } + + /** + * Creates a new request context for this server using the url with variables + * replaced with their respective values and the endpoint of the request appended. + * + * @param endpoint the endpoint to be queried on the server + * @param httpMethod httpMethod to be used + * + */ + public makeRequestContext(endpoint: string, httpMethod: HttpMethod): RequestContext { + return new RequestContext(this.getUrl() + endpoint, httpMethod); + } +} + +{{#servers}} +export const server{{-index}} = new ServerConfiguration<{ {{#variables}} + "{{name}}": {{#enumValues}}"{{.}}"{{^-last}} | {{/-last}}{{/enumValues}}{{^enumValues}}string{{/enumValues}}{{^-last}},{{/-last}} +{{/variables}} }>("{{url}}", { {{#variables}} + "{{name}}": "{{defaultValue}}" {{^-last}},{{/-last}}{{/variables}} +}) +{{/servers}} + +export const servers = [{{#servers}}server{{-index}}{{^-last}}, {{/-last}}{{/servers}}]; + +export const operationServers: { [endpoint: string]: BaseServerConfiguration[] } = { +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} +{{#servers}} +{{#-first}} + "{{{classname}}}.{{{nickname}}}": [ +{{/-first}} + new ServerConfiguration<{ {{#variables}} + "{{name}}": {{#enumValues}}"{{.}}"{{^-last}} | {{/-last}}{{/enumValues}}{{^enumValues}}string{{/enumValues}}{{^-last}},{{/-last}} + {{/variables}} }>("{{url}}", { {{#variables}} + "{{name}}": "{{defaultValue}}" {{^-last}},{{/-last}}{{/variables}} + }), +{{#-last}} + ], +{{/-last}} +{{/servers}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} diff --git a/.generator/templates/index.mustache b/.generator/templates/index.mustache new file mode 100644 index 000000000000..b19f7f9348a9 --- /dev/null +++ b/.generator/templates/index.mustache @@ -0,0 +1,40 @@ +export * from "./http/http{{extensionForDeno}}"; +export * from "./auth/auth{{extensionForDeno}}"; +export * from "./models/all{{extensionForDeno}}"; +export { createConfiguration } from "./configuration{{extensionForDeno}}" +export{{#platforms}}{{#deno}} type{{/deno}}{{/platforms}} { Configuration } from "./configuration{{extensionForDeno}}" +export * from "./apis/exception{{extensionForDeno}}"; +export * from "./servers{{extensionForDeno}}"; + +{{#useRxJS}} +export { Middleware } from './middleware{{extensionForDeno}}'; +{{/useRxJS}} +{{^useRxJS}} +export{{#platforms}}{{#deno}} type{{/deno}}{{/platforms}} { PromiseMiddleware as Middleware } from './middleware{{extensionForDeno}}'; +{{/useRxJS}} +{{#useObjectParameters}} +export { {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{classname}}{{operationIdCamelCase}}Request, {{/operation}}Object{{classname}} as {{classname}}{{^-last}}, {{/-last}} {{/operations}}{{/apis}}{{/apiInfo}}} from './types/ObjectParamAPI{{extensionForDeno}}'; +{{/useObjectParameters}} +{{^useObjectParameters}} +{{#useRxJS}} +export { {{#apiInfo}}{{#apis}}{{#operations}}Observable{{classname}} as {{classname}}{{^-last}}, {{/-last}} {{/operations}}{{/apis}}{{/apiInfo}}} from './types/ObservableAPI{{extensionForDeno}}'; +{{/useRxJS}} +{{^useRxJS}} +export { {{#apiInfo}}{{#apis}}{{#operations}}Promise{{classname}} as {{classname}}{{^-last}}, {{/-last}} {{/operations}}{{/apis}}{{/apiInfo}}} from './types/PromiseAPI{{extensionForDeno}}'; +{{/useRxJS}} +{{/useObjectParameters}} + +{{#useInversify}} +export * from "./services/index{{extensionForDeno}}"; +{{#useObjectParameters}} +export { {{#apiInfo}}{{#apis}}{{#operations}}AbstractObject{{classname}} as Abstract{{classname}}{{^-last}}, {{/-last}} {{/operations}}{{/apis}}{{/apiInfo}}} from './services/ObjectParamAPI'; +{{/useObjectParameters}} +{{^useObjectParameters}} +{{#useRxJS}} +export { {{#apiInfo}}{{#apis}}{{#operations}}AbstractObservable{{classname}} as Abstract{{classname}}{{^-last}}, {{/-last}} {{/operations}}{{/apis}}{{/apiInfo}}} from './services/ObservableAPI{{extensionForDeno}}'; +{{/useRxJS}} +{{^useRxJS}} +export { {{#apiInfo}}{{#apis}}{{#operations}}AbstractPromise{{classname}} as Abstract{{classname}}{{^-last}}, {{/-last}} {{/operations}}{{/apis}}{{/apiInfo}}} from './services/PromiseAPI{{extensionForDeno}}'; +{{/useRxJS}} +{{/useObjectParameters}} +{{/useInversify}} diff --git a/.generator/templates/licenseInfo.mustache b/.generator/templates/licenseInfo.mustache new file mode 100644 index 000000000000..c59e5b520cbc --- /dev/null +++ b/.generator/templates/licenseInfo.mustache @@ -0,0 +1,9 @@ +/** + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2020-Present Datadog, Inc. + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ diff --git a/.generator/templates/model/ObjectSerializer.mustache b/.generator/templates/model/ObjectSerializer.mustache new file mode 100644 index 000000000000..cb58a6c2f8d0 --- /dev/null +++ b/.generator/templates/model/ObjectSerializer.mustache @@ -0,0 +1,248 @@ +{{#models}} +{{#model}} +export * from './{{{ classFilename }}}{{extensionForDeno}}'; +{{/model}} +{{/models}} + +{{#models}} +{{#model}} +import { {{classname}}{{#hasEnums}}{{#vars}}{{#isEnum}}, {{classname}}{{enumName}} {{/isEnum}} {{/vars}}{{/hasEnums}} } from './{{{ classFilename }}}{{extensionForDeno}}'; +{{/model}} +{{/models}} + +/* tslint:disable:no-unused-variable */ +let primitives = [ + "string", + "boolean", + "double", + "integer", + "long", + "float", + "number", + "any" + ]; + +const supportedMediaTypes: { [mediaType: string]: number } = { + "application/json": Infinity, + "application/octet-stream": 0 +} + + +let enumsMap: Set = new Set([ + {{#models}} + {{#model}} + {{#isEnum}} + "{{classname}}{{enumName}}", + {{/isEnum}} + {{#hasEnums}} + {{#vars}} + {{#isEnum}} + "{{classname}}{{enumName}}", + {{/isEnum}} + {{/vars}} + {{/hasEnums}} + {{/model}} + {{/models}} +]); + +let typeMap: {[index: string]: any} = { + {{#models}} + {{#model}} + {{^isEnum}} + "{{classname}}": {{classname}}, + {{/isEnum}} + {{/model}} + {{/models}} +} + +export class ObjectSerializer { + public static findCorrectType(data: any, expectedType: string) { + if (data == undefined) { + return expectedType; + } else if (primitives.indexOf(expectedType.toLowerCase()) !== -1) { + return expectedType; + } else if (expectedType === "Date") { + return expectedType; + } else { + if (enumsMap.has(expectedType)) { + return expectedType; + } + + if (!typeMap[expectedType]) { + return expectedType; // w/e we don't know the type + } + + // Check the discriminator + let discriminatorProperty = typeMap[expectedType].discriminator; + if (discriminatorProperty == null) { + return expectedType; // the type does not have a discriminator. use it. + } else { + if (data[discriminatorProperty]) { + var discriminatorType = data[discriminatorProperty]; + if(typeMap[discriminatorType]){ + return discriminatorType; // use the type given in the discriminator + } else { + return expectedType; // discriminator did not map to a type + } + } else { + return expectedType; // discriminator was not present (or an empty string) + } + } + } + } + + public static serialize(data: any, type: string, format: string) { + if (data == undefined) { + return data; + } else if (primitives.indexOf(type.toLowerCase()) !== -1) { + return data; + } else if (type.lastIndexOf("Array<", 0) === 0) { // string.startsWith pre es6 + let subType: string = type.replace("Array<", ""); // Array => Type> + subType = subType.substring(0, subType.length - 1); // Type> => Type + let transformedData: any[] = []; + for (let index in data) { + let date = data[index]; + transformedData.push(ObjectSerializer.serialize(date, subType, format)); + } + return transformedData; + } else if (type === "Date") { + if ("string" == typeof data) { + return data; + } + if (format == "date") { + let month = data.getMonth()+1 + month = month < 10 ? "0" + month.toString() : month.toString() + let day = data.getDate(); + day = day < 10 ? "0" + day.toString() : day.toString(); + + return data.getFullYear() + "-" + month + "-" + day; + } else { + return data.toISOString(); + } + } else { + if (enumsMap.has(type)) { + return data; + } + if (!typeMap[type]) { // in case we dont know the type + return data; + } + + // Get the actual type of this object + type = this.findCorrectType(data, type); + + // get the map for the correct type. + let attributeTypes = typeMap[type].getAttributeTypeMap(); + let instance: {[index: string]: any} = {}; + for (let index in attributeTypes) { + let attributeType = attributeTypes[index]; + instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type, attributeType.format); + } + return instance; + } + } + + public static deserialize(data: any, type: string, format: string) { + // polymorphism may change the actual type. + type = ObjectSerializer.findCorrectType(data, type); + if (data == undefined) { + return data; + } else if (primitives.indexOf(type.toLowerCase()) !== -1) { + return data; + } else if (type.lastIndexOf("Array<", 0) === 0) { // string.startsWith pre es6 + let subType: string = type.replace("Array<", ""); // Array => Type> + subType = subType.substring(0, subType.length - 1); // Type> => Type + let transformedData: any[] = []; + for (let index in data) { + let date = data[index]; + transformedData.push(ObjectSerializer.deserialize(date, subType, format)); + } + return transformedData; + } else if (type === "Date") { + return new Date(data); + } else { + if (enumsMap.has(type)) {// is Enum + return data; + } + + if (!typeMap[type]) { // dont know the type + return data; + } + let instance = new typeMap[type](); + let attributeTypes = typeMap[type].getAttributeTypeMap(); + for (let index in attributeTypes) { + let attributeType = attributeTypes[index]; + instance[attributeType.name] = ObjectSerializer.deserialize(data[attributeType.baseName], attributeType.type, attributeType.format); + } + return instance; + } + } + + + /** + * Normalize media type + * + * We currently do not handle any media types attributes, i.e. anything + * after a semicolon. All content is assumed to be UTF-8 compatible. + */ + public static normalizeMediaType(mediaType: string | undefined): string | undefined { + if (mediaType === undefined) { + return undefined; + } + return mediaType.split(";")[0].trim().toLowerCase(); + } + + /** + * From a list of possible media types, choose the one we can handle best. + * + * The order of the given media types does not have any impact on the choice + * made. + */ + public static getPreferredMediaType(mediaTypes: Array): string { + /** According to OAS 3 we should default to json */ + if (!mediaTypes) { + return "application/json"; + } + + const normalMediaTypes = mediaTypes.map(this.normalizeMediaType); + let selectedMediaType: string | undefined = undefined; + let selectedRank: number = -Infinity; + for (const mediaType of normalMediaTypes) { + if (supportedMediaTypes[mediaType!] > selectedRank) { + selectedMediaType = mediaType; + selectedRank = supportedMediaTypes[mediaType!]; + } + } + + if (selectedMediaType === undefined) { + throw new Error("None of the given media types are supported: " + mediaTypes.join(", ")); + } + + return selectedMediaType!; + } + + /** + * Convert data to a string according the given media type + */ + public static stringify(data: any, mediaType: string): string { + if (mediaType === "application/json") { + return JSON.stringify(data); + } + + throw new Error("The mediaType " + mediaType + " is not supported by ObjectSerializer.stringify."); + } + + /** + * Parse data from a string according to the given media type + */ + public static parse(rawData: string, mediaType: string | undefined) { + if (mediaType === undefined) { + throw new Error("Cannot parse content. No Content-Type defined."); + } + + if (mediaType === "application/json") { + return JSON.parse(rawData); + } + + throw new Error("The mediaType " + mediaType + " is not supported by ObjectSerializer.parse."); + } +} diff --git a/.generator/templates/model/model.mustache b/.generator/templates/model/model.mustache new file mode 100644 index 000000000000..3cf7498ceaa7 --- /dev/null +++ b/.generator/templates/model/model.mustache @@ -0,0 +1,84 @@ +{{>licenseInfo}} +{{#models}} +{{#model}} +{{#tsImports}} +import { {{classname}} } from './{{filename}}{{extensionForDeno}}'; +{{/tsImports}} +import { HttpFile } from '../http/http{{extensionForDeno}}'; + +{{#description}} +/** +* {{{description}}} +*/ +{{/description}} +{{^isEnum}} +export class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{ +{{#vars}} +{{#description}} + /** + * {{{description}}} + */ +{{/description}} + '{{name}}'{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}; +{{/vars}} + + {{#discriminator}} + static readonly discriminator: string | undefined = "{{discriminatorName}}"; + {{/discriminator}} + {{^discriminator}} + static readonly discriminator: string | undefined = undefined; + {{/discriminator}} + + {{^isArray}} + static readonly attributeTypeMap: Array<{name: string, baseName: string, type: string, format: string}> = [ + {{#vars}} + { + "name": "{{name}}", + "baseName": "{{baseName}}", + "type": "{{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}", + "format": "{{dataFormat}}" + }{{^-last}}, + {{/-last}} + {{/vars}} + ]; + + static getAttributeTypeMap() { + {{#parent}} + return super.getAttributeTypeMap().concat({{classname}}.attributeTypeMap); + {{/parent}} + {{^parent}} + return {{classname}}.attributeTypeMap; + {{/parent}} + } + {{/isArray}} + + public constructor() { + {{#parent}} + super(); + {{/parent}} + {{#allVars}} + {{#discriminatorValue}} + this.{{name}} = "{{discriminatorValue}}"; + {{/discriminatorValue}} + {{/allVars}} + {{#discriminatorName}} + this.{{discriminatorName}} = "{{classname}}"; + {{/discriminatorName}} + } +} + +{{#hasEnums}} + +{{#vars}} +{{#isEnum}} +export type {{classname}}{{enumName}} ={{#allowableValues}}{{#values}} "{{.}}" {{^-last}}|{{/-last}}{{/values}}{{/allowableValues}}; +{{/isEnum}} +{{/vars}} + +{{/hasEnums}} +{{/isEnum}} +{{#isEnum}} +export type {{classname}} ={{#allowableValues}}{{#values}} "{{.}}" {{^-last}}|{{/-last}}{{/values}}{{/allowableValues}}; +{{/isEnum}} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/.generator/templates/model/models_all.mustache b/.generator/templates/model/models_all.mustache new file mode 100644 index 000000000000..43782db7ef55 --- /dev/null +++ b/.generator/templates/model/models_all.mustache @@ -0,0 +1,5 @@ +{{#models}} +{{#model}} +export * from './{{{ classFilename }}}{{extensionForDeno}}' +{{/model}} +{{/models}} \ No newline at end of file diff --git a/.generator/templates/package.mustache b/.generator/templates/package.mustache new file mode 100644 index 000000000000..807989026b14 --- /dev/null +++ b/.generator/templates/package.mustache @@ -0,0 +1,61 @@ +{ + "name": "{{npmName}}", + "version": "{{npmVersion}}", + "description": "OpenAPI client for {{npmName}}", + "author": "OpenAPI-Generator Contributors", + "keywords": [ + "fetch", + "typescript", + "openapi-client", + "openapi-generator" + ], + "license": "Unlicense", + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "prepare": "npm run build" + }, + "dependencies": { + {{#frameworks}} + {{#fetch-api}} + {{#platforms}} + {{#node}} + "node-fetch": "^2.6.0", + "@types/node-fetch": "^2.5.7", + {{/node}} + {{#browser}} + "whatwg-fetch": "^3.0.0", + {{/browser}} + {{/platforms}} + {{/fetch-api}} + {{#jquery}} + "@types/jquery": "^3.3.29", + "jquery": "^3.4.1", + {{/jquery}} + {{/frameworks}} + {{#platforms}} + {{#node}} + "@types/node": "*", + "form-data": "^2.5.0", + "btoa": "^1.2.1", + {{/node}} + {{/platforms}} + {{#useRxJS}} + "rxjs": "^6.4.0", + {{/useRxJS}} + {{#useInversify}} + "inversify": "^5.0.1", + {{/useInversify}} + "es6-promise": "^4.2.4", + "url-parse": "^1.4.3" + }, + "devDependencies": { + "typescript": "^3.9.3" + }{{#npmRepository}},{{/npmRepository}} +{{#npmRepository}} + "publishConfig":{ + "registry":"{{npmRepository}}" + } +{{/npmRepository}} +} diff --git a/.generator/templates/rxjsStub.mustache b/.generator/templates/rxjsStub.mustache new file mode 100644 index 000000000000..4c73715a2486 --- /dev/null +++ b/.generator/templates/rxjsStub.mustache @@ -0,0 +1,27 @@ +export class Observable { + constructor(private promise: Promise) {} + + toPromise() { + return this.promise; + } + + pipe(callback: (value: T) => S | Promise): Observable { + return new Observable(this.promise.then(callback)); + } +} + +export function from(promise: Promise) { + return new Observable(promise); +} + +export function of(value: T) { + return new Observable(Promise.resolve(value)); +} + +export function mergeMap(callback: (value: T) => Observable) { + return (value: T) => callback(value).toPromise(); +} + +export function map(callback: any) { + return callback; +} diff --git a/.generator/templates/services/ObjectParamAPI.mustache b/.generator/templates/services/ObjectParamAPI.mustache new file mode 100644 index 000000000000..1c34257dcfa7 --- /dev/null +++ b/.generator/templates/services/ObjectParamAPI.mustache @@ -0,0 +1,35 @@ +import type { HttpFile } from '../http/http'; +import type { Configuration } from '../configuration' +import type * as req from "../types/ObjectParamAPI"; +{{#useRxJS}} +import type { Observable } from 'rxjs'; +{{/useRxJS}} + +{{#models}} +{{#model}} +import type { {{{ classname }}} } from '../models/{{{ classFilename }}}'; +{{/model}} +{{/models}} +{{#apiInfo}} +{{#apis}} +{{#operations}} + + +export abstract class AbstractObject{{classname}} { + {{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + * @param param the request object + */ + public abstract {{nickname}}(param: req.{{classname}}{{operationIdCamelCase}}Request, options?: Configuration): {{#useRxJS}}Observable{{/useRxJS}}{{^useRxJS}}Promise{{/useRxJS}}<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>; + + {{/operation}} +} +{{/operations}} +{{/apis}} +{{/apiInfo}} \ No newline at end of file diff --git a/.generator/templates/services/ObservableAPI.mustache b/.generator/templates/services/ObservableAPI.mustache new file mode 100644 index 000000000000..209436b07b41 --- /dev/null +++ b/.generator/templates/services/ObservableAPI.mustache @@ -0,0 +1,23 @@ +import type { HttpFile } from "../http/http"; +import type { Observable } from {{#useRxJS}}"rxjs"{{/useRxJS}}{{^useRxJS}}"../rxjsStub"{{/useRxJS}}; +import type { Configuration } from "../configuration"; + +{{#models}} +{{#model}} +import { {{{ classname }}} } from "../models/{{{ classFilename }}}"; +{{/model}} +{{/models}} +{{#apiInfo}} +{{#apis}} +{{#operations}} + + +export abstract class AbstractObservable{{classname}} { + {{#operation}} + public abstract {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>; + + {{/operation}} +} +{{/operations}} +{{/apis}} +{{/apiInfo}} \ No newline at end of file diff --git a/.generator/templates/services/PromiseAPI.mustache b/.generator/templates/services/PromiseAPI.mustache new file mode 100644 index 000000000000..d3403ff707a4 --- /dev/null +++ b/.generator/templates/services/PromiseAPI.mustache @@ -0,0 +1,22 @@ +import type { HttpFile } from "../http/http"; +import type { Configuration } from "../configuration"; + +{{#models}} +{{#model}} +import { {{{ classname }}} } from "../models/{{{ classFilename }}}"; +{{/model}} +{{/models}} +{{#apiInfo}} +{{#apis}} +{{#operations}} + + +export abstract class AbstractPromise{{classname}} { + {{#operation}} + public abstract {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>; + + {{/operation}} +} +{{/operations}} +{{/apis}} +{{/apiInfo}} \ No newline at end of file diff --git a/.generator/templates/services/api.mustache b/.generator/templates/services/api.mustache new file mode 100644 index 000000000000..8992017a7065 --- /dev/null +++ b/.generator/templates/services/api.mustache @@ -0,0 +1,23 @@ +import type { Configuration } from "../configuration"; +import type { HttpFile, RequestContext, ResponseContext } from "../http/http"; + +{{#imports}} +import { {{classname}} } from "..{{filename}}"; +{{/imports}} +{{#operations}} + +export abstract class Abstract{{classname}}RequestFactory { + {{#operation}} + public abstract {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Promise; + + {{/operation}} +} + + +export abstract class Abstract{{classname}}ResponseProcessor { + {{#operation}} + public abstract {{nickname}}(response: ResponseContext): Promise<{{#returnType}}{{{returnType}}}{{/returnType}} {{^returnType}}void{{/returnType}}>; + + {{/operation}} +} +{{/operations}} diff --git a/.generator/templates/services/configuration.mustache b/.generator/templates/services/configuration.mustache new file mode 100644 index 000000000000..06e8dc992fcc --- /dev/null +++ b/.generator/templates/services/configuration.mustache @@ -0,0 +1,21 @@ +import type { AbstractServerConfiguration } from "./http"; +import type { HttpLibrary, RequestContext } from "../http/http"; +import type { Middleware } from "../middleware"; +import type { AuthMethods, TokenProvider } from "../auth/auth"; +import type { Configuration } from "../configuration"; + +export abstract class AbstractConfiguration implements Configuration { + abstract get baseServer(): AbstractServerConfiguration; + abstract get httpApi(): HttpLibrary; + abstract get middleware(): Middleware[]; + abstract get authMethods(): AuthMethods; +} + +export abstract class AbstractAuthMethod { + public abstract getName(): string; + public abstract applySecurityAuthentication(context: RequestContext): void | Promise; +}; + +export abstract class AbstractTokenProvider implements TokenProvider { + public abstract getToken(): string | Promise; +} diff --git a/.generator/templates/services/http.mustache b/.generator/templates/services/http.mustache new file mode 100644 index 000000000000..89bda7bf3e3d --- /dev/null +++ b/.generator/templates/services/http.mustache @@ -0,0 +1,20 @@ +{{#useRxJS}} +import type { Observable } from "rxjs"; +{{/useRxJS}} +import type { {{^useRxJS}}Promise{{/useRxJS}}HttpLibrary, HttpMethod, RequestContext, ResponseContext } from "../http/http"; +import type { {{^useRxJS}}Promise{{/useRxJS}}Middleware } from "../middleware"; +import type { BaseServerConfiguration } from "../servers"; + +export abstract class AbstractHttpLibrary implements {{^useRxJS}}Promise{{/useRxJS}}HttpLibrary { + public abstract send(request: RequestContext): {{#useRxJS}}Observable{{/useRxJS}}{{^useRxJS}}Promise{{/useRxJS}}; +}; + +export abstract class AbstractMiddleware implements {{^useRxJS}}Promise{{/useRxJS}}Middleware { + public abstract pre(context: RequestContext): {{#useRxJS}}Observable{{/useRxJS}}{{^useRxJS}}Promise{{/useRxJS}}; + public abstract post(context: ResponseContext): {{#useRxJS}}Observable{{/useRxJS}}{{^useRxJS}}Promise{{/useRxJS}}; +} + +export abstract class AbstractServerConfiguration implements BaseServerConfiguration { + public abstract makeRequestContext(endpoint: string, httpMethod: HttpMethod): RequestContext; +}; + diff --git a/.generator/templates/services/index.mustache b/.generator/templates/services/index.mustache new file mode 100644 index 000000000000..0b662f5d4d33 --- /dev/null +++ b/.generator/templates/services/index.mustache @@ -0,0 +1,165 @@ +import { inject, injectable, multiInject, optional, interfaces } from "inversify"; + +import { Configuration } from "../configuration"; +import { ServerConfiguration, servers } from "../servers"; +import { HttpLibrary{{^useRxJS}}, wrapHttpLibrary{{/useRxJS}} } from "../http/http"; +import { Middleware{{^useRxJS}}, PromiseMiddlewareWrapper{{/useRxJS}} } from "../middleware"; +import { authMethodServices, AuthMethods } from "../auth/auth"; + +{{#frameworks}} +{{#fetch-api}} +import { IsomorphicFetchHttpLibrary as DefaultHttpLibrary } from "../http/isomorphic-fetch"; +{{/fetch-api}} +{{#jquery}} +import { JQueryHttpLibrary as DefaultHttpLibrary } from "../http/jquery"; +{{/jquery}} +{{/frameworks}} + +import { AbstractHttpLibrary, AbstractMiddleware, AbstractServerConfiguration } from "./http"; +import { AbstractConfiguration, AbstractAuthMethod, AbstractTokenProvider } from "./configuration"; + +export { AbstractHttpLibrary, AbstractMiddleware, AbstractServerConfiguration, AbstractConfiguration, AbstractAuthMethod, AbstractTokenProvider }; + +{{#useObjectParameters}} +import * as apis from "../types/ObjectParamAPI"; +import * as apiServices from "./ObjectParamAPI"; +{{/useObjectParameters}} +{{^useObjectParameters}} +{{#useRxJS}} +import * as apis from "../types/ObservableAPI"; +import * as apiServices from "./ObservableAPI"; +{{/useRxJS}} +{{^useRxJS}} +import * as apis from "../types/PromiseAPI"; +import * as apiServices from "./PromiseAPI"; +{{/useRxJS}} +{{/useObjectParameters}} + +@injectable() +class InjectableConfiguration implements AbstractConfiguration { + public httpApi: HttpLibrary = new DefaultHttpLibrary(); + public middleware: Middleware[] = []; + public authMethods: AuthMethods = {}; + + constructor( + @inject(AbstractServerConfiguration) @optional() public baseServer: AbstractServerConfiguration = servers[0], + @inject(AbstractHttpLibrary) @optional() httpApi: AbstractHttpLibrary, + @multiInject(AbstractMiddleware) @optional() middleware: AbstractMiddleware[] = [], + @multiInject(AbstractAuthMethod) @optional() securityConfiguration: AbstractAuthMethod[] = [] + ) { + {{#useRxJS}} + this.httpApi = httpApi || new DefaultHttpLibrary(); + this.middleware = middleware; + {{/useRxJS}} + {{^useRxJS}} + this.httpApi = httpApi === undefined ? new DefaultHttpLibrary() : wrapHttpLibrary(httpApi); + for (const _middleware of middleware) { + this.middleware.push(new PromiseMiddlewareWrapper(_middleware)); + } + {{/useRxJS}} + for (const authMethod of securityConfiguration) { + const authName = authMethod.getName(); + // @ts-ignore + if (authMethodServices[authName] !== undefined) { + // @ts-ignore + this.authMethods[authName] = authMethod; + } + } + } +} + +/** + * Helper class to simplify binding the services + */ +export class ApiServiceBinder { + constructor(private container: interfaces.Container) { + this.container.bind(AbstractConfiguration).to(InjectableConfiguration); + } + + /** + * Allows you to bind a server configuration without having to import the service identifier. + */ + public get bindServerConfiguration() { + return this.container.bind(AbstractServerConfiguration); + } + + /** + * Use one of the predefined server configurations. + * + * To customize the server variables you can call `setVariables` on the + * return value; + */ + public bindServerConfigurationToPredefined(idx: number) { + this.bindServerConfiguration.toConstantValue(servers[idx]); + return servers[idx]; + } + + /** + * Explicitly define the service base url + */ + public bindServerConfigurationToURL(url: string) { + return this.bindServerConfiguration.toConstantValue( + new ServerConfiguration<{}>(url, {}) + ); + } + + /** + * Allows you to bind a http library without having to import the service identifier. + */ + public get bindHttpLibrary() { + return this.container.bind(AbstractHttpLibrary); + } + + /** + * Allows you to bind a middleware without having to import the service identifier. + * + * You can bind multiple middlewares by calling this multiple method times. + */ + public get bindMiddleware() { + return this.container.bind(AbstractMiddleware); + } + + /** + * Allows you to bind an auth method without having to import the service identifier. + * + * Note: The name of the bound auth method needs to be known in the specs, + * because the name is used to decide for which endpoints to apply the authentication. + */ + public get bindAuthMethod() { + return this.container.bind(AbstractAuthMethod); + } + + /** + * Use one of the predefined auth methods. + * + * Make sure that you have injected all dependencies for it. + */ + public bindAuthMethodToPredefined(name: keyof AuthMethods) { + return this.bindAuthMethod.to(authMethodServices[name]); + } + + /** + * Bind all the apis to their respective service identifiers + * + * If you want to only bind some of the apis, you need to do that manually. + */ + public bindAllApiServices() { + {{#apiInfo}} + {{#apis}} + {{#operations}} + {{#useObjectParameters}} + this.container.bind(apiServices.AbstractObject{{classname}}).to(apis.Object{{classname}}).inSingletonScope(); + {{/useObjectParameters}} + {{^useObjectParameters}} + {{#useRxJS}} + this.container.bind(apiServices.AbstractObservable{{classname}}).to(apis.Observable{{classname}}).inSingletonScope(); + {{/useRxJS}} + {{^useRxJS}} + this.container.bind(apiServices.AbstractPromise{{classname}}).to(apis.Promise{{classname}}).inSingletonScope(); + {{/useRxJS}} + {{/useObjectParameters}} + {{/operations}} + {{/apis}} + {{/apiInfo}} + } +} diff --git a/.generator/templates/tsconfig.mustache b/.generator/templates/tsconfig.mustache new file mode 100644 index 000000000000..886f26f68ac3 --- /dev/null +++ b/.generator/templates/tsconfig.mustache @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "strict": true, + /* Basic Options */ + "target": "{{#supportsES6}}es6{{/supportsES6}}{{^supportsES6}}es5{{/supportsES6}}", + "module": "{{#supportsES6}}es6{{/supportsES6}}{{^supportsES6}}commonjs{{/supportsES6}}", + "moduleResolution": "node", + "declaration": true, + + /* Additional Checks */ + "noUnusedLocals": false, /* Report errors on unused locals. */ // TODO: reenable (unused imports!) + "noUnusedParameters": false, /* Report errors on unused parameters. */ // TODO: set to true again + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + "removeComments": true, + "sourceMap": true, + "outDir": "./dist", + "noLib": false, + {{#platforms}} + {{#node}} + "lib": [ "es6" ], + {{/node}} + {{#browser}} + "lib": [ "es6", "dom" ], + {{/browser}} + {{/platforms}} + {{#useInversify}} + "experimentalDecorators": true, + {{/useInversify}} + }, + "exclude": [ + "dist", + "node_modules" + ], + "filesGlob": [ + "./**/*.ts", + ] +} diff --git a/.generator/templates/types/ObjectParamAPI.mustache b/.generator/templates/types/ObjectParamAPI.mustache new file mode 100644 index 000000000000..43886364d6c3 --- /dev/null +++ b/.generator/templates/types/ObjectParamAPI.mustache @@ -0,0 +1,58 @@ +import { ResponseContext, RequestContext, HttpFile } from '../http/http{{extensionForDeno}}'; +import * as models from '../models/all{{extensionForDeno}}'; +import { Configuration} from '../configuration{{extensionForDeno}}' +{{#useRxJS}} +import { Observable } from 'rxjs'; +{{/useRxJS}} + +{{#models}} +{{#model}} +import { {{{ classname }}} } from '../models/{{{ classFilename }}}{{extensionForDeno}}'; +{{/model}} +{{/models}} +{{#apiInfo}} +{{#apis}} + +{{#operations}} +import { Observable{{classname}} } from "./ObservableAPI{{extensionForDeno}}"; +import { {{classname}}RequestFactory, {{classname}}ResponseProcessor} from "../apis/{{classname}}{{extensionForDeno}}"; + +{{#operation}} +export interface {{classname}}{{operationIdCamelCase}}Request { + {{#allParams}} + /** + * {{description}} + * @type {{dataType}} + * @memberof {{classname}}{{nickname}} + */ + {{paramName}}{{^required}}?{{/required}}: {{{dataType}}} + {{/allParams}} +} + +{{/operation}} +export class Object{{classname}} { + private api: Observable{{classname}} + + public constructor(configuration: Configuration, requestFactory?: {{classname}}RequestFactory, responseProcessor?: {{classname}}ResponseProcessor) { + this.api = new Observable{{classname}}(configuration, requestFactory, responseProcessor); + } + +{{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + * @param param the request object + */ + public {{nickname}}(param: {{classname}}{{operationIdCamelCase}}Request, options?: Configuration): {{#useRxJS}}Observable{{/useRxJS}}{{^useRxJS}}Promise{{/useRxJS}}<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + return this.api.{{nickname}}({{#allParams}}param.{{paramName}}, {{/allParams}} options){{^useRxJS}}.toPromise(){{/useRxJS}}; + } + +{{/operation}} +} +{{/operations}} +{{/apis}} +{{/apiInfo}} diff --git a/.generator/templates/types/ObservableAPI.mustache b/.generator/templates/types/ObservableAPI.mustache new file mode 100644 index 000000000000..645d28b7c25c --- /dev/null +++ b/.generator/templates/types/ObservableAPI.mustache @@ -0,0 +1,88 @@ +import { ResponseContext, RequestContext, HttpFile } from '../http/http{{extensionForDeno}}'; +import * as models from '../models/all{{extensionForDeno}}'; +import { Configuration} from '../configuration{{extensionForDeno}}' +import { Observable, of, from as from_ } from {{#useRxJS}}'rxjs'{{/useRxJS}}{{^useRxJS}}'../rxjsStub{{extensionForDeno}}'{{/useRxJS}}; +import {mergeMap, map} from {{#useRxJS}}'rxjs/operators'{{/useRxJS}}{{^useRxJS}}'../rxjsStub{{extensionForDeno}}'{{/useRxJS}}; +{{#useInversify}} +import { injectable, inject, optional } from "inversify"; +import { AbstractConfiguration } from "../services/configuration{{extensionForDeno}}"; +{{/useInversify}} +{{#models}} +{{#model}} +import { {{{ classname }}} } from '../models/{{{ classFilename }}}{{extensionForDeno}}'; +{{/model}} +{{/models}} +{{#apiInfo}} +{{#apis}} + +{{#operations}} +import { {{classname}}RequestFactory, {{classname}}ResponseProcessor} from "../apis/{{classname}}{{extensionForDeno}}"; +{{#useInversify}} +import { Abstract{{classname}}RequestFactory, Abstract{{classname}}ResponseProcessor } from "../apis/{{classname}}.service{{extensionForDeno}}"; + +@injectable() +{{/useInversify}} +export class Observable{{classname}} { + {{#useInversify}} + private requestFactory: Abstract{{classname}}RequestFactory; + private responseProcessor: Abstract{{classname}}ResponseProcessor; + {{/useInversify}} + {{^useInversify}} + private requestFactory: {{classname}}RequestFactory; + private responseProcessor: {{classname}}ResponseProcessor; + {{/useInversify}} + private configuration: Configuration; + + public constructor( + {{#useInversify}} + @inject(AbstractConfiguration) configuration: Configuration, + @inject(Abstract{{classname}}RequestFactory) @optional() requestFactory?: Abstract{{classname}}RequestFactory, + @inject(Abstract{{classname}}ResponseProcessor) @optional() responseProcessor?: Abstract{{classname}}ResponseProcessor + {{/useInversify}} + {{^useInversify}} + configuration: Configuration, + requestFactory?: {{classname}}RequestFactory, + responseProcessor?: {{classname}}ResponseProcessor + {{/useInversify}} + ) { + this.configuration = configuration; + this.requestFactory = requestFactory || new {{classname}}RequestFactory(configuration); + this.responseProcessor = responseProcessor || new {{classname}}ResponseProcessor(); + } + +{{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{paramName}} {{description}} + {{/allParams}} + */ + public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + const requestContextPromise = this.requestFactory.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options); + + // build promise chain + let middlewarePreObservable = from_(requestContextPromise); + for (let middleware of this.configuration.middleware) { + middlewarePreObservable = middlewarePreObservable.pipe(mergeMap((ctx: RequestContext) => middleware.pre(ctx))); + } + + return middlewarePreObservable.pipe(mergeMap((ctx: RequestContext) => this.configuration.httpApi.send(ctx))). + pipe(mergeMap((response: ResponseContext) => { + let middlewarePostObservable = of(response); + for (let middleware of this.configuration.middleware) { + middlewarePostObservable = middlewarePostObservable.pipe(mergeMap((rsp: ResponseContext) => middleware.post(rsp))); + } + return middlewarePostObservable.pipe(map((rsp: ResponseContext) => this.responseProcessor.{{nickname}}(rsp))); + })); + } + +{{/operation}} +} +{{/operations}} +{{/apis}} +{{/apiInfo}} diff --git a/.generator/templates/types/PromiseAPI.mustache b/.generator/templates/types/PromiseAPI.mustache new file mode 100644 index 000000000000..4014dec1e068 --- /dev/null +++ b/.generator/templates/types/PromiseAPI.mustache @@ -0,0 +1,68 @@ +import { ResponseContext, RequestContext, HttpFile } from '../http/http{{extensionForDeno}}'; +import * as models from '../models/all{{extensionForDeno}}'; +import { Configuration} from '../configuration{{extensionForDeno}}' +{{#useInversify}} +import { injectable, inject, optional } from "inversify"; +import { AbstractConfiguration } from "../services/configuration"; +{{/useInversify}} + +{{#models}} +{{#model}} +import { {{{ classname }}} } from '../models/{{{ classFilename }}}{{extensionForDeno}}'; +{{/model}} +{{/models}} +{{#apiInfo}} +{{#apis}} +import { Observable{{classname}} } from './ObservableAPI{{extensionForDeno}}'; + +{{#operations}} +import { {{classname}}RequestFactory, {{classname}}ResponseProcessor} from "../apis/{{classname}}{{extensionForDeno}}"; +{{#useInversify}} +import { Abstract{{classname}}RequestFactory, Abstract{{classname}}ResponseProcessor } from "../apis/{{classname}}.service"; + +@injectable() +{{/useInversify}} +export class Promise{{classname}} { + private api: Observable{{classname}} + + public constructor( + {{#useInversify}} + @inject(AbstractConfiguration) configuration: Configuration, + @inject(Abstract{{classname}}RequestFactory) @optional() requestFactory?: Abstract{{classname}}RequestFactory, + @inject(Abstract{{classname}}ResponseProcessor) @optional() responseProcessor?: Abstract{{classname}}ResponseProcessor + {{/useInversify}} + {{^useInversify}} + configuration: Configuration, + requestFactory?: {{classname}}RequestFactory, + responseProcessor?: {{classname}}ResponseProcessor + {{/useInversify}} + ) { + this.api = new Observable{{classname}}(configuration, requestFactory, responseProcessor); + } + +{{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{paramName}} {{description}} + {{/allParams}} + */ + public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: Configuration): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + const result = this.api.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options); + return result.toPromise(); + } + +{{/operation}} + +} + +{{/operations}} + + +{{/apis}} +{{/apiInfo}} diff --git a/.generator/templates/util.mustache b/.generator/templates/util.mustache new file mode 100644 index 000000000000..07317f5828bc --- /dev/null +++ b/.generator/templates/util.mustache @@ -0,0 +1,28 @@ +/** + * Returns if a specific http code is in a given code range + * where the code range is defined as a combination of digits + * and "X" (the letter X) with a length of 3 + * + * @param codeRange string with length 3 consisting of digits and "X" (the letter X) + * @param code the http status code to be checked against the code range + */ +export function isCodeInRange(codeRange: string, code: number): boolean { + // This is how the default value is encoded in OAG + if (codeRange === "0") { + return true; + } + if (codeRange == code.toString()) { + return true; + } else { + const codeString = code.toString(); + if (codeString.length != codeRange.length) { + return false; + } + for (let i = 0; i < codeString.length; i++) { + if (codeRange.charAt(i) != "X" && codeRange.charAt(i) != codeString.charAt(i)) { + return false; + } + } + return true; + } +}