2018-01-22 21:30:53 +03:00
|
|
|
import { action, observable } from 'mobx';
|
2017-10-12 00:01:37 +03:00
|
|
|
import { join as joinPaths } from 'path';
|
|
|
|
import { parse as urlParse } from 'url';
|
|
|
|
|
|
|
|
import { IMenuItem } from '../MenuStore';
|
|
|
|
import { GroupModel } from './Group.model';
|
2018-01-22 21:30:53 +03:00
|
|
|
import { SecurityRequirementModel } from './SecurityRequirement';
|
2017-10-12 00:01:37 +03:00
|
|
|
|
|
|
|
import { OpenAPIExternalDocumentation, OpenAPIServer } from '../../types';
|
|
|
|
|
2018-02-19 14:31:18 +03:00
|
|
|
import {
|
|
|
|
getOperationSummary,
|
2018-06-18 15:56:30 +03:00
|
|
|
getStatusCodeType,
|
2018-07-17 12:17:06 +03:00
|
|
|
IS_BROWSER,
|
2018-02-19 14:31:18 +03:00
|
|
|
isAbsolutePath,
|
2018-06-18 15:56:30 +03:00
|
|
|
isStatusCode,
|
2018-02-19 14:31:18 +03:00
|
|
|
JsonPointer,
|
2018-05-14 11:37:19 +03:00
|
|
|
mergeParams,
|
2018-02-19 14:31:18 +03:00
|
|
|
sortByRequired,
|
2018-03-05 18:58:19 +03:00
|
|
|
stripTrailingSlash,
|
2018-02-19 14:31:18 +03:00
|
|
|
} from '../../utils';
|
2018-01-22 21:30:53 +03:00
|
|
|
import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
|
|
|
|
import { OpenAPIParser } from '../OpenAPIParser';
|
|
|
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
2017-10-12 00:01:37 +03:00
|
|
|
import { FieldModel } from './Field';
|
|
|
|
import { RequestBodyModel } from './RequestBody';
|
2018-01-22 21:30:53 +03:00
|
|
|
import { ResponseModel } from './Response';
|
2017-10-12 00:01:37 +03:00
|
|
|
import { CodeSample } from './types';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Operation model ready to be used by components
|
|
|
|
*/
|
|
|
|
export class OperationModel implements IMenuItem {
|
|
|
|
//#region IMenuItem fields
|
|
|
|
id: string;
|
|
|
|
absoluteIdx?: number;
|
|
|
|
name: string;
|
|
|
|
description?: string;
|
|
|
|
type = 'operation' as 'operation';
|
|
|
|
|
|
|
|
parent?: GroupModel;
|
|
|
|
externalDocs?: OpenAPIExternalDocumentation;
|
2018-01-22 21:30:53 +03:00
|
|
|
items: ContentItemModel[] = [];
|
2017-10-12 00:01:37 +03:00
|
|
|
|
|
|
|
depth: number;
|
|
|
|
|
|
|
|
@observable ready?: boolean = true;
|
|
|
|
@observable active: boolean = false;
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
_$ref: string;
|
|
|
|
operationId?: string;
|
|
|
|
httpVerb: string;
|
|
|
|
deprecated: boolean;
|
|
|
|
requestBody?: RequestBodyModel;
|
|
|
|
parameters: FieldModel[];
|
|
|
|
responses: ResponseModel[];
|
|
|
|
path: string;
|
|
|
|
servers: OpenAPIServer[];
|
2017-12-15 13:17:14 +03:00
|
|
|
security: SecurityRequirementModel[];
|
2017-10-12 00:01:37 +03:00
|
|
|
codeSamples: CodeSample[];
|
|
|
|
|
2017-11-21 14:00:33 +03:00
|
|
|
constructor(
|
|
|
|
parser: OpenAPIParser,
|
|
|
|
operationSpec: ExtendedOpenAPIOperation,
|
|
|
|
parent: GroupModel | undefined,
|
|
|
|
options: RedocNormalizedOptions,
|
|
|
|
) {
|
2018-03-22 18:46:30 +03:00
|
|
|
this.id =
|
|
|
|
operationSpec.operationId !== undefined
|
|
|
|
? 'operation/' + operationSpec.operationId
|
2018-07-14 18:16:31 +03:00
|
|
|
: parent !== undefined
|
|
|
|
? parent.id + operationSpec._$ref
|
2018-05-14 11:37:19 +03:00
|
|
|
: operationSpec._$ref;
|
2018-03-22 18:46:30 +03:00
|
|
|
|
2017-10-12 00:01:37 +03:00
|
|
|
this.name = getOperationSummary(operationSpec);
|
|
|
|
this.description = operationSpec.description;
|
|
|
|
|
|
|
|
this.parent = parent;
|
|
|
|
this.externalDocs = operationSpec.externalDocs;
|
|
|
|
|
|
|
|
this._$ref = operationSpec._$ref;
|
|
|
|
this.deprecated = !!operationSpec.deprecated;
|
|
|
|
this.httpVerb = operationSpec.httpVerb;
|
|
|
|
this.deprecated = !!operationSpec.deprecated;
|
|
|
|
this.operationId = operationSpec.operationId;
|
|
|
|
this.requestBody =
|
2017-11-21 14:24:41 +03:00
|
|
|
operationSpec.requestBody && new RequestBodyModel(parser, operationSpec.requestBody, options);
|
2017-10-12 00:01:37 +03:00
|
|
|
this.codeSamples = operationSpec['x-code-samples'] || [];
|
|
|
|
this.path = JsonPointer.baseName(this._$ref, 2);
|
|
|
|
|
2018-05-14 11:37:19 +03:00
|
|
|
this.parameters = mergeParams(
|
|
|
|
parser,
|
|
|
|
operationSpec.pathParameters,
|
|
|
|
operationSpec.parameters,
|
|
|
|
).map(paramOrRef => new FieldModel(parser, paramOrRef, this._$ref, options));
|
2017-10-12 00:01:37 +03:00
|
|
|
|
2018-02-19 14:31:18 +03:00
|
|
|
if (options.requiredPropsFirst) {
|
|
|
|
sortByRequired(this.parameters);
|
|
|
|
}
|
|
|
|
|
2017-11-14 13:53:56 +03:00
|
|
|
let hasSuccessResponses = false;
|
2017-10-12 00:01:37 +03:00
|
|
|
this.responses = Object.keys(operationSpec.responses || [])
|
2017-11-14 13:53:56 +03:00
|
|
|
.filter(code => {
|
2018-06-18 15:56:30 +03:00
|
|
|
if (code === 'default') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getStatusCodeType(code) === 'success') {
|
2017-11-14 13:53:56 +03:00
|
|
|
hasSuccessResponses = true;
|
|
|
|
}
|
2018-06-18 15:56:30 +03:00
|
|
|
|
|
|
|
return isStatusCode(code);
|
2017-11-14 13:53:56 +03:00
|
|
|
}) // filter out other props (e.g. x-props)
|
2017-11-21 14:00:33 +03:00
|
|
|
.map(code => {
|
|
|
|
return new ResponseModel(
|
|
|
|
parser,
|
|
|
|
code,
|
|
|
|
hasSuccessResponses,
|
|
|
|
operationSpec.responses[code],
|
|
|
|
options,
|
|
|
|
);
|
|
|
|
});
|
2017-10-12 00:01:37 +03:00
|
|
|
|
|
|
|
this.servers = normalizeServers(
|
|
|
|
parser.specUrl,
|
2017-11-14 18:46:50 +03:00
|
|
|
operationSpec.servers || parser.spec.servers || [],
|
2017-10-12 00:01:37 +03:00
|
|
|
);
|
2017-12-15 13:17:14 +03:00
|
|
|
|
|
|
|
this.security = (operationSpec.security || parser.spec.security || []).map(
|
|
|
|
security => new SecurityRequirementModel(security, parser),
|
|
|
|
);
|
2017-10-12 00:01:37 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set operation as active (used by side menu)
|
|
|
|
*/
|
|
|
|
@action
|
|
|
|
activate() {
|
|
|
|
this.active = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set operation as inactive (used by side menu)
|
|
|
|
*/
|
|
|
|
@action
|
|
|
|
deactivate() {
|
|
|
|
this.active = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 12:17:06 +03:00
|
|
|
function normalizeServers(specUrl: string | undefined, servers: OpenAPIServer[]): OpenAPIServer[] {
|
|
|
|
const baseUrl = specUrl === undefined ? (IS_BROWSER ? window.location.href : '') : specUrl;
|
2017-12-15 13:17:14 +03:00
|
|
|
|
2017-10-12 00:01:37 +03:00
|
|
|
if (servers.length === 0) {
|
|
|
|
return [
|
|
|
|
{
|
2018-07-17 12:17:06 +03:00
|
|
|
url: baseUrl,
|
2017-10-12 00:01:37 +03:00
|
|
|
},
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
function normalizeUrl(url: string): string {
|
2018-07-17 12:17:06 +03:00
|
|
|
url = isAbsolutePath(url) ? url : joinPaths(baseUrl, url);
|
2017-10-12 00:01:37 +03:00
|
|
|
return stripTrailingSlash(url.startsWith('//') ? `${specProtocol}${url}` : url);
|
|
|
|
}
|
|
|
|
|
2018-07-17 12:17:06 +03:00
|
|
|
const { protocol: specProtocol } = urlParse(baseUrl);
|
2017-10-12 00:01:37 +03:00
|
|
|
|
|
|
|
return servers.map(server => {
|
|
|
|
return {
|
|
|
|
...server,
|
|
|
|
url: normalizeUrl(server.url),
|
|
|
|
description: server.description || '',
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|