- Working on v2 service generation

This commit is contained in:
Ferdi Koomen 2019-11-05 17:49:24 +01:00
parent ec30f2852e
commit aec93386a5
60 changed files with 34516 additions and 246 deletions

View File

@ -5,9 +5,9 @@ export interface Model {
name: string;
base: string;
type: string;
template: string;
description: string | null;
extends: string | null;
template?: string;
description?: string;
extends: string[];
imports: string[];
properties: ModelProperty[];
enums: ModelEnum[];

View File

@ -1,7 +1,7 @@
import { ModelEnumProperty } from './ModelEnumProperty';
import { ModelEnumValue } from './ModelEnumValue';
export interface ModelEnum {
name: string;
value: string;
values: ModelEnumProperty[];
values: ModelEnumValue[];
}

View File

@ -1,5 +1,6 @@
export interface ModelEnumProperty {
export interface ModelEnumValue {
type: string;
name: string;
description?: string;
value: string | number;
}

View File

@ -1,8 +0,0 @@
import { ModelProperty } from './ModelProperty';
import { ModelEnum } from './ModelEnum';
export interface ModelProperties {
imports: string[];
properties: ModelProperty[];
enums: ModelEnum[];
}

View File

@ -2,9 +2,13 @@ export interface ModelProperty {
name: string;
type: string;
base: string;
template: string | null;
description: string | null;
template?: string;
description?: string;
default?: any;
required: boolean;
nullable: boolean;
readOnly: boolean;
extends: string[];
imports: string[];
properties: ModelProperty[];
}

View File

@ -1,5 +1,7 @@
import { ServiceOperation } from './ServiceOperation';
export interface Service {
name: string;
base: string;
imports: [];
operations: ServiceOperation[];
imports: string[];
}

View File

@ -0,0 +1,23 @@
import { ServiceOperationError } from './ServiceOperationError';
import { ServiceOperationParameter } from './ServiceOperationParameter';
import { Model } from './Model';
import { ServiceOperationResponse } from './ServiceOperationResponse';
export interface ServiceOperation {
name: string;
summary?: string;
description?: string;
deprecated?: boolean;
method: string;
path: string;
parameters: ServiceOperationParameter[];
parametersPath: ServiceOperationParameter[];
parametersQuery: ServiceOperationParameter[];
parametersForm: ServiceOperationParameter[];
parametersHeader: ServiceOperationParameter[];
parametersBody: ServiceOperationParameter | null;
models: Model[];
errors: ServiceOperationError[];
response: ServiceOperationResponse | null;
result: string;
}

View File

@ -0,0 +1,4 @@
export interface ServiceOperationError {
code: number;
text: string;
}

View File

@ -0,0 +1,13 @@
export interface ServiceOperationParameter {
name: string;
type: string;
base: string;
template: string;
description: string;
default?: any;
required: boolean;
nullable: boolean;
// extends: string[];
// imports: string[];
// properties: ModelProperty[];
}

View File

@ -0,0 +1,5 @@
export interface ServiceOperationResponse {
code: number;
text: string;
property: any;
}

View File

@ -20,7 +20,7 @@ export enum HttpClient {
/**
* Generate the OpenAPI client. This method will read the OpenAPI specification and based on the
* given language it will generate the client, including the types models, validation schemas,
* given language it will generate the client, including the typed models, validation schemas,
* service layer, etc.
* @param input The relative location of the OpenAPI spec.
* @param output The relative location of the output directory
@ -39,6 +39,8 @@ export function generate(input: string, output: string, language: Language = Lan
console.log(os.EOL);
try {
// Load the specification, read the OpenAPI version and load the
// handlebar templates for the given language
const openApi = getOpenApiSpec(inputPath);
const openApiVersion = getOpenApiVersion(openApi);
const templates = readHandlebarsTemplates(language);

View File

@ -5,6 +5,11 @@ import { getServices } from './parser/getServices';
import { getModels } from './parser/getModels';
import { getSchemas } from './parser/getSchemas';
/**
* Parse the OpenAPI specification to a Client model that contains
* all the models, services and schema's we should output.
* @param openApi The OpenAPI spec that we have loaded from disk.
*/
export function parse(openApi: OpenApi): Client {
return {
version: openApi.info.version,

View File

@ -21,6 +21,6 @@ export interface OpenApiHeader {
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
enum?: string[] | number[];
enum?: (string | number)[];
multipleOf?: number;
}

View File

@ -17,6 +17,6 @@ export interface OpenApiItems {
maxItems?: number;
minItems?: number;
uniqueItems?: number;
enum?: string[] | number[];
enum?: (string | number)[];
multipleOf?: number;
}

View File

@ -15,7 +15,7 @@ export interface OpenApiOperation {
operationId?: string;
consumes?: string[];
produces?: string[];
parameters?: OpenApiParameter[] | OpenApiReference[];
parameters?: (OpenApiParameter & OpenApiReference)[];
responses: OpenApiResponses;
schemes: ('http' | 'https' | 'ws' | 'wss')[];
deprecated?: boolean;

View File

@ -26,6 +26,6 @@ export interface OpenApiParameter {
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
enum?: string[] | number[];
enum?: (string | number)[];
multipleOf?: number;
}

View File

@ -6,7 +6,6 @@ import { OpenApiReference } from './OpenApiReference';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#pathItemObject
*/
export interface OpenApiPath {
$ref?: string;
get?: OpenApiOperation;
put?: OpenApiOperation;
post?: OpenApiOperation;
@ -14,5 +13,5 @@ export interface OpenApiPath {
options?: OpenApiOperation;
head?: OpenApiOperation;
patch?: OpenApiOperation;
parameters?: OpenApiParameter[] | OpenApiReference[];
parameters?: (OpenApiParameter & OpenApiReference)[];
}

View File

@ -2,5 +2,5 @@
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#referenceObject
*/
export interface OpenApiReference {
$ref: string;
$ref?: string;
}

View File

@ -5,7 +5,7 @@ import { OpenApiResponse } from './OpenApiResponse';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#responsesObject
*/
export interface OpenApiResponses {
[httpcode: string]: OpenApiResponse | OpenApiReference;
[httpcode: string]: OpenApiResponse & OpenApiReference;
default: OpenApiResponse | OpenApiReference;
default: OpenApiResponse & OpenApiReference;
}

View File

@ -7,7 +7,6 @@ import { OpenApiXml } from './OpenApiXml';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject
*/
export interface OpenApiSchema {
$ref?: string;
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
title?: string;
description?: string;
@ -26,12 +25,12 @@ export interface OpenApiSchema {
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: string[] | number[];
enum?: (string | number)[];
type?: string;
items?: OpenApiReference | OpenApiSchema;
allOf?: OpenApiReference[] | OpenApiSchema[];
properties?: Dictionary<OpenApiSchema>;
additionalProperties?: boolean | OpenApiReference | OpenApiSchema;
items?: OpenApiSchema & OpenApiReference;
allOf?: (OpenApiSchema & OpenApiReference)[];
properties?: Dictionary<OpenApiSchema & OpenApiReference>;
additionalProperties?: boolean | (OpenApiSchema & OpenApiReference);
discriminator?: string;
readOnly?: boolean;
xml?: OpenApiXml;

View File

@ -1,14 +1,100 @@
import { ModelProperty } from '../../../client/interfaces/ModelProperty';
import { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { OpenApiReference } from '../interfaces/OpenApiReference';
import { getType } from './getType';
export function parseModelProperty(): ModelProperty {
return {
name: '',
export function getModelProperty(name: string, schema: OpenApiSchema & OpenApiReference): ModelProperty {
/**
$ref?: string;
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
title?: string;
description?: string;
default?: any;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: boolean;
minimum?: number;
exclusiveMinimum?: boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: number;
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: string[] | number[];
type?: string;
items?: OpenApiReference | OpenApiSchema;
allOf?: OpenApiReference[] | OpenApiSchema[];
properties?: Dictionary<OpenApiSchema>;
additionalProperties?: boolean | OpenApiReference | OpenApiSchema;
discriminator?: string;
readOnly?: boolean;
xml?: OpenApiXml;
externalDocs?: OpenApiExternalDocs;
example?: any;
*/
const prop: ModelProperty = {
name: name,
type: '',
base: '',
template: '',
description: null,
description: schema.description,
default: schema.default,
required: false,
readOnly: false,
nullable: false,
readOnly: schema.readOnly || false,
imports: [],
extends: [],
properties: [],
};
if (schema.$ref) {
// console.log('parse $ref?');
}
if (schema.properties || schema.type === 'object') {
prop.type = 'interface';
// type is interface!?
}
if (schema.enum) {
prop.type = 'enum';
// type is enum!
}
console.log('propertyName:', schema);
console.log('format:', schema.format);
console.log('type:', schema.type);
const properties = schema.properties;
for (const propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
const property = properties[propertyName];
console.log('propertyName', propertyName);
// getModelProperty(propertyName, property);
}
}
if (schema.allOf) {
schema.allOf.forEach(parent => {
if (parent.$ref) {
const extend = getType(parent.$ref);
prop.extends.push(extend.type);
prop.imports.push(extend.base);
}
if (parent.properties) {
console.log(parent.properties);
// const properties: ParsedModelProperties = parseModelProperties(modelClass, definition.allOf[1].properties as SwaggerDefinitions, required);
// model.imports.push(...properties.imports);
// model.properties.push(...properties.properties);
// model.enums.push(...properties.enums);
}
});
}
return prop;
}

View File

@ -1,7 +1,6 @@
import { Model } from '../../../client/interfaces/Model';
import { OpenApi } from '../interfaces/OpenApi';
import { getType } from './getType';
import { getModelTemplate } from './getModelTemplate';
/**
* Parse and return the OpenAPI models.
@ -9,26 +8,40 @@ import { getModelTemplate } from './getModelTemplate';
*/
export function getModels(openApi: OpenApi): Map<string, Model> {
const models = new Map<string, Model>();
// Iterate over the definitions
const definitions = openApi.definitions;
for (const definitionName in definitions) {
if (definitions.hasOwnProperty(definitionName)) {
const definition = definitions[definitionName];
const required = definition.required || [];
const modelClass = getType(definitionName);
const modelTemplate: string = getModelTemplate(modelClass);
// Check if we haven't already parsed the model
if (!models.has(modelClass.base)) {
const model: Model = {
name: modelClass.base,
base: modelClass.base,
type: modelClass.type,
template: modelTemplate,
description: null,
extends: null,
imports: [],
properties: [],
enums: [],
};
models.set(modelClass.base, model);
// // Create a new model object
// const model: Model = {
// name: modelClass.base,
// base: modelClass.base,
// type: modelClass.type,
// template: getModelTemplate(modelClass),
// description: null,
// extends: [],
// imports: [],
// properties: [],
// enums: [],
// };
//
// const properties = definition.properties;
// for (const propertyName in properties) {
// if (properties.hasOwnProperty(propertyName)) {
// const property = properties[propertyName];
// const propertyRequired = required.includes(propertyName);
// getModelProperty(propertyName, property);
// }
// }
//
// models.set(modelClass.base, model);
}
}
}

View File

@ -0,0 +1,16 @@
import { ServiceOperationResponse } from '../../../client/interfaces/ServiceOperationResponse';
import { ServiceOperationError } from '../../../client/interfaces/ServiceOperationError';
/**
* Get list of service errors.
* @param responses List of parsed service responses.
* @returns List of service errors containing the error code and message.
*/
export function getServiceOperationErrors(responses: ServiceOperationResponse[]): ServiceOperationError[] {
return responses
.filter((response: ServiceOperationResponse): boolean => response.code >= 300 && response.text !== undefined && response.text !== '')
.map(response => ({
code: response.code,
text: response.text,
}));
}

View File

@ -0,0 +1,10 @@
import { getServiceOperationPath } from './getServiceOperationPath';
describe('getServiceOperationPath', () => {
it('should produce correct result', () => {
expect(getServiceOperationPath('/api/v{api-version}/list/{id}/{type}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}/${type}');
expect(getServiceOperationPath('/api/v{api-version}/list/{id}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}');
expect(getServiceOperationPath('/api/v1/list/{id}')).toEqual('/api/v1/list/${id}');
expect(getServiceOperationPath('/api/v1/list')).toEqual('/api/v1/list');
});
});

View File

@ -4,6 +4,6 @@
* OpenAPI version without the need to hardcode this in the URL.
* @param path
*/
export function getServicePath(path: string): string {
export function getServiceOperationPath(path: string): string {
return path.replace(/{api-version}/g, '{OpenAPI.VERSION}').replace(/\{(.*?)\}/g, '${$1}');
}

View File

@ -0,0 +1,16 @@
export function getServiceOperationResponsesCode(value: string | 'default'): number | null {
// You can specify a "default" response, this is treated as HTTP code 200
if (value === 'default') {
return 200;
}
// Check if we can parse the code and return of successful.
if (/[0-9]+/g.test(value)) {
const code = parseInt(value);
if (Number.isInteger(code)) {
return code;
}
}
return null;
}

View File

@ -0,0 +1,35 @@
import { OpenApiResponses } from '../interfaces/OpenApiResponses';
import { ServiceOperationResponse } from '../../../client/interfaces/ServiceOperationResponse';
import { getServiceOperationResponsesCode } from './getServiceOperationResponseCode';
/**
* Parse the service response object into a list with status codes and response messages.
* @param responses Swagger responses.
* @returns List of status codes and response messages.
*/
export function getServiceOperationResponses(responses: OpenApiResponses): ServiceOperationResponse[] {
const result: ServiceOperationResponse[] = [];
// Iterate over each response code.
for (const code in responses) {
if (responses.hasOwnProperty(code)) {
// Get the status code and response message (if any).
const response = responses[code];
const responseCode = getServiceOperationResponsesCode(code);
const responseText = response.description || '';
if (responseCode) {
result.push({
code: responseCode,
text: responseText,
property: undefined,
});
}
}
}
// Sort the responses to 2XX success codes come before 4XX and 5XX error codes.
return result.sort((a, b): number => {
return a.code < b.code ? -1 : a.code > b.code ? 1 : 0;
});
}

View File

@ -0,0 +1,29 @@
import { ServiceOperationResponse } from '../../../client/interfaces/ServiceOperationResponse';
export interface ServiceOperationResult {
type: string;
imports: string[];
}
/**
* Parse service result.
* @param responses List of service responses.
* @returns Object containing the result type and needed imports.
*/
export function getServiceOperationResult(responses: ServiceOperationResponse[]): ServiceOperationResult {
let resultType = 'any';
let resultImports: string[] = [];
// Fetch the first valid (2XX range) response code and return that type.
const result = responses.find(response => response.code && response.code >= 200 && response.code < 300 && response.property);
if (result) {
resultType = result.property.type;
resultImports = [...result.property.imports];
}
return {
type: resultType,
imports: resultImports,
};
}

View File

@ -1,10 +0,0 @@
import { getServicePath } from './getServicePath';
describe('getServicePath', () => {
it('should produce correct result', () => {
expect(getServicePath('/api/v{api-version}/list/{id}/{type}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}/${type}');
expect(getServicePath('/api/v{api-version}/list/{id}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}');
expect(getServicePath('/api/v1/list/{id}')).toEqual('/api/v1/list/${id}');
expect(getServicePath('/api/v1/list')).toEqual('/api/v1/list');
});
});

View File

@ -1,5 +1,62 @@
import { Service } from '../../../client/interfaces/Service';
import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiOperation } from '../interfaces/OpenApiOperation';
import { getServiceClassName } from './getServiceClassName';
import { getServiceOperationName } from './getServiceOperationName';
import { getServiceOperationPath } from './getServiceOperationPath';
import { ServiceOperation } from '../../../client/interfaces/ServiceOperation';
import { getServiceOperationResponses } from './getServiceOperationResponses';
import { getServiceOperationResult } from './getServiceOperationResult';
import { getServiceOperationErrors } from './getServiceOperationErrors';
function getMethod(url: string, services: Map<string, Service>, op: OpenApiOperation, method: string): void {
const serviceName = (op.tags && op.tags[0]) || 'Service';
const serviceClassName: string = getServiceClassName(serviceName);
const serviceOperationNameFallback = `${method}${serviceClassName}`;
const serviceOperationName: string = getServiceOperationName(op.operationId || serviceOperationNameFallback);
const servicePath: string = getServiceOperationPath(url);
// If we have already declared a service, then we should fetch that and
// append the new method to it. Otherwise we should create a new service object.
const service =
services.get(serviceClassName) ||
({
name: serviceClassName,
imports: [],
operations: [],
} as Service);
// Create a new operation object for this method.
const operation: ServiceOperation = {
name: serviceOperationName,
summary: op.summary,
description: op.description,
deprecated: op.deprecated,
method: method,
path: servicePath,
parameters: [],
parametersPath: [],
parametersQuery: [],
parametersForm: [],
parametersHeader: [],
parametersBody: null,
models: [],
errors: [],
response: null,
result: 'any',
};
if (op.responses) {
const responses = getServiceOperationResponses(op.responses);
const result = getServiceOperationResult(responses);
operation.errors = getServiceOperationErrors(responses);
operation.result = result.type;
service.imports.push(...result.imports);
}
service.operations.push(operation);
services.set(serviceClassName, service);
}
/**
* Parse and return the OpenAPI services.
@ -7,29 +64,15 @@ import { OpenApi } from '../interfaces/OpenApi';
*/
export function getServices(openApi: OpenApi): Map<string, Service> {
const services = new Map<string, Service>();
const paths = openApi.paths;
for (const url in paths) {
if (paths.hasOwnProperty(url)) {
const path = paths[url];
for (const method in path) {
if (path.hasOwnProperty(method)) {
switch (method) {
case 'get':
case 'put':
case 'post':
case 'delete':
case 'options':
case 'head':
case 'patch':
const op = path[method];
if (op) {
//
}
break;
}
}
}
}
}
Object.keys(openApi.paths).forEach(url => {
const path = openApi.paths[url];
path.get && getMethod(url, services, path.get, 'get');
path.put && getMethod(url, services, path.put, 'put');
path.post && getMethod(url, services, path.post, 'post');
path.delete && getMethod(url, services, path.delete, 'delete');
path.options && getMethod(url, services, path.options, 'options');
path.head && getMethod(url, services, path.head, 'head');
path.patch && getMethod(url, services, path.patch, 'patch');
});
return services;
}

View File

@ -5,6 +5,11 @@ import { getModels } from './parser/getModels';
import { getServices } from './parser/getServices';
import { getSchemas } from './parser/getSchemas';
/**
* Parse the OpenAPI specification to a Client model that contains
* all the models, services and schema's we should output.
* @param openApi The OpenAPI spec that we have loaded from disk.
*/
export function parse(openApi: OpenApi): Client {
return {
version: openApi.info.version,

View File

@ -14,13 +14,13 @@ import { OpenApiSecurityScheme } from './OpenApiSecurityScheme';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#componentsObject
*/
export interface OpenApiComponents {
schemas?: Dictionary<OpenApiSchema | OpenApiReference>;
responses?: Dictionary<OpenApiResponses | OpenApiReference>;
parameters?: Dictionary<OpenApiParameter | OpenApiReference>;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
requestBodies?: Dictionary<OpenApiRequestBody | OpenApiReference>;
headers?: Dictionary<OpenApiHeader | OpenApiReference>;
securitySchemes: Dictionary<OpenApiSecurityScheme | OpenApiReference>;
links?: Dictionary<OpenApiLink | OpenApiReference>;
callbacks?: Dictionary<OpenApiCallback | OpenApiReference>;
schemas?: Dictionary<OpenApiSchema & OpenApiReference>;
responses?: Dictionary<OpenApiResponses & OpenApiReference>;
parameters?: Dictionary<OpenApiParameter & OpenApiReference>;
examples?: Dictionary<OpenApiExample & OpenApiReference>;
requestBodies?: Dictionary<OpenApiRequestBody & OpenApiReference>;
headers?: Dictionary<OpenApiHeader & OpenApiReference>;
securitySchemes: Dictionary<OpenApiSecurityScheme & OpenApiReference>;
links?: Dictionary<OpenApiLink & OpenApiReference>;
callbacks?: Dictionary<OpenApiCallback & OpenApiReference>;
}

View File

@ -7,7 +7,7 @@ import { OpenApiReference } from './OpenApiReference';
*/
export interface OpenApiEncoding {
contentType?: string;
headers?: Dictionary<OpenApiHeader | OpenApiReference>;
headers?: Dictionary<OpenApiHeader & OpenApiReference>;
style?: string;
explode?: boolean;
allowReserved?: boolean;

View File

@ -14,7 +14,7 @@ export interface OpenApiHeader {
style?: string;
explode?: boolean;
allowReserved?: boolean;
schema?: OpenApiSchema | OpenApiReference;
schema?: OpenApiSchema & OpenApiReference;
example?: any;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
examples?: Dictionary<OpenApiExample & OpenApiReference>;
}

View File

@ -8,8 +8,8 @@ import { OpenApiSchema } from './OpenApiSchema';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#mediaTypeObject
*/
export interface OpenApiMediaType {
schema?: OpenApiSchema | OpenApiReference;
schema?: OpenApiSchema & OpenApiReference;
example?: any;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
examples?: Dictionary<OpenApiExample & OpenApiReference>;
encoding?: Dictionary<OpenApiEncoding>;
}

View File

@ -17,10 +17,10 @@ export interface OpenApiOperation {
description?: string;
externalDocs?: OpenApiExternalDocs;
operationId?: string;
parameters?: OpenApiParameter[] | OpenApiReference[];
requestBody?: OpenApiRequestBody | OpenApiReference;
parameters?: (OpenApiParameter & OpenApiReference)[];
requestBody?: OpenApiRequestBody & OpenApiReference;
responses: OpenApiResponses;
callbacks?: Dictionary<OpenApiCallback | OpenApiReference>;
callbacks?: Dictionary<OpenApiCallback & OpenApiReference>;
deprecated?: boolean;
security?: OpenApiSecurityRequirement[];
servers?: OpenApiServer[];

View File

@ -16,7 +16,7 @@ export interface OpenApiParameter {
style?: string;
explode?: boolean;
allowReserved?: boolean;
schema?: OpenApiSchema | OpenApiReference;
schema?: OpenApiSchema & OpenApiReference;
example?: any;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
examples?: Dictionary<OpenApiExample & OpenApiReference>;
}

View File

@ -7,7 +7,6 @@ import { OpenApiServer } from './OpenApiServer';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#pathItemObject
*/
export interface OpenApiPath {
$ref?: string;
summary?: string;
description?: string;
get?: OpenApiOperation;
@ -19,5 +18,5 @@ export interface OpenApiPath {
patch?: OpenApiOperation;
trace?: OpenApiOperation;
servers?: OpenApiServer[];
parameters?: OpenApiParameter[] | OpenApiReference[];
parameters?: (OpenApiParameter & OpenApiReference)[];
}

View File

@ -2,5 +2,5 @@
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#referenceObject
*/
export interface OpenApiReference {
$ref: string;
$ref?: string;
}

View File

@ -7,6 +7,6 @@ import { OpenApiReference } from './OpenApiReference';
*/
export interface OpenApiRequestBody {
description?: string;
content: Dictionary<OpenApiMediaType | OpenApiReference>;
content: Dictionary<OpenApiMediaType & OpenApiReference>;
required?: boolean;
}

View File

@ -9,7 +9,7 @@ import { OpenApiReference } from './OpenApiReference';
*/
export interface OpenApiResponse {
description: string;
headers?: Dictionary<OpenApiHeader | OpenApiReference>;
headers?: Dictionary<OpenApiHeader & OpenApiReference>;
content?: Dictionary<OpenApiMediaType>;
links?: Dictionary<OpenApiLink | OpenApiReference>;
links?: Dictionary<OpenApiLink & OpenApiReference>;
}

View File

@ -5,7 +5,7 @@ import { OpenApiResponse } from './OpenApiResponse';
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject
*/
export interface OpenApiResponses {
[httpcode: string]: OpenApiResponse | OpenApiReference;
[httpcode: string]: OpenApiResponse & OpenApiReference;
default: OpenApiResponse | OpenApiReference;
default: OpenApiResponse & OpenApiReference;
}

View File

@ -23,15 +23,15 @@ export interface OpenApiSchema {
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: string[] | number[];
enum?: (string | number)[];
type?: string;
allOf?: OpenApiReference[] | OpenApiSchema[];
oneOf?: OpenApiReference[] | OpenApiSchema[];
anyOf?: OpenApiReference[] | OpenApiSchema[];
not?: OpenApiReference[] | OpenApiSchema[];
items?: OpenApiReference | OpenApiSchema;
properties?: Dictionary<OpenApiSchema>;
additionalProperties?: boolean | OpenApiReference | OpenApiSchema;
allOf?: (OpenApiSchema & OpenApiReference)[];
oneOf?: (OpenApiSchema & OpenApiReference)[];
anyOf?: (OpenApiSchema & OpenApiReference)[];
not?: (OpenApiSchema & OpenApiReference)[];
items?: OpenApiSchema & OpenApiReference;
properties?: Dictionary<OpenApiSchema & OpenApiReference>;
additionalProperties?: boolean | (OpenApiSchema & OpenApiReference);
description?: string;
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
default?: any;

View File

@ -18,6 +18,6 @@ export { {{{basename}}} } from './schemas/{{{basename}}}';
{{#if services}}
{{#each services}}
export { {{{basename}}} } from './services/{{{basename}}}';
export { {{{name}}} } from './services/{{{name}}}';
{{/each}}
{{/if}}

View File

@ -1,11 +1,22 @@
/* istanbul ignore file */
/* eslint-disable */
{{#if enums}}
{{#if imports}}
{{#each imports}}
import { {{{this}}} } from '../models/{{{this}}}';
{{/each}}
{{/if}}
import * as yup from 'yup';
export let {{{base}}};
(function ({{{base}}}) {
{{#each enums}}
{{#if description}}
/**
* {{{description}}}
*/
{{/if}}
{{{../base}}}.{{{name}}} = {
{{#each values}}
{{{name}}}: {{{value}}},
@ -13,5 +24,17 @@ export let {{{base}}};
};
{{/each}}
{{{../base}}}.schema = yup.object().shape({
// Add properties
};
{{{../base}}}.validate = function(value) {
return schema.validate(value, { strict: true });
};
{{{../base}}}.validateSync = function(value) {
return schema.validateSync(value, { strict: true });
};
})({{{base}}} || ({{{base}}} = {}));
{{/if}}

View File

@ -1,2 +1,70 @@
/* istanbul ignore file */
/* eslint-disable */
import * as yup from 'yup';
export class {{{name}}} {
{{#each operations}}
/**
{{#if deprecated}}
* @deprecated
{{/if}}
{{#if summary}}
* {{{summary}}}
{{/if}}
{{#if description}}
* {{{description}}}
{{/if}}
{{#if parameters}}
{{#each parameters}}
* @param {{{name}}} {{{description}}}
{{/each}}
{{/if}}
*/
static async {{{name}}}({{#each parameters}}{{{name}}}{{#unless required}}?{{/unless}}{{#unless @last}}, {{/unless}}{{/each}}) {
{{#if parameters}}
{{#each parameters}}
yup.schema.validate();
isValidRequiredParam({{{name}}}, '{{{name}}}');
{{/each}}
{{/if}}
const result = await request({
method: '{{{method}}}',
path: `{{{path}}}`,{{#if parametersHeader}}
headers: {
{{#each parametersHeader}}
'{{{prop}}}': {{{name}}},
{{/each}}
},{{/if}}{{#if parametersQuery}}
query: {
{{#each parametersQuery}}
'{{{prop}}}': {{{name}}},
{{/each}}
},{{/if}}{{#if parametersForm}}
formData: {
{{#each parametersForm}}
'{{{prop}}}': {{{name}}},
{{/each}}
},{{/if}}{{#if parametersBody}}
body: {{{parametersBody.name}}},{{/if}}
});
{{#if errors}}
if (!result.ok) {
switch (result.status) {
{{#each errors}}
case {{{code}}}: throw new ApiError(result, `{{{text}}}`);
{{/each}}
}
}
{{/if}}
catchGenericError(result);
return result.body;
}
{{/each}}
}

View File

@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
const server: string = '{{{server}}}';
const version: string = '{{{version}}}';
const server = '{{{server}}}';
const version = '{{{version}}}';
{{#if models}}
{{#each models}}
@ -19,6 +19,6 @@ export { {{{base}}} } from './schemas/{{{base}}}';
{{#if services}}
{{#each services}}
export { {{{base}}} } from './services/{{{base}}}';
export { {{{name}}} } from './services/{{{name}}}';
{{/each}}
{{/if}}

View File

@ -1,23 +1,38 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
{{#if imports}}
{{#if imports}}
{{#each imports}}
import { {{{this}}} } from '../models/{{{this}}}';
{{/each}}
{{/if}}
import * as yup from 'yup';
{{#if description}}
/**
* {{{description}}}
*/
{{/if}}
export interface {{{base}}}{{{template}}}{{#if extend}} extends {{{extend}}}{{/if}} {
{{#each properties}}
{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{{type}}};
{{#if description}}
/**
* {{{description}}}
*/
{{/if}}
{{#if readOnly}}readonly {{/if}}{{{name}}}{{#unless required}}?{{/unless}}: {{{type}}}{{#if nullable}} | null{{/if}};
{{/each}}
}
{{#if enums}}
export namespace {{{base}}} {
{{#each enums}}
{{#if description}}
/**
* {{{description}}}
*/
{{/if}}
export enum {{{name}}} {
{{#each values}}
{{{name}}} = {{{value}}},
@ -25,5 +40,16 @@ export namespace {{{base}}} {
};
{{/each}}
export const schema = yup.object<{{{base}}}{{{template}}}>().shape({
// Add properties
});
export function validate(value: {{{base}}}{{{template}}}): Promise<{{{base}}}{{{template}}}> {
return schema.validate(value, { strict: true });
}
export function validateSync(value: {{{base}}}{{{template}}}): {{{base}}}{{{template}}} {
return schema.validateSync(value, { strict: true });
}
}
{{/if}}

View File

@ -1,3 +1,71 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import * as yup from 'yup';
export class {{{name}}} {
{{#each operations}}
/**
{{#if deprecated}}
* @deprecated
{{/if}}
{{#if summary}}
* {{{summary}}}
{{/if}}
{{#if description}}
* {{{description}}}
{{/if}}
{{#if parameters}}
{{#each parameters}}
* @param {{{name}}} {{{description}}}
{{/each}}
{{/if}}
*/
public static async {{{name}}}({{#each parameters}}{{{name}}}{{#unless required}}?{{/unless}}: {{{type}}}{{#if nullable}} | null{{/if}}{{#unless @last}}, {{/unless}}{{/each}}): Promise<{{{result}}}> {
{{#if parameters}}
{{#each parameters}}
yup.schema.validate();
isValidRequiredParam({{{name}}}, '{{{name}}}');
{{/each}}
{{/if}}
const result = await request<{{{result}}}>({
method: '{{{method}}}',
path: `{{{path}}}`,{{#if parametersHeader}}
headers: {
{{#each parametersHeader}}
'{{{prop}}}': {{{name}}},
{{/each}}
},{{/if}}{{#if parametersQuery}}
query: {
{{#each parametersQuery}}
'{{{prop}}}': {{{name}}},
{{/each}}
},{{/if}}{{#if parametersForm}}
formData: {
{{#each parametersForm}}
'{{{prop}}}': {{{name}}},
{{/each}}
},{{/if}}{{#if parametersBody}}
body: {{{parametersBody.name}}},{{/if}}
});
{{#if errors}}
if (!result.ok) {
switch (result.status) {
{{#each errors}}
case {{{code}}}: throw new ApiError(result, `{{{text}}}`);
{{/each}}
}
}
{{/if}}
catchGenericError(result);
return result.body;
}
{{/each}}
}

View File

@ -1,5 +1,10 @@
import { Language } from '../index';
/**
* Get the correct file name and extension for a given language.
* @param fileName Any file name.
* @param language Typescript or Javascript.
*/
export function getFileName(fileName: string, language: Language): string {
switch (language) {
case Language.TYPESCRIPT:

View File

@ -9,8 +9,7 @@ describe('getSortedModels', () => {
base: 'John',
type: '',
template: '',
description: null,
extends: null,
extends: [],
imports: [],
properties: [],
enums: [],
@ -20,8 +19,7 @@ describe('getSortedModels', () => {
base: 'Jane',
type: '',
template: '',
description: null,
extends: null,
extends: [],
imports: [],
properties: [],
enums: [],
@ -31,8 +29,7 @@ describe('getSortedModels', () => {
base: 'Doe',
type: '',
template: '',
description: null,
extends: null,
extends: [],
imports: [],
properties: [],
enums: [],

View File

@ -7,8 +7,8 @@ import { Service } from '../client/interfaces/Service';
export function getSortedServices(services: Map<string, Service>): Service[] {
return (
Array.from(services.values()).sort((a, b) => {
const nameA = a.base.toLowerCase();
const nameB = b.base.toLowerCase();
const nameA = a.name.toLowerCase();
const nameB = b.name.toLowerCase();
return nameA.localeCompare(nameB, 'en');
}) || []
);

View File

@ -25,12 +25,14 @@ export function writeClient(client: Client, language: Language, templates: Templ
const outputPathSchemas = path.resolve(outputPath, 'schemas');
const outputPathServices = path.resolve(outputPath, 'services');
// Clean output directory
try {
rimraf.sync(outputPath);
} catch (e) {
throw new Error(`Could not clean output directory`);
}
// Create new directories
try {
mkdirp.sync(outputPath);
mkdirp.sync(outputPathCore);
@ -41,6 +43,7 @@ export function writeClient(client: Client, language: Language, templates: Templ
throw new Error(`Could not create output directories`);
}
// Write the client files
try {
writeClientIndex(client, language, templates.index, outputPath);
writeClientModels(getSortedModels(client.models), language, templates.model, outputPathModels);

View File

@ -6,43 +6,50 @@ const OpenAPI = require('../dist');
OpenAPI.generate(
'./test/mock/v2/test-petstore.json',
'./test/tmp/v2/test-petstore',
'./test/tmp/v2/ts/test-petstore',
OpenAPI.Language.TYPESCRIPT,
OpenAPI.HttpClient.FETCH,
);
OpenAPI.generate(
'./test/mock/v2/test-petstore.yaml',
'./test/tmp/v2/test-petstore-yaml',
OpenAPI.Language.TYPESCRIPT,
OpenAPI.HttpClient.FETCH,
);
OpenAPI.generate(
'./test/mock/v3/test-petstore.json',
'./test/tmp/v3/test-petstore-json',
OpenAPI.Language.TYPESCRIPT,
OpenAPI.HttpClient.FETCH
);
OpenAPI.generate(
'./test/mock/v3/test-petstore.yaml',
'./test/tmp/v3/test-petstore-yaml',
OpenAPI.Language.TYPESCRIPT,
OpenAPI.HttpClient.FETCH,
);
OpenAPI.generate(
'./test/mock/v3/test-uspto.json',
'./test/tmp/v3/test-uspto',
OpenAPI.Language.TYPESCRIPT,
OpenAPI.HttpClient.FETCH,
);
OpenAPI.generate(
'./test/mock/v3/test-with-examples.json',
'./test/tmp/v3/test-with-examples',
OpenAPI.Language.TYPESCRIPT,
'./test/mock/v2/test-petstore.json',
'./test/tmp/v2/js/test-petstore',
OpenAPI.Language.JAVASCRIPT,
OpenAPI.HttpClient.FETCH,
);
// OpenAPI.generate(
// './test/mock/v2/test-petstore.yaml',
// './test/tmp/v2/test-petstore-yaml',
// OpenAPI.Language.TYPESCRIPT,
// OpenAPI.HttpClient.FETCH,
// );
//
// OpenAPI.generate(
// './test/mock/v3/test-petstore.json',
// './test/tmp/v3/test-petstore-json',
// OpenAPI.Language.TYPESCRIPT,
// OpenAPI.HttpClient.FETCH
// );
//
// OpenAPI.generate(
// './test/mock/v3/test-petstore.yaml',
// './test/tmp/v3/test-petstore-yaml',
// OpenAPI.Language.TYPESCRIPT,
// OpenAPI.HttpClient.FETCH,
// );
//
// OpenAPI.generate(
// './test/mock/v3/test-uspto.json',
// './test/tmp/v3/test-uspto',
// OpenAPI.Language.TYPESCRIPT,
// OpenAPI.HttpClient.FETCH,
// );
//
// OpenAPI.generate(
// './test/mock/v3/test-with-examples.json',
// './test/tmp/v3/test-with-examples',
// OpenAPI.Language.TYPESCRIPT,
// OpenAPI.HttpClient.FETCH,
// );
//

View File

@ -0,0 +1,954 @@
{
"x-generator": "NSwag v12.3.1.0 (NJsonSchema v9.14.1.0 (Newtonsoft.Json v12.0.0.0))",
"swagger": "2.0",
"info": {
"title": "Add-on Service API",
"version": "v1"
},
"host": "10.91.90.47:83",
"schemes": [
"http"
],
"paths": {
"/addon/api/v1/addons": {
"get": {
"tags": [
"Addons"
],
"summary": "Gets the list of addons.",
"operationId": "GetAll",
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Addon"
}
}
}
}
},
"post": {
"tags": [
"Addons"
],
"summary": "Upload add-ons' zip files. Creates (or updates if exists) add-on.",
"operationId": "Upload",
"consumes": [
"multipart/form-data"
],
"parameters": [
{
"type": "file",
"name": "file",
"in": "formData",
"required": true,
"description": "FileStream of the uploading file."
}
],
"responses": {
"201": {
"x-nullable": true,
"description": "UploadResult",
"schema": {
"$ref": "#/definitions/UploadResult"
}
},
"202": {
"x-nullable": true,
"description": "UploadResult",
"schema": {
"$ref": "#/definitions/UploadResult"
}
}
}
}
},
"/addon/api/v1/addons/{id}": {
"put": {
"tags": [
"Addons"
],
"summary": "Upload addons' zip files. Updates existing add-on.",
"operationId": "Update",
"consumes": [
"multipart/form-data"
],
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The add-on id.",
"format": "int32",
"x-nullable": false
},
{
"type": "file",
"name": "file",
"in": "formData",
"required": true,
"description": "FileStream of the uploading file."
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "UploadResult.",
"schema": {
"$ref": "#/definitions/UploadResult"
}
}
}
},
"get": {
"tags": [
"Addons"
],
"summary": "Gets the addon metadata by the specified id.",
"operationId": "Get",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "The addon metadata.",
"schema": {
"$ref": "#/definitions/Addon"
}
}
}
},
"delete": {
"tags": [
"Addons"
],
"summary": "Deletes the addon with a specified id.",
"operationId": "Delete",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "file"
}
}
}
}
},
"/addon/api/v1/addons/{id}/status": {
"post": {
"tags": [
"Addons"
],
"summary": "Updates the addon status.",
"operationId": "UpdateAddonStatus",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
},
{
"name": "statusReport",
"in": "body",
"required": true,
"description": "The status report.",
"schema": {
"$ref": "#/definitions/StatusReport"
},
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "file"
}
}
}
},
"delete": {
"tags": [
"Addons"
],
"summary": "Deletes the addon status.",
"operationId": "DeleteAddonStatus",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "file"
}
}
}
}
},
"/addon/api/v1/addons/{id}/download": {
"get": {
"tags": [
"Addons"
],
"summary": "Downloads addon by the specified id.",
"description": "When HEAD request is used the method only checks if the addon exists and returns the empty file.",
"operationId": "Download",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "Binary addon.",
"schema": {
"type": "file"
}
}
}
},
"head": {
"tags": [
"Addons"
],
"summary": "Downloads addon by the specified id.",
"description": "When HEAD request is used the method only checks if the addon exists and returns the empty file.",
"operationId": "Fetch",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "Binary addon.",
"schema": {
"type": "file"
}
}
}
}
},
"/addon/api/v1/addons/{id}/downloadicon": {
"get": {
"tags": [
"Addons"
],
"summary": "Downloads Addon Icon by the specified id.",
"operationId": "DownloadIcon",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "Addon icon stream.",
"schema": {
"type": "file"
}
}
}
}
},
"/addon/api/v1/addons/{id}/configurations": {
"put": {
"tags": [
"Configurations"
],
"summary": "Upload addon configuration files. Extracts required fields from the containing manifest file.",
"operationId": "UploadConfiguration",
"consumes": [
"multipart/form-data"
],
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
},
{
"type": "file",
"name": "file",
"in": "formData",
"required": true,
"description": "FileStream of the uploading file."
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "The updated addon.",
"schema": {
"$ref": "#/definitions/UploadResult"
}
}
}
},
"delete": {
"tags": [
"Configurations"
],
"summary": "Deletes the addon configuration with a specified id.",
"operationId": "ConfigurationDelete",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "file"
}
}
}
},
"get": {
"tags": [
"Configurations"
],
"summary": "Downloads addon configuration by the specified id.",
"description": "When HEAD request is used the method only checks if the addon exists and returns the empty file.",
"operationId": "DownloadAddonConfiguration",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The addon id.",
"format": "int32",
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "Binary addon configuration.",
"schema": {
"type": "file"
}
}
}
}
},
"/addon/api/v1/extensions/{id}/status": {
"post": {
"tags": [
"Extensions"
],
"summary": "Updates the extension status.",
"operationId": "UpdateExtensionStatus",
"consumes": [
"application/json-patch+json",
"application/json",
"text/json",
"application/*+json"
],
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The extension id.",
"format": "int32",
"x-nullable": false
},
{
"name": "statusReport",
"in": "body",
"required": true,
"description": "The status report.",
"schema": {
"$ref": "#/definitions/StatusReport"
},
"x-nullable": false
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "file"
}
}
}
},
"delete": {
"tags": [
"Extensions"
],
"summary": "Deletes the extension status.",
"operationId": "DeleteExtensionStatus",
"parameters": [
{
"type": "integer",
"name": "id",
"in": "path",
"required": true,
"description": "The extension id.",
"format": "int32",
"x-nullable": false
},
{
"type": "string",
"name": "clientId",
"in": "query",
"description": "The client id. If it is specified, extension status for that client will be deleted.\n If not specified then extension status will be deleted for all clients.",
"x-nullable": true
}
],
"responses": {
"200": {
"x-nullable": true,
"description": "",
"schema": {
"type": "file"
}
}
}
}
},
"/addon/api/v1/health": {
"get": {
"tags": [
"Health"
],
"summary": "Checks whether the service is up and running",
"operationId": "Health",
"responses": {
"200": {
"x-nullable": true,
"description": "Ok - in success case, otherwise return the error description.",
"schema": {
"type": "file"
}
}
}
}
},
"/addon/api/v1/report/heartbeat": {
"post": {
"tags": [
"Report"
],
"summary": "Receives heartbeat from all clients.",
"description": "This heartbeat is used to calculate the aggregated status of the Addons.\nIf the service does not receive heartbeat for a certain while,\nall reported status are ignored during status calculation.\nAfter a longer period those ignored status reports are cleaned out.\nBoth these values are configurable in the service.",
"operationId": "Report_Heartbeat",
"parameters": [
{
"type": "string",
"name": "environment",
"in": "query",
"x-nullable": true
},
{
"type": "string",
"name": "clientIdentity",
"in": "query",
"x-nullable": true
},
{
"type": "string",
"name": "process",
"in": "query",
"x-nullable": true
}
],
"responses": {
"204": {
"description": ""
}
}
}
}
},
"definitions": {
"Addon": {
"type": "object",
"additionalProperties": false,
"required": [
"Id",
"RequireConfiguration",
"Enabled",
"Status"
],
"properties": {
"Id": {
"type": "integer",
"format": "int32"
},
"ManifestVersion": {
"type": "string",
"description": "Gets the manifest version."
},
"PackageId": {
"type": "string",
"description": "Gets the unique identifier of the add-on."
},
"Name": {
"type": "string",
"description": "Gets the descriptive name of add-on."
},
"Description": {
"type": "string",
"description": "Gets the description of add-on."
},
"Author": {
"type": "string",
"description": "Gets the Author of add-on."
},
"MinVersion": {
"type": "string",
"description": "Gets the minimum SDL Tridion Sites version this extension supports."
},
"MaxVersion": {
"type": "string",
"description": "Gets the maximum SDL Tridion Sites version this extension supports."
},
"Version": {
"type": "string",
"description": "Gets the version of this add-on."
},
"Icon": {
"type": "string",
"description": "Gets the add-on icon."
},
"RequireConfiguration": {
"description": "Describes whether the custom configuration can be uploaded for the addon.",
"$ref": "#/definitions/RequireConfiguration"
},
"Dependencies": {
"type": "array",
"description": "Gets the collection of add-on dependencies.",
"items": {
"$ref": "#/definitions/Dependency"
}
},
"Extensions": {
"type": "array",
"description": "Gets the list of extensions this add-on contains.",
"items": {
"$ref": "#/definitions/Extension"
}
},
"Enabled": {
"type": "boolean"
},
"Status": {
"$ref": "#/definitions/DeploymentStatus"
},
"PackageHash": {
"type": "string"
},
"UploadedAt": {
"type": "string",
"format": "date-time"
},
"DownloadUri": {
"type": "string",
"format": "uri"
},
"StatusReports": {
"type": "array",
"items": {
"$ref": "#/definitions/StatusReport"
}
},
"DownloadIconUri": {
"type": "string",
"format": "uri"
},
"DownloadConfigurationUri": {
"type": "string",
"format": "uri"
},
"Configuration": {
"$ref": "#/definitions/Configuration"
}
}
},
"RequireConfiguration": {
"type": "string",
"description": "Represents whether the Addon requires configuration or not",
"x-enumNames": [
"Optional",
"Yes",
"No"
],
"enum": [
"Optional",
"Yes",
"No"
],
"x-ms-enum": {
"name": "RequireConfiguration",
"modelAsString": false
}
},
"Dependency": {
"type": "object",
"description": "Represents add-on dependencies from the add-on manifest file.",
"additionalProperties": false,
"properties": {
"Id": {
"type": "string",
"description": "Gets the name of dependency package"
},
"Version": {
"type": "string",
"description": "Gets the version of dependency package"
}
}
},
"Extension": {
"type": "object",
"description": "Represents extension information from the add-on manifest file.",
"additionalProperties": false,
"required": [
"Id",
"Status"
],
"properties": {
"Id": {
"type": "integer",
"description": "Add-on service generated ID.",
"format": "int32"
},
"Name": {
"type": "string",
"description": "Gets the extension name"
},
"SupportedVersions": {
"type": "string",
"description": "Gets the SDL Tridion Sites component versions this extension support"
},
"Type": {
"type": "string",
"description": "Gets the identifier of the extension point that this extension extends."
},
"properties": {
"description": "Gets the custom properties associated with this extension as raw json."
},
"StatusReports": {
"type": "array",
"description": "List of status reports.",
"items": {
"$ref": "#/definitions/StatusReport"
}
},
"DisabledClients": {
"type": "array",
"description": "List of the clients where the extension is disabled.",
"items": {
"type": "string"
}
},
"Status": {
"description": "Calculated status for this extension.",
"$ref": "#/definitions/DeploymentStatus"
}
}
},
"StatusReport": {
"type": "object",
"description": "Represents the extension status object to be received from the update addon status REST method.",
"additionalProperties": false,
"required": [
"Status"
],
"properties": {
"Environment": {
"type": "string",
"description": "The free-form unique identifier of the environment of the client reporting the addon and extension status."
},
"ClientIdentity": {
"type": "string",
"description": "The free form unique identifier of the client who reporting the addon and extension status.\n\nIs used in order to calculate the addon and extension status based on all the client's reports.\nE.g. at least one reported fail - addon extension gets failed status."
},
"Process": {
"type": "string",
"description": "The free-form unique identifier of the process of the client reporting the addon and extension status."
},
"Status": {
"description": "The addon status.",
"$ref": "#/definitions/DeploymentStatus"
},
"Message": {
"type": "string",
"description": "The exception details for the case of failed status."
}
}
},
"DeploymentStatus": {
"type": "string",
"description": "Represents the status the Addon or Extension is currently in",
"x-enumNames": [
"Fail",
"Pending",
"Success",
"WaitingConfiguration",
"Disabled"
],
"enum": [
"Fail",
"Pending",
"Success",
"WaitingConfiguration",
"Disabled"
],
"x-ms-enum": {
"name": "DeploymentStatus",
"modelAsString": false
}
},
"Configuration": {
"type": "object",
"description": "Represents add-on configuration information",
"additionalProperties": false,
"properties": {
"FileName": {
"type": "string",
"description": "Gets the configuration file name."
},
"ContentHash": {
"type": "string",
"description": "Gets the hash of configuration file content."
},
"UploadedAt": {
"type": "string",
"description": "Gets configuration upload date.",
"format": "date-time"
}
}
},
"UploadResult": {
"type": "object",
"additionalProperties": false,
"required": [
"Id",
"IsModified"
],
"properties": {
"Id": {
"type": "integer",
"format": "int32"
},
"IsModified": {
"type": "boolean"
}
}
},
"IHeaderDictionary": {
"type": "object",
"x-abstract": true,
"additionalProperties": false,
"required": [
"Item"
],
"properties": {
"Item": {
"type": "array",
"items": {
"type": "string"
}
},
"ContentLength": {
"type": "integer",
"format": "int64"
}
}
},
"Manifest": {
"type": "object",
"description": "Represents add-on information from the add-on manifest file.",
"additionalProperties": false,
"required": [
"RequireConfiguration"
],
"properties": {
"ManifestVersion": {
"type": "string",
"description": "Gets the manifest version."
},
"Id": {
"type": "string",
"description": "Gets the unique identifier of the add-on."
},
"Name": {
"type": "string",
"description": "Gets the descriptive name of add-on."
},
"Description": {
"type": "string",
"description": "Gets the description of add-on."
},
"Author": {
"type": "string",
"description": "Gets the Author of add-on."
},
"MinVersion": {
"type": "string",
"description": "Gets the minimum SDL Tridion Sites version this extension supports."
},
"MaxVersion": {
"type": "string",
"description": "Gets the maximum SDL Tridion Sites version this extension supports."
},
"Version": {
"type": "string",
"description": "Gets the version of this add-on."
},
"Icon": {
"type": "string",
"description": "Gets the add-on icon."
},
"RequireConfiguration": {
"description": "Describes whether the custom configuration can be uploaded for the addon.",
"$ref": "#/definitions/RequireConfiguration"
},
"Dependencies": {
"type": "array",
"description": "Gets the collection of add-on dependencies.",
"items": {
"$ref": "#/definitions/DependencyManifest"
}
},
"Extensions": {
"type": "array",
"description": "Gets the list of extensions this add-on contains.",
"items": {
"$ref": "#/definitions/ExtensionManifest"
}
}
}
},
"DependencyManifest": {
"type": "object",
"description": "Represents add-on dependencies from the add-on manifest file.",
"additionalProperties": false,
"properties": {
"Id": {
"type": "string",
"description": "Gets the name of dependency package"
},
"Version": {
"type": "string",
"description": "Gets the version of dependency package"
}
}
},
"ExtensionManifest": {
"type": "object",
"description": "Represents extension information from the add-on manifest file.",
"additionalProperties": false,
"required": [
"Id"
],
"properties": {
"Id": {
"type": "integer",
"description": "Add-on service generated ID.",
"format": "int32"
},
"Name": {
"type": "string",
"description": "Gets the extension name"
},
"SupportedVersions": {
"type": "string",
"description": "Gets the SDL Tridion Sites component versions this extension support"
},
"Type": {
"type": "string",
"description": "Gets the identifier of the extension point that this extension extends."
},
"properties": {
"description": "Gets the additional properties required to configure the extension."
}
}
}
}
}

10503
test/mock/v2/test-docs.json Normal file

File diff suppressed because it is too large Load Diff

18116
test/mock/v2/test-sites.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,17 @@
openapi: "3.0.0"
swagger: "2.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
host: petstore.swagger.io
basePath: /v1
schemes:
- http
consumes:
- application/json
produces:
- application/json
paths:
/pets:
get:
@ -14,45 +20,37 @@ paths:
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
type: integer
format: int32
responses:
'200':
"200":
description: A paged array of pets
headers:
x-next:
type: string
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
schema:
$ref: '#/definitions/Pets'
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
schema:
$ref: '#/definitions/Error'
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
"201":
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
schema:
$ref: '#/definitions/Error'
/pets/{petId}:
get:
summary: Info for a specific pet
@ -60,52 +58,46 @@ paths:
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
- name: petId
in: path
required: true
description: The id of the pet to retrieve
type: string
responses:
'200':
"200":
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
schema:
$ref: '#/definitions/Pets'
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
schema:
$ref: '#/definitions/Error'
definitions:
Pet:
type: "object"
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: '#/definitions/Pet'
Error:
type: "object"
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@ -7,7 +7,7 @@
"paths": {
"/": {
"get": {
"operationId": "listVersionsv2",
"operationId": "listVersions",
"summary": "List API versions",
"responses": {
"200": {
@ -64,7 +64,7 @@
},
"/v2": {
"get": {
"operationId": "getVersionDetailsv2",
"operationId": "getVersionDetails",
"summary": "Show API version details",
"responses": {
"200": {