@atlassian/atlassian-openapi 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +16 -0
- package/lib/lookup.d.ts +50 -0
- package/lib/lookup.js +108 -0
- package/lib/operation-grouping.d.ts +28 -0
- package/lib/operation-grouping.js +183 -0
- package/lib/swagger.d.ts +837 -0
- package/lib/swagger.js +2 -0
- package/lib/test-functions.d.ts +2 -0
- package/lib/test-functions.js +15 -0
- package/lib/type-checks.d.ts +33 -0
- package/lib/type-checks.js +380 -0
- package/package.json +48 -0
package/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# atlassian-openapi
|
2
|
+
|
3
|
+
This package contains Typescript typings for OpenAPI 3.0 with Atlassian extensions included as well as
|
4
|
+
convenience functions for dealing with OpenAPI 3.0.
|
5
|
+
|
6
|
+
Atlassian code and tooling that is based on OpenAPI 3.0 should use this library.
|
7
|
+
|
8
|
+
Specifically, it includes:
|
9
|
+
|
10
|
+
- A `Swagger` namespace with OpenAPI 3.0 types.
|
11
|
+
- A `Lookup` class to let you easily resolve `$ref`s within your OpenAPI files.
|
12
|
+
- OpenAPI Operation Grouping logic that mirrors developer.atlassian.com's Side Nav and Postman collections.
|
13
|
+
- Type Checking functions to let you differentiate between different types.
|
14
|
+
|
15
|
+
Enjoy!
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5
|
+
}) : (function(o, m, k, k2) {
|
6
|
+
if (k2 === undefined) k2 = k;
|
7
|
+
o[k2] = m[k];
|
8
|
+
}));
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
10
|
+
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
|
11
|
+
};
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
13
|
+
__exportStar(require("./swagger"), exports);
|
14
|
+
__exportStar(require("./lookup"), exports);
|
15
|
+
__exportStar(require("./type-checks"), exports);
|
16
|
+
__exportStar(require("./operation-grouping"), exports);
|
package/lib/lookup.d.ts
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
import { Swagger as S } from './swagger';
|
2
|
+
export declare namespace SwaggerLookup {
|
3
|
+
/**
|
4
|
+
* Given an object and a potential reference to that object, this interface will help you perform that
|
5
|
+
* lookup and either return the right object or undefined.
|
6
|
+
*/
|
7
|
+
interface Lookup {
|
8
|
+
getCallback: (c: S.Callback | S.Reference) => S.Callback | undefined;
|
9
|
+
getExample: (e: S.Example | S.Reference) => S.Example | undefined;
|
10
|
+
getHeaders: (h: S.Header | S.Reference) => S.Header | undefined;
|
11
|
+
getLink: (l: S.Link | S.Reference) => S.Link | undefined;
|
12
|
+
getParam: (p: S.ParameterOrRef) => S.Parameter | undefined;
|
13
|
+
getRequestBody: (b: S.RequestBody | S.Reference) => S.RequestBody | undefined;
|
14
|
+
getResponse: (r: S.Response | S.Reference) => S.Response | undefined;
|
15
|
+
getSchema: (s: S.Schema | S.Reference) => S.Schema | undefined;
|
16
|
+
getSecuritySchemeByName: (name: string) => S.SecurityScheme | undefined;
|
17
|
+
getSecurityScheme: (ss: S.SecurityScheme | S.Reference) => S.SecurityScheme | undefined;
|
18
|
+
}
|
19
|
+
/**
|
20
|
+
* This lookup does not return any values that could be discovered via references. Every function
|
21
|
+
* just operates as the ID function; thus the name.
|
22
|
+
*/
|
23
|
+
class IdLookup implements Lookup {
|
24
|
+
getExample(e: S.Example | S.Reference): S.Example | undefined;
|
25
|
+
getRequestBody(b: S.Reference | S.RequestBody): S.RequestBody | undefined;
|
26
|
+
getHeaders(h: S.Reference | S.Header): S.Header | undefined;
|
27
|
+
getSecuritySchemeByName(_name: string): S.SecurityScheme | undefined;
|
28
|
+
getSecurityScheme(ss: S.Reference | S.SecurityScheme): S.SecurityScheme | undefined;
|
29
|
+
getLink(l: S.Reference | S.Link): S.Link | undefined;
|
30
|
+
getCallback(c: S.Reference | S.Callback): S.Callback | undefined;
|
31
|
+
getResponse(r: S.Response | S.Reference): S.Response | undefined;
|
32
|
+
getSchema(s: S.Schema | S.Reference): S.Schema | undefined;
|
33
|
+
getParam(p: S.ParameterOrRef): S.Parameter | undefined;
|
34
|
+
}
|
35
|
+
class InternalLookup implements Lookup {
|
36
|
+
private schema;
|
37
|
+
constructor(swagger: S.SwaggerV3);
|
38
|
+
getCallback(c: S.Reference | S.Callback): S.Callback | undefined;
|
39
|
+
getExample(e: S.Example | S.Reference): S.Example | undefined;
|
40
|
+
getHeaders(h: S.Reference | S.Header): S.Header | undefined;
|
41
|
+
getLink(l: S.Reference | S.Link): S.Link | undefined;
|
42
|
+
getParam(p: S.ParameterOrRef): S.Parameter | undefined;
|
43
|
+
getRequestBody(b: S.Reference | S.RequestBody): S.RequestBody | undefined;
|
44
|
+
getResponse(r: S.Response | S.Reference): S.Response | undefined;
|
45
|
+
getSchema(s: S.Schema | S.Reference): S.Schema | undefined;
|
46
|
+
getSecuritySchemeByName(name: string): S.SecurityScheme | undefined;
|
47
|
+
getSecurityScheme(ss: S.Reference | S.SecurityScheme): S.SecurityScheme | undefined;
|
48
|
+
private performLookup;
|
49
|
+
}
|
50
|
+
}
|
package/lib/lookup.js
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.SwaggerLookup = void 0;
|
4
|
+
const type_checks_1 = require("./type-checks");
|
5
|
+
const jsonpointer_1 = require("jsonpointer");
|
6
|
+
var SwaggerLookup;
|
7
|
+
(function (SwaggerLookup) {
|
8
|
+
/**
|
9
|
+
* This lookup does not return any values that could be discovered via references. Every function
|
10
|
+
* just operates as the ID function; thus the name.
|
11
|
+
*/
|
12
|
+
class IdLookup {
|
13
|
+
getExample(e) {
|
14
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(e) ? e : undefined;
|
15
|
+
}
|
16
|
+
getRequestBody(b) {
|
17
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(b) ? b : undefined;
|
18
|
+
}
|
19
|
+
getHeaders(h) {
|
20
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(h) ? h : undefined;
|
21
|
+
}
|
22
|
+
getSecuritySchemeByName(_name) {
|
23
|
+
return undefined;
|
24
|
+
}
|
25
|
+
getSecurityScheme(ss) {
|
26
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(ss) ? ss : undefined;
|
27
|
+
}
|
28
|
+
getLink(l) {
|
29
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(l) ? l : undefined;
|
30
|
+
}
|
31
|
+
getCallback(c) {
|
32
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(c) ? c : undefined;
|
33
|
+
}
|
34
|
+
getResponse(r) {
|
35
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(r) ? r : undefined;
|
36
|
+
}
|
37
|
+
getSchema(s) {
|
38
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(s) ? s : undefined;
|
39
|
+
}
|
40
|
+
getParam(p) {
|
41
|
+
return !type_checks_1.SwaggerTypeChecks.isReference(p) ? p : undefined;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
SwaggerLookup.IdLookup = IdLookup;
|
45
|
+
class InternalLookup {
|
46
|
+
constructor(swagger) {
|
47
|
+
this.schema = swagger;
|
48
|
+
}
|
49
|
+
getCallback(c) {
|
50
|
+
return this.performLookup(c, type_checks_1.SwaggerTypeChecks.isCallback);
|
51
|
+
}
|
52
|
+
getExample(e) {
|
53
|
+
return this.performLookup(e, type_checks_1.SwaggerTypeChecks.isExample);
|
54
|
+
}
|
55
|
+
getHeaders(h) {
|
56
|
+
return this.performLookup(h, type_checks_1.SwaggerTypeChecks.isHeader);
|
57
|
+
}
|
58
|
+
getLink(l) {
|
59
|
+
return this.performLookup(l, type_checks_1.SwaggerTypeChecks.isLink);
|
60
|
+
}
|
61
|
+
getParam(p) {
|
62
|
+
return this.performLookup(p, type_checks_1.SwaggerTypeChecks.isParameter);
|
63
|
+
}
|
64
|
+
getRequestBody(b) {
|
65
|
+
return this.performLookup(b, type_checks_1.SwaggerTypeChecks.isRequestBody);
|
66
|
+
}
|
67
|
+
getResponse(r) {
|
68
|
+
return this.performLookup(r, type_checks_1.SwaggerTypeChecks.isResponse);
|
69
|
+
}
|
70
|
+
getSchema(s) {
|
71
|
+
const potentialSchema = this.performLookup(s, type_checks_1.SwaggerTypeChecks.isSchema);
|
72
|
+
// Use the definition name if the title is missing
|
73
|
+
if (typeof potentialSchema !== 'undefined'
|
74
|
+
&& typeof potentialSchema.title === 'undefined'
|
75
|
+
&& type_checks_1.SwaggerTypeChecks.isReference(s)) {
|
76
|
+
const refSplit = s.$ref.split('/');
|
77
|
+
if (refSplit.length === 4) {
|
78
|
+
return Object.assign(Object.assign({}, potentialSchema), { title: refSplit[3] });
|
79
|
+
}
|
80
|
+
}
|
81
|
+
return potentialSchema;
|
82
|
+
}
|
83
|
+
getSecuritySchemeByName(name) {
|
84
|
+
return this.getSecurityScheme({ '$ref': `#/components/securitySchemes/${name}` });
|
85
|
+
}
|
86
|
+
getSecurityScheme(ss) {
|
87
|
+
return this.performLookup(ss, type_checks_1.SwaggerTypeChecks.isSecurityScheme);
|
88
|
+
}
|
89
|
+
// tslint:disable-next-line:no-any
|
90
|
+
performLookup(o, tCheck) {
|
91
|
+
if (!type_checks_1.SwaggerTypeChecks.isReference(o)) {
|
92
|
+
return o;
|
93
|
+
}
|
94
|
+
const ref = o.$ref;
|
95
|
+
if (!ref.startsWith('#')) {
|
96
|
+
// Any references that don't start with a # are external, and thus not handled
|
97
|
+
return undefined;
|
98
|
+
}
|
99
|
+
const result = jsonpointer_1.get(this.schema, ref.slice(1));
|
100
|
+
// Call recursively if you perform a lookup and get another reference
|
101
|
+
if (type_checks_1.SwaggerTypeChecks.isReference(result)) {
|
102
|
+
return this.performLookup(result, tCheck);
|
103
|
+
}
|
104
|
+
return tCheck(result) ? result : undefined;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
SwaggerLookup.InternalLookup = InternalLookup;
|
108
|
+
})(SwaggerLookup = exports.SwaggerLookup || (exports.SwaggerLookup = {}));
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { Swagger } from './swagger';
|
2
|
+
export declare type PathAndOperation = {
|
3
|
+
baseUrl: string;
|
4
|
+
path: string;
|
5
|
+
method: Swagger.Method;
|
6
|
+
operation: Swagger.Operation;
|
7
|
+
pathItemParameters: (Swagger.ParameterOrRef)[];
|
8
|
+
security: Swagger.SecurityRequirement[] | undefined;
|
9
|
+
};
|
10
|
+
export declare type OperationGrouping = {
|
11
|
+
title: string;
|
12
|
+
description?: string;
|
13
|
+
operations: PathAndOperation[];
|
14
|
+
};
|
15
|
+
export declare type OperationGroupings = {
|
16
|
+
groups: OperationGrouping[];
|
17
|
+
};
|
18
|
+
export declare function getIdForOperationGroup(grouping: OperationGrouping): string;
|
19
|
+
export declare function getIdForOperation(po: PathAndOperation): string;
|
20
|
+
export declare function getOpPath(po: PathAndOperation): string;
|
21
|
+
export declare type GroupingStrategy = (swagger: Swagger.SwaggerV3) => OperationGroupings;
|
22
|
+
/**
|
23
|
+
* This function replicates the RAD V1 grouping strategy that we used for the side-nav. Having this grouping strategy
|
24
|
+
* present should ease the transition to RAD V2 by making it the fallback mechanism if tagging has not been implemented.
|
25
|
+
*/
|
26
|
+
export declare const radV1GroupingStrategy: GroupingStrategy;
|
27
|
+
export declare const urlGroupingStrategy: GroupingStrategy;
|
28
|
+
export declare const tagGroupingStrategy: GroupingStrategy;
|
@@ -0,0 +1,183 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.tagGroupingStrategy = exports.urlGroupingStrategy = exports.radV1GroupingStrategy = exports.getOpPath = exports.getIdForOperation = exports.getIdForOperationGroup = void 0;
|
4
|
+
const URI = require("urijs");
|
5
|
+
function findBestServer(swagger) {
|
6
|
+
if (swagger.servers === undefined) {
|
7
|
+
return undefined;
|
8
|
+
}
|
9
|
+
const validServers = swagger.servers.filter(s => s.url.startsWith('http'));
|
10
|
+
if (validServers.length === 0) {
|
11
|
+
return swagger.servers.length > 0 ? swagger.servers[0] : undefined;
|
12
|
+
}
|
13
|
+
const httpsServer = validServers.find(s => s.url.startsWith('https://'));
|
14
|
+
return httpsServer === undefined ? validServers[0] : httpsServer;
|
15
|
+
}
|
16
|
+
function getServerUrlWithDefault(swagger) {
|
17
|
+
const bestServer = findBestServer(swagger);
|
18
|
+
// Default value
|
19
|
+
if (bestServer === undefined) {
|
20
|
+
return 'https://your-domain.atlassian.net';
|
21
|
+
}
|
22
|
+
return bestServer.url.startsWith('//') ? `https:${bestServer.url}` : bestServer.url;
|
23
|
+
}
|
24
|
+
function getIdForOperationGroup(grouping) {
|
25
|
+
// In the end, only alphanumeric characters should remain
|
26
|
+
return 'api-group-' + grouping.title.replace(/[^A-Za-z\-]/g, '-');
|
27
|
+
}
|
28
|
+
exports.getIdForOperationGroup = getIdForOperationGroup;
|
29
|
+
function generateId(text) {
|
30
|
+
return text.replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '');
|
31
|
+
}
|
32
|
+
function getIdForOperation(po) {
|
33
|
+
return `api-${generateId(po.path.substring(1))}-${po.method}`;
|
34
|
+
}
|
35
|
+
exports.getIdForOperation = getIdForOperation;
|
36
|
+
function getOpPath(po) {
|
37
|
+
const pathSegments = new URI(po.baseUrl).segment();
|
38
|
+
const basePath = pathSegments.filter(s => s.length > 0).join('/');
|
39
|
+
const path = basePath.length > 0 ? `/${basePath}${po.path}` : po.path;
|
40
|
+
return `${po.method.toUpperCase()} ${path}`;
|
41
|
+
}
|
42
|
+
exports.getOpPath = getOpPath;
|
43
|
+
function pathItemHasSummary(pathItem) {
|
44
|
+
return typeof pathItem.summary !== 'undefined' && pathItem.summary.length > 0;
|
45
|
+
}
|
46
|
+
function extractFromPathItem(baseUrl, path, pathItem, security) {
|
47
|
+
const pathItemParameters = pathItem.parameters === undefined ? [] : pathItem.parameters;
|
48
|
+
const toPO = (method, operation) => {
|
49
|
+
return {
|
50
|
+
baseUrl,
|
51
|
+
path,
|
52
|
+
method,
|
53
|
+
operation,
|
54
|
+
pathItemParameters,
|
55
|
+
security
|
56
|
+
};
|
57
|
+
};
|
58
|
+
const operations = new Array();
|
59
|
+
// tslint:disable:no-unused-expression
|
60
|
+
pathItem.get && operations.push(toPO('get', pathItem.get));
|
61
|
+
pathItem.put && operations.push(toPO('put', pathItem.put));
|
62
|
+
pathItem.post && operations.push(toPO('post', pathItem.post));
|
63
|
+
pathItem.delete && operations.push(toPO('delete', pathItem.delete));
|
64
|
+
pathItem.options && operations.push(toPO('options', pathItem.options));
|
65
|
+
pathItem.head && operations.push(toPO('head', pathItem.head));
|
66
|
+
pathItem.patch && operations.push(toPO('patch', pathItem.patch));
|
67
|
+
pathItem.trace && operations.push(toPO('trace', pathItem.trace));
|
68
|
+
// tslint:enable:no-unused-expression
|
69
|
+
return operations;
|
70
|
+
}
|
71
|
+
function toPathAndOperation(baseUrl, paths, security) {
|
72
|
+
return new Array().concat(...Object.keys(paths).map(path => extractFromPathItem(baseUrl, path, paths[path], security)));
|
73
|
+
}
|
74
|
+
function toTagGroups(tags, pos) {
|
75
|
+
let lookup = {};
|
76
|
+
let defaultOps = new Array();
|
77
|
+
// For each path and operation
|
78
|
+
pos.forEach(op => {
|
79
|
+
// If there is no tag, put it in the default group
|
80
|
+
if (typeof op.operation.tags === 'undefined' || op.operation.tags.length === 0) {
|
81
|
+
defaultOps.push(op);
|
82
|
+
}
|
83
|
+
else {
|
84
|
+
const firstTag = op.operation.tags[0];
|
85
|
+
const matchingTag = tags.find(t => t.name === firstTag);
|
86
|
+
// If there is a tag but it does not match any defined tag, put it in the default group
|
87
|
+
if (typeof matchingTag !== 'undefined') {
|
88
|
+
if (typeof lookup[firstTag] === 'undefined') {
|
89
|
+
const opArr = new Array();
|
90
|
+
opArr.push(op);
|
91
|
+
lookup[firstTag] = {
|
92
|
+
tag: matchingTag,
|
93
|
+
operations: opArr
|
94
|
+
};
|
95
|
+
}
|
96
|
+
else {
|
97
|
+
lookup[firstTag].operations.push(op);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
else {
|
101
|
+
defaultOps.push(op);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
});
|
105
|
+
return {
|
106
|
+
// Maintain the original sort order of the tags
|
107
|
+
groups: tags.map(t => lookup[t.name]).filter(tg => typeof tg !== 'undefined'),
|
108
|
+
default: {
|
109
|
+
operations: defaultOps
|
110
|
+
}
|
111
|
+
};
|
112
|
+
}
|
113
|
+
function tagGroupToOperationGrouping(tg) {
|
114
|
+
return {
|
115
|
+
title: tg.tag.name,
|
116
|
+
description: tg.tag.description,
|
117
|
+
operations: tg.operations
|
118
|
+
};
|
119
|
+
}
|
120
|
+
function tagGroupsToOperationGroupings(tgs) {
|
121
|
+
const groups = tgs.groups.map(tagGroupToOperationGrouping);
|
122
|
+
if (tgs.default.operations.length > 0) {
|
123
|
+
groups.push({
|
124
|
+
title: 'Other operations',
|
125
|
+
operations: tgs.default.operations
|
126
|
+
});
|
127
|
+
}
|
128
|
+
return { groups };
|
129
|
+
}
|
130
|
+
function generateTitleFromPath(path) {
|
131
|
+
// This is a workaround for the issue with deeper endpoint paths in Jira Cloud REST Api.
|
132
|
+
// This should be replaced with tags feature, once it's implemented. See go/j/JCE-1590
|
133
|
+
const pathElement = path.startsWith('/api/3/') || path.startsWith('/api/2/') || path.startsWith('/auth/1') ? 2 : 0;
|
134
|
+
const firstPathItem = path.substring(1).split('/')[pathElement];
|
135
|
+
return firstPathItem ? firstPathItem[0].toUpperCase() + firstPathItem.slice(1) : '/';
|
136
|
+
}
|
137
|
+
/**
|
138
|
+
* This function replicates the RAD V1 grouping strategy that we used for the side-nav. Having this grouping strategy
|
139
|
+
* present should ease the transition to RAD V2 by making it the fallback mechanism if tagging has not been implemented.
|
140
|
+
*/
|
141
|
+
exports.radV1GroupingStrategy = swagger => {
|
142
|
+
const baseUrl = getServerUrlWithDefault(swagger);
|
143
|
+
const groupings = {};
|
144
|
+
Object.keys(swagger.paths).forEach(path => {
|
145
|
+
const topLevelPathGroup = generateTitleFromPath(path);
|
146
|
+
const newPathItems = extractFromPathItem(baseUrl, path, swagger.paths[path], swagger.security);
|
147
|
+
if (groupings[topLevelPathGroup] === undefined) {
|
148
|
+
groupings[topLevelPathGroup] = {
|
149
|
+
title: topLevelPathGroup,
|
150
|
+
operations: newPathItems
|
151
|
+
};
|
152
|
+
}
|
153
|
+
else {
|
154
|
+
groupings[topLevelPathGroup].operations.push(...newPathItems);
|
155
|
+
}
|
156
|
+
});
|
157
|
+
return {
|
158
|
+
groups: Object.keys(groupings).map(pg => groupings[pg])
|
159
|
+
};
|
160
|
+
};
|
161
|
+
exports.urlGroupingStrategy = swagger => {
|
162
|
+
if (!Object.keys(swagger.paths).some(path => pathItemHasSummary(swagger.paths[path]))) {
|
163
|
+
return exports.radV1GroupingStrategy(swagger);
|
164
|
+
}
|
165
|
+
const paths = Object.keys(swagger.paths).sort();
|
166
|
+
const baseUrl = getServerUrlWithDefault(swagger);
|
167
|
+
const groups = paths.map(path => {
|
168
|
+
const pathItem = swagger.paths[path];
|
169
|
+
return {
|
170
|
+
title: pathItem.summary || generateTitleFromPath(path),
|
171
|
+
description: pathItem.description,
|
172
|
+
operations: extractFromPathItem(baseUrl, path, pathItem, swagger.security)
|
173
|
+
};
|
174
|
+
});
|
175
|
+
return { groups };
|
176
|
+
};
|
177
|
+
exports.tagGroupingStrategy = swagger => {
|
178
|
+
if (!swagger.tags) {
|
179
|
+
// Fall-back to a urlGrouping strategy if tagging does not work.
|
180
|
+
return exports.urlGroupingStrategy(swagger);
|
181
|
+
}
|
182
|
+
return tagGroupsToOperationGroupings(toTagGroups(swagger.tags, toPathAndOperation(getServerUrlWithDefault(swagger), swagger.paths, swagger.security)));
|
183
|
+
};
|