@oslo-flanders/swagger-generator 1.0.2 → 1.0.4
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/bin/runner.js +0 -0
- package/lib/SwaggerGenerationService.d.ts +6 -6
- package/lib/SwaggerGenerationService.js +336 -115
- package/lib/config/SwaggerGenerationServiceConfiguration.js +2 -3
- package/lib/constants/Swagger.d.ts +5 -2
- package/lib/constants/Swagger.js +13 -4
- package/lib/enums/Properties.d.ts +1 -1
- package/lib/enums/Properties.js +54 -31
- package/lib/types/Swagger.d.ts +51 -50
- package/lib/utils/swaggerUtils.d.ts +5 -5
- package/lib/utils/swaggerUtils.js +46 -25
- package/package.json +1 -1
package/bin/runner.js
CHANGED
|
File without changes
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type { IService } from '@oslo-flanders/core';
|
|
2
2
|
import { QuadStore, Logger } from '@oslo-flanders/core';
|
|
3
|
-
import type * as RDF from '@rdfjs/types';
|
|
4
|
-
import { DataFactory } from 'rdf-data-factory';
|
|
5
3
|
import { SwaggerGenerationServiceConfiguration } from './config/SwaggerGenerationServiceConfiguration';
|
|
6
4
|
export declare class SwaggerGenerationService implements IService {
|
|
7
5
|
readonly logger: Logger;
|
|
8
6
|
readonly configuration: SwaggerGenerationServiceConfiguration;
|
|
9
|
-
readonly dataFactory: DataFactory<RDF.Quad>;
|
|
10
7
|
readonly store: QuadStore;
|
|
11
8
|
constructor(logger: Logger, config: SwaggerGenerationServiceConfiguration, store: QuadStore);
|
|
9
|
+
private getProbleemdetailsContent;
|
|
10
|
+
private getProbleemdetailsLink;
|
|
12
11
|
private getContact;
|
|
13
12
|
private getLicense;
|
|
14
|
-
private getTag;
|
|
15
13
|
init(): Promise<void>;
|
|
16
14
|
run(): Promise<void>;
|
|
17
|
-
createSwagger(): Object;
|
|
18
|
-
|
|
15
|
+
createSwagger(schemas: any, links: any): Object;
|
|
16
|
+
createSchemas(): Object;
|
|
17
|
+
createLinks(): Object;
|
|
18
|
+
writeJSON(json: Object, outputPath: string): Promise<void>;
|
|
19
19
|
}
|
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
2
18
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
19
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
20
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
21
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
22
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
23
|
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
25
|
+
if (mod && mod.__esModule) return mod;
|
|
26
|
+
var result = {};
|
|
27
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
28
|
+
__setModuleDefault(result, mod);
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
8
31
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
32
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
33
|
};
|
|
@@ -14,20 +37,37 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
38
|
exports.SwaggerGenerationService = void 0;
|
|
16
39
|
const core_1 = require("@oslo-flanders/core");
|
|
40
|
+
const path = __importStar(require("path"));
|
|
17
41
|
const promises_1 = require("fs/promises");
|
|
18
42
|
const inversify_1 = require("inversify");
|
|
19
|
-
const rdf_data_factory_1 = require("rdf-data-factory");
|
|
20
43
|
const SwaggerGenerationServiceConfiguration_1 = require("./config/SwaggerGenerationServiceConfiguration");
|
|
21
44
|
const Properties_1 = require("./enums/Properties");
|
|
22
|
-
const swaggerUtils_1 = require("./utils/swaggerUtils");
|
|
23
|
-
const Swagger_1 = require("./constants/Swagger");
|
|
24
45
|
let SwaggerGenerationService = class SwaggerGenerationService {
|
|
25
46
|
constructor(logger, config, store) {
|
|
26
|
-
this.dataFactory = new rdf_data_factory_1.DataFactory();
|
|
27
47
|
this.logger = logger;
|
|
28
48
|
this.configuration = config;
|
|
29
49
|
this.store = store;
|
|
30
50
|
}
|
|
51
|
+
getProbleemdetailsContent() {
|
|
52
|
+
return {
|
|
53
|
+
[core_1.OutputFormat.JsonProblem]: {
|
|
54
|
+
schema: {
|
|
55
|
+
$ref: '#/components/schemas/Probleemdetail',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
getProbleemdetailsLink(label) {
|
|
61
|
+
return {
|
|
62
|
+
'Probleemdetail.type': {
|
|
63
|
+
operationId: `${label}GET`,
|
|
64
|
+
parameters: {
|
|
65
|
+
type: `$response.body#/type`,
|
|
66
|
+
},
|
|
67
|
+
description: 'De waarde van het attribuut `type` kan gebruikt worden om het gerefereerde object van het type `ProblemDetails` op te halen.',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
31
71
|
getContact() {
|
|
32
72
|
/* If no contact information is provided, the whole block may not appear */
|
|
33
73
|
if (!this.configuration.contactName &&
|
|
@@ -49,25 +89,37 @@ let SwaggerGenerationService = class SwaggerGenerationService {
|
|
|
49
89
|
url: this.configuration.licenseURL,
|
|
50
90
|
};
|
|
51
91
|
}
|
|
52
|
-
getTag(subject) {
|
|
53
|
-
var _a;
|
|
54
|
-
if ((_a = this.store
|
|
55
|
-
.getAssignedUri(subject)) === null || _a === void 0 ? void 0 : _a.value.startsWith(this.configuration.baseURL)) {
|
|
56
|
-
return 'OSLO datastandaarden';
|
|
57
|
-
}
|
|
58
|
-
return 'Externe datastandaarden';
|
|
59
|
-
}
|
|
60
92
|
async init() {
|
|
61
93
|
return this.store.addQuadsFromFile(this.configuration.input);
|
|
62
94
|
}
|
|
63
95
|
async run() {
|
|
64
|
-
/* Create Swagger
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
96
|
+
/* Create Swagger schemas */
|
|
97
|
+
const schemas = this.createSchemas();
|
|
98
|
+
for (const label of Object.keys(schemas))
|
|
99
|
+
await this.writeJSON(schemas[label], `swagger/components/schemas/${label}.json`);
|
|
100
|
+
/* Create Swagger links */
|
|
101
|
+
const links = this.createLinks();
|
|
102
|
+
for (const label of Object.keys(links))
|
|
103
|
+
await this.writeJSON(links[label], `swagger/components/links/${label}.json`);
|
|
104
|
+
/* Create self-standing referenceable components */
|
|
105
|
+
const components = {
|
|
106
|
+
openapi: this.configuration.versionSwagger,
|
|
107
|
+
info: {
|
|
108
|
+
title: `Components of ${this.configuration.title}`,
|
|
109
|
+
description: this.configuration.description,
|
|
110
|
+
contact: this.getContact(),
|
|
111
|
+
license: this.getLicense(),
|
|
112
|
+
version: this.configuration.versionAPI,
|
|
113
|
+
},
|
|
114
|
+
components: { schemas, links },
|
|
115
|
+
};
|
|
116
|
+
await this.writeJSON(components, `swagger/components.json`);
|
|
117
|
+
/* Create Swagger endpoint paths as example */
|
|
118
|
+
const swagger = this.createSwagger(schemas, links);
|
|
119
|
+
await this.writeJSON(swagger, 'swagger/example.json');
|
|
68
120
|
}
|
|
69
|
-
createSwagger() {
|
|
70
|
-
var _a, _b, _c
|
|
121
|
+
createSwagger(schemas, links) {
|
|
122
|
+
var _a, _b, _c;
|
|
71
123
|
const swagger = {
|
|
72
124
|
openapi: this.configuration.versionSwagger,
|
|
73
125
|
info: {
|
|
@@ -84,36 +136,173 @@ let SwaggerGenerationService = class SwaggerGenerationService {
|
|
|
84
136
|
},
|
|
85
137
|
],
|
|
86
138
|
paths: {},
|
|
87
|
-
components: {
|
|
88
|
-
schemas: {},
|
|
89
|
-
},
|
|
90
139
|
};
|
|
91
|
-
|
|
92
|
-
|
|
140
|
+
const hasRootClasses = this.store.findSubjects(core_1.ns.rdf('type'), core_1.ns.oslo('RootClass')).length > 0;
|
|
141
|
+
/* Create endpoint for each Class which is marked as RootClass, if none, generate all */
|
|
93
142
|
for (const classId of this.store.findSubjects(core_1.ns.rdf('type'), core_1.ns.owl('Class'))) {
|
|
143
|
+
if (hasRootClasses && !this.store.isRootClass(classId))
|
|
144
|
+
continue;
|
|
145
|
+
const filteredLinks = {};
|
|
94
146
|
let label = (_a = (0, core_1.getApplicationProfileLabel)(classId, this.store, this.configuration.language)) === null || _a === void 0 ? void 0 : _a.value;
|
|
95
147
|
const definition = (_b = (0, core_1.getApplicationProfileDefinition)(classId, this.store, this.configuration.language)) === null || _b === void 0 ? void 0 : _b.value;
|
|
96
|
-
const usageNote = (_c = (0, core_1.
|
|
148
|
+
const usageNote = (_c = (0, core_1.getApplicationProfileUsageNote)(classId, this.store, this.configuration.language)) === null || _c === void 0 ? void 0 : _c.value;
|
|
149
|
+
if (!label) {
|
|
150
|
+
this.logger.error(`Unknown class label for ${classId.value}`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
/* Class labels should be always pascal cased */
|
|
154
|
+
label = (0, core_1.toPascalCase)(label);
|
|
155
|
+
/* Only keep links which are related to the class */
|
|
156
|
+
for (const key of Object.keys(links)) {
|
|
157
|
+
if (key.startsWith(`${label}.`))
|
|
158
|
+
filteredLinks[key] = links[key];
|
|
159
|
+
}
|
|
160
|
+
/* Create an endpoint for each object to have PURIs for each object */
|
|
161
|
+
swagger.paths[`/id/${label}/{id}`] = {
|
|
162
|
+
get: {
|
|
163
|
+
summary: definition,
|
|
164
|
+
description: usageNote,
|
|
165
|
+
operationId: `${label}GET`,
|
|
166
|
+
parameters: [
|
|
167
|
+
{
|
|
168
|
+
name: 'id',
|
|
169
|
+
required: true,
|
|
170
|
+
in: 'path',
|
|
171
|
+
description: `Identificator van een ${label} object.`,
|
|
172
|
+
schema: {
|
|
173
|
+
type: 'string',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
responses: {
|
|
178
|
+
200: {
|
|
179
|
+
description: `${label} object gevonden.`,
|
|
180
|
+
content: {
|
|
181
|
+
[core_1.OutputFormat.JsonLd]: {
|
|
182
|
+
schema: {
|
|
183
|
+
$ref: `#/components/schemas/${label}`,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
links: filteredLinks,
|
|
188
|
+
},
|
|
189
|
+
/* Error codes follow the RFC 7807 */
|
|
190
|
+
400: {
|
|
191
|
+
description: 'Invalid data supplied.',
|
|
192
|
+
content: this.getProbleemdetailsContent(),
|
|
193
|
+
links: this.getProbleemdetailsLink(label),
|
|
194
|
+
},
|
|
195
|
+
401: {
|
|
196
|
+
description: 'Invalid authorization.',
|
|
197
|
+
content: this.getProbleemdetailsContent(),
|
|
198
|
+
links: this.getProbleemdetailsLink(label),
|
|
199
|
+
},
|
|
200
|
+
403: {
|
|
201
|
+
description: 'Authentication failed.',
|
|
202
|
+
content: this.getProbleemdetailsContent(),
|
|
203
|
+
links: this.getProbleemdetailsLink(label),
|
|
204
|
+
},
|
|
205
|
+
404: {
|
|
206
|
+
description: 'Resource not found.',
|
|
207
|
+
content: this.getProbleemdetailsContent(),
|
|
208
|
+
links: this.getProbleemdetailsLink(label),
|
|
209
|
+
},
|
|
210
|
+
412: {
|
|
211
|
+
description: 'Pre-condition failed.',
|
|
212
|
+
content: this.getProbleemdetailsContent(),
|
|
213
|
+
links: this.getProbleemdetailsLink(label),
|
|
214
|
+
},
|
|
215
|
+
500: {
|
|
216
|
+
description: 'Unexpected Server Error.',
|
|
217
|
+
content: this.getProbleemdetailsContent(),
|
|
218
|
+
links: this.getProbleemdetailsLink(label),
|
|
219
|
+
},
|
|
220
|
+
502: {
|
|
221
|
+
description: 'Bad Gateway.',
|
|
222
|
+
content: this.getProbleemdetailsContent(),
|
|
223
|
+
links: this.getProbleemdetailsLink(label),
|
|
224
|
+
},
|
|
225
|
+
503: {
|
|
226
|
+
description: 'Service unavailable.',
|
|
227
|
+
content: this.getProbleemdetailsContent(),
|
|
228
|
+
links: this.getProbleemdetailsLink(label),
|
|
229
|
+
},
|
|
230
|
+
504: {
|
|
231
|
+
description: 'Gateway Timeout.',
|
|
232
|
+
content: this.getProbleemdetailsContent(),
|
|
233
|
+
links: this.getProbleemdetailsLink(label),
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/* Inline schemas */
|
|
240
|
+
swagger.components = {
|
|
241
|
+
schemas: {},
|
|
242
|
+
};
|
|
243
|
+
for (const schemaLabel of Object.keys(schemas))
|
|
244
|
+
swagger.components.schemas[schemaLabel] = schemas[schemaLabel];
|
|
245
|
+
return swagger;
|
|
246
|
+
}
|
|
247
|
+
createSchemas() {
|
|
248
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
249
|
+
const schemas = {};
|
|
250
|
+
/* Create schema for each enumeration */
|
|
251
|
+
for (const enumId of this.store.findSubjects(core_1.ns.oslo('assignedURI'), core_1.ns.skos('Concept'))) {
|
|
252
|
+
let label = (_a = (0, core_1.getApplicationProfileLabel)(enumId, this.store, this.configuration.language)) === null || _a === void 0 ? void 0 : _a.value;
|
|
253
|
+
if (!label) {
|
|
254
|
+
this.logger.error(`Unknown enum label for ${enumId.value}`);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
/* Class labels should be always pascal cased */
|
|
258
|
+
label = (0, core_1.toPascalCase)(label);
|
|
259
|
+
schemas[label] = {
|
|
260
|
+
title: label,
|
|
261
|
+
type: 'object',
|
|
262
|
+
description: `Enumeratie van ${label}`,
|
|
263
|
+
properties: {
|
|
264
|
+
'@id': {
|
|
265
|
+
type: 'string',
|
|
266
|
+
format: 'uri',
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
required: ['@id'],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/* Create schema for each Class and Datatype */
|
|
273
|
+
for (const classId of [
|
|
274
|
+
...this.store.findSubjects(core_1.ns.rdf('type'), core_1.ns.owl('Class')),
|
|
275
|
+
...this.store.findSubjects(core_1.ns.rdf('type'), core_1.ns.rdfs('Datatype')),
|
|
276
|
+
]) {
|
|
277
|
+
const assignedUri = this.store.getAssignedUri(classId);
|
|
278
|
+
/* Primitive datatypes may not be generated */
|
|
279
|
+
if (assignedUri && [...core_1.DataTypes.values()].includes(assignedUri.value))
|
|
280
|
+
continue;
|
|
281
|
+
/* Abstract classes may not be generated to avoid that they are instantiated */
|
|
282
|
+
if (this.store.isAbstractClass(classId))
|
|
283
|
+
continue;
|
|
284
|
+
let label = (_b = (0, core_1.getApplicationProfileLabel)(classId, this.store, this.configuration.language)) === null || _b === void 0 ? void 0 : _b.value;
|
|
285
|
+
const definition = (_c = (0, core_1.getApplicationProfileDefinition)(classId, this.store, this.configuration.language)) === null || _c === void 0 ? void 0 : _c.value;
|
|
97
286
|
let attributes = {};
|
|
98
287
|
let requiredAttributes = {};
|
|
99
288
|
/* Get all attributes in a recursive manner for inheritance */
|
|
100
289
|
let attributeIds = [];
|
|
101
|
-
attributeIds = (0,
|
|
290
|
+
attributeIds = (0, core_1.findAllAttributes)(classId, attributeIds, this.store, this.logger);
|
|
102
291
|
if (!label) {
|
|
103
292
|
this.logger.error(`Unknown class label for ${classId.value}`);
|
|
104
293
|
continue;
|
|
105
294
|
}
|
|
106
295
|
/* Class labels should be always pascal cased */
|
|
107
|
-
label = (0,
|
|
296
|
+
label = (0, core_1.toPascalCase)(label);
|
|
108
297
|
attributes[label] = [];
|
|
109
298
|
requiredAttributes[label] = [];
|
|
110
|
-
links[label] = {};
|
|
111
299
|
/* Find all attributes for object */
|
|
112
300
|
for (const attributeId of attributeIds) {
|
|
113
301
|
let attributeLabel = (_d = (0, core_1.getApplicationProfileLabel)(attributeId, this.store, this.configuration.language)) === null || _d === void 0 ? void 0 : _d.value;
|
|
114
302
|
const attributeMinCount = (0, core_1.getMinCount)(attributeId, this.store);
|
|
115
303
|
const attributeMaxCount = (0, core_1.getMaxCount)(attributeId, this.store);
|
|
116
304
|
const attributeRangeId = this.store.getRange(attributeId);
|
|
305
|
+
const attributeDomainId = this.store.getDomain(attributeId);
|
|
117
306
|
const attributeDefinition = (_e = (0, core_1.getApplicationProfileDefinition)(attributeId, this.store, this.configuration.language)) === null || _e === void 0 ? void 0 : _e.value;
|
|
118
307
|
const attributeUsageNote = (_f = (0, core_1.getApplicationProfileUsageNote)(attributeId, this.store, this.configuration.language)) === null || _f === void 0 ? void 0 : _f.value;
|
|
119
308
|
if (!attributeLabel) {
|
|
@@ -121,39 +310,65 @@ let SwaggerGenerationService = class SwaggerGenerationService {
|
|
|
121
310
|
continue;
|
|
122
311
|
}
|
|
123
312
|
/* Attribute labels should be always camel cased */
|
|
124
|
-
attributeLabel = (0,
|
|
313
|
+
attributeLabel = (0, core_1.toCamelCase)(attributeLabel);
|
|
125
314
|
if (!attributeRangeId) {
|
|
126
315
|
this.logger.error(`Unknown range for attribute ${attributeId.value}`);
|
|
127
316
|
continue;
|
|
128
317
|
}
|
|
318
|
+
if (!attributeDomainId) {
|
|
319
|
+
this.logger.error(`Unknown domain for attribute ${attributeId.value}`);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
129
322
|
const attributeDatatypeId = (_g = this.store.getAssignedUri(attributeRangeId)) === null || _g === void 0 ? void 0 : _g.value;
|
|
130
|
-
|
|
323
|
+
let attributeDatatypeLabel = (_h = (0, core_1.getApplicationProfileLabel)(attributeRangeId, this.store, this.configuration.language)) === null || _h === void 0 ? void 0 : _h.value;
|
|
131
324
|
if (!attributeDatatypeId || !attributeDatatypeLabel) {
|
|
132
325
|
this.logger.error(`Unknown datatype for attribute ${attributeId.value}`);
|
|
133
326
|
continue;
|
|
134
327
|
}
|
|
328
|
+
attributeDatatypeLabel = (0, core_1.toPascalCase)(attributeDatatypeLabel);
|
|
329
|
+
const attributeDatatypeAbstract = this.store.isAbstractClass(attributeRangeId);
|
|
330
|
+
/* Attribute datatypes may have a super class assigned, but any subclass of this super class must be allowed */
|
|
331
|
+
const subclasses = [];
|
|
332
|
+
for (const subclassId of this.store.findSubjects(core_1.ns.rdfs('subClassOf'), attributeRangeId)) {
|
|
333
|
+
const subclassLabel = (_j = (0, core_1.getApplicationProfileLabel)(subclassId, this.store, this.configuration.language)) === null || _j === void 0 ? void 0 : _j.value;
|
|
334
|
+
if (!subclassLabel) {
|
|
335
|
+
console.error(`Unable to retrieve subclass label of ${subclassId}`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
subclasses.push((0, core_1.toPascalCase)(subclassLabel));
|
|
339
|
+
}
|
|
340
|
+
/* Cardinality */
|
|
135
341
|
if (!attributeMinCount || !attributeMaxCount) {
|
|
136
342
|
this.logger.error(`Unknown cardinality for attribute ${attributeId.value}`);
|
|
137
343
|
continue;
|
|
138
344
|
}
|
|
139
345
|
/* Arrays must be introduced into the schema if the max cardinality is 2 or more */
|
|
140
|
-
const type = 'object';
|
|
141
346
|
const description = `${attributeDefinition}${attributeUsageNote ? ' ' + attributeUsageNote : ''}`;
|
|
142
|
-
const properties = (0, Properties_1.mapProperties)(attributeDatatypeId, attributeDatatypeLabel,
|
|
347
|
+
const properties = (0, Properties_1.mapProperties)(attributeDatatypeId, attributeDatatypeLabel, subclasses, attributeDatatypeAbstract);
|
|
143
348
|
const requiredProperties = properties
|
|
144
349
|
? Object.keys(properties)
|
|
145
350
|
: undefined;
|
|
351
|
+
/* Primitive datatypes are objects with properties */
|
|
352
|
+
let item = {};
|
|
353
|
+
if ([...core_1.DataTypes.values()].includes(attributeDatatypeId)) {
|
|
354
|
+
item = {
|
|
355
|
+
type: 'object',
|
|
356
|
+
description: description,
|
|
357
|
+
properties: properties,
|
|
358
|
+
required: requiredProperties,
|
|
359
|
+
};
|
|
360
|
+
/* Schema references are $ref, not objects */
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
item = properties;
|
|
364
|
+
}
|
|
146
365
|
if (attributeMaxCount != '0' && attributeMaxCount != '1') {
|
|
147
|
-
|
|
366
|
+
/* minItems must be 1, empty arrays are removed by not including the attribute, even if minCount == 0 */
|
|
367
|
+
attributes[label][attributeLabel] = {
|
|
148
368
|
type: 'array',
|
|
149
369
|
description: `Lijst van ${attributeDatatypeLabel} items.`,
|
|
150
|
-
items:
|
|
151
|
-
|
|
152
|
-
description: description,
|
|
153
|
-
properties: properties,
|
|
154
|
-
required: requiredProperties,
|
|
155
|
-
},
|
|
156
|
-
minItems: parseInt(attributeMinCount),
|
|
370
|
+
items: item,
|
|
371
|
+
minItems: Math.max(parseInt(attributeMinCount), 1),
|
|
157
372
|
maxItems: attributeMaxCount == '*'
|
|
158
373
|
? undefined
|
|
159
374
|
: parseInt(attributeMaxCount),
|
|
@@ -161,102 +376,108 @@ let SwaggerGenerationService = class SwaggerGenerationService {
|
|
|
161
376
|
/* Regular property with a cardinality of [0..0], [0..1], [1..1] */
|
|
162
377
|
}
|
|
163
378
|
else {
|
|
164
|
-
attributes[label][
|
|
165
|
-
type: type,
|
|
166
|
-
description: description,
|
|
167
|
-
properties: properties,
|
|
168
|
-
required: requiredProperties,
|
|
169
|
-
};
|
|
379
|
+
attributes[label][attributeLabel] = item;
|
|
170
380
|
}
|
|
171
381
|
/* Only require properties which are not arrays since those have their own cardinality checks */
|
|
172
382
|
if (attributeMinCount == '1')
|
|
173
|
-
requiredAttributes[label].push(
|
|
174
|
-
/* Create all possible links for endpoint */
|
|
175
|
-
if (!(0, core_1.isStandardDatatype)(attributeDatatypeId)) {
|
|
176
|
-
links[label][`${attributeDatatypeLabel}GET`] = {
|
|
177
|
-
operationId: `${attributeDatatypeLabel}GET`,
|
|
178
|
-
parameters: {
|
|
179
|
-
/* Referring to response body always starts with the same prefix */
|
|
180
|
-
id: `$response.body#/${label}.${attributeLabel}/@id`,
|
|
181
|
-
},
|
|
182
|
-
description: `Het \`@id\` attribuut van de waarde van \`${label}.${attributeLabel}\` kan gebruikt worden om het gerefereerde object van het type \`${attributeDatatypeLabel}\` op te halen.`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
383
|
+
requiredAttributes[label].push(attributeLabel);
|
|
185
384
|
}
|
|
186
|
-
/*
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
required: true,
|
|
197
|
-
in: 'path',
|
|
198
|
-
description: `Identificator van een ${label} object.`,
|
|
199
|
-
schema: {
|
|
200
|
-
type: 'string',
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
],
|
|
204
|
-
responses: {
|
|
205
|
-
200: {
|
|
206
|
-
description: `${label} object gevonden.`,
|
|
207
|
-
content: {
|
|
208
|
-
[core_1.OutputFormat.JsonLd]: {
|
|
209
|
-
schema: {
|
|
210
|
-
$ref: `#/components/schemas/${label}`,
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
links: links[label],
|
|
215
|
-
},
|
|
216
|
-
400: {
|
|
217
|
-
description: `Ontbrekende informatie bij het opvragen een ${label} object.`,
|
|
218
|
-
},
|
|
219
|
-
404: {
|
|
220
|
-
description: `${label} object met gegeven ID niet gevonden.`,
|
|
221
|
-
},
|
|
385
|
+
/* Remove duplicates in requiredAttributes list which may be introduced due to redefine/subsetting in inheritance */
|
|
386
|
+
requiredAttributes[label] = [...new Set(requiredAttributes[label])];
|
|
387
|
+
/* Root classes require a @context property, add it here if needed */
|
|
388
|
+
let context = {};
|
|
389
|
+
if (this.store.isRootClass(classId)) {
|
|
390
|
+
context = {
|
|
391
|
+
'@context': {
|
|
392
|
+
type: 'string',
|
|
393
|
+
description: 'URI of the JSON-LD versioned context file for this response.',
|
|
394
|
+
pattern: `^${this.configuration.contextURL}$`.replace(/\//g, '\\/'),
|
|
222
395
|
},
|
|
223
|
-
}
|
|
224
|
-
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
225
398
|
/* Create components for each schema */
|
|
226
|
-
|
|
399
|
+
schemas[label] = {
|
|
400
|
+
title: label,
|
|
227
401
|
type: 'object',
|
|
228
402
|
description: definition,
|
|
229
403
|
properties: {
|
|
230
|
-
'@context': {
|
|
231
|
-
type: 'string',
|
|
232
|
-
format: 'uri',
|
|
233
|
-
enum: [this.configuration.contextURL],
|
|
234
|
-
},
|
|
235
404
|
'@id': {
|
|
236
405
|
type: 'string',
|
|
237
406
|
format: 'uri',
|
|
238
|
-
pattern: `^${this.configuration.baseURL.replace(Swagger_1.RE_SLASH, '\\/').replace(Swagger_1.RE_DOT, '\\.')}id\\/${label}\\/\\d`,
|
|
239
407
|
},
|
|
408
|
+
/* Do not allow double typing for strict validation and problems with Swagger tooling (discriminator keyword) */
|
|
240
409
|
'@type': {
|
|
241
|
-
type: '
|
|
242
|
-
description:
|
|
243
|
-
|
|
244
|
-
type: 'string',
|
|
245
|
-
description: `${label} type`,
|
|
246
|
-
/* enum validation breaks codegen, see https://vlaamseoverheid.atlassian.net/browse/DATAST-46 */
|
|
247
|
-
},
|
|
248
|
-
minItems: 1,
|
|
410
|
+
type: 'string',
|
|
411
|
+
description: `Object type (klasse ${label})`,
|
|
412
|
+
pattern: `^${label}\$`,
|
|
249
413
|
},
|
|
250
414
|
...attributes[label],
|
|
415
|
+
...context,
|
|
251
416
|
},
|
|
252
|
-
required: ['@
|
|
417
|
+
required: ['@type', ...requiredAttributes[label]],
|
|
253
418
|
};
|
|
254
419
|
}
|
|
255
|
-
return
|
|
420
|
+
return schemas;
|
|
421
|
+
}
|
|
422
|
+
createLinks() {
|
|
423
|
+
var _a, _b, _c, _d;
|
|
424
|
+
let links = {};
|
|
425
|
+
/* Create link for each Class, datatypes are always nested. */
|
|
426
|
+
for (const classId of this.store.findSubjects(core_1.ns.rdf('type'), core_1.ns.owl('Class'))) {
|
|
427
|
+
let label = (_a = (0, core_1.getApplicationProfileLabel)(classId, this.store, this.configuration.language)) === null || _a === void 0 ? void 0 : _a.value;
|
|
428
|
+
/* Abstract classes may not be instantiated so they cannot have links */
|
|
429
|
+
if (this.store.isAbstractClass(classId))
|
|
430
|
+
continue;
|
|
431
|
+
/* Get all attributes in a recursive manner for inheritance */
|
|
432
|
+
let attributeIds = [];
|
|
433
|
+
attributeIds = (0, core_1.findAllAttributes)(classId, attributeIds, this.store, this.logger);
|
|
434
|
+
if (!label) {
|
|
435
|
+
this.logger.error(`Unknown class label for ${classId.value}`);
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
/* Class labels should be always pascal cased */
|
|
439
|
+
label = (0, core_1.toPascalCase)(label);
|
|
440
|
+
/* Find all attributes for object */
|
|
441
|
+
for (const attributeId of attributeIds) {
|
|
442
|
+
let attributeLabel = (_b = (0, core_1.getApplicationProfileLabel)(attributeId, this.store, this.configuration.language)) === null || _b === void 0 ? void 0 : _b.value;
|
|
443
|
+
const attributeRangeId = this.store.getRange(attributeId);
|
|
444
|
+
if (!attributeLabel) {
|
|
445
|
+
this.logger.error(`Unknown label for attribute ${attributeId.value}`);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
/* Attribute labels should be always camel cased */
|
|
449
|
+
attributeLabel = (0, core_1.toCamelCase)(attributeLabel);
|
|
450
|
+
if (!attributeRangeId) {
|
|
451
|
+
this.logger.error(`Unknown range for attribute ${attributeId.value}`);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const attributeDatatypeId = (_c = this.store.getAssignedUri(attributeRangeId)) === null || _c === void 0 ? void 0 : _c.value;
|
|
455
|
+
let attributeDatatypeLabel = (_d = (0, core_1.getApplicationProfileLabel)(attributeRangeId, this.store, this.configuration.language)) === null || _d === void 0 ? void 0 : _d.value;
|
|
456
|
+
if (!attributeDatatypeId || !attributeDatatypeLabel) {
|
|
457
|
+
this.logger.error(`Unknown datatype for attribute ${attributeId.value}`);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
attributeDatatypeLabel = (0, core_1.toPascalCase)(attributeDatatypeLabel);
|
|
461
|
+
/* Create all possible links for endpoint */
|
|
462
|
+
if (!(0, core_1.isStandardDatatype)(attributeDatatypeId)) {
|
|
463
|
+
links[`${label}.${attributeLabel}`] = {
|
|
464
|
+
operationId: `${label}GET`,
|
|
465
|
+
parameters: {
|
|
466
|
+
/* Referring to response body always starts with the same prefix */
|
|
467
|
+
id: `$response.body#/${label}.${attributeLabel}/@id`,
|
|
468
|
+
},
|
|
469
|
+
description: `De waarde van het attribuut \`@id\` kan gebruikt worden om het gerefereerde object van het type \`${attributeDatatypeLabel}\` op te halen.`,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return links;
|
|
256
475
|
}
|
|
257
|
-
async
|
|
258
|
-
const data = JSON.stringify(
|
|
259
|
-
|
|
476
|
+
async writeJSON(json, outputPath) {
|
|
477
|
+
const data = JSON.stringify(json, null, 2);
|
|
478
|
+
const filePath = path.join(this.configuration.output, outputPath);
|
|
479
|
+
(0, core_1.ensureOutputDirectory)(path.dirname(filePath));
|
|
480
|
+
await (0, promises_1.writeFile)(filePath, data);
|
|
260
481
|
}
|
|
261
482
|
};
|
|
262
483
|
SwaggerGenerationService = __decorate([
|
|
@@ -10,7 +10,6 @@ exports.SwaggerGenerationServiceConfiguration = void 0;
|
|
|
10
10
|
const inversify_1 = require("inversify");
|
|
11
11
|
let SwaggerGenerationServiceConfiguration = class SwaggerGenerationServiceConfiguration {
|
|
12
12
|
async createFromCli(params) {
|
|
13
|
-
console.log(params.title);
|
|
14
13
|
this._input = params.input;
|
|
15
14
|
this._output = params.output;
|
|
16
15
|
this._language = params.language;
|
|
@@ -67,13 +66,13 @@ let SwaggerGenerationServiceConfiguration = class SwaggerGenerationServiceConfig
|
|
|
67
66
|
}
|
|
68
67
|
get contextURL() {
|
|
69
68
|
if (!this._contextURL) {
|
|
70
|
-
throw new Error(`Trying access property "contextURL" before it was set.`);
|
|
69
|
+
throw new Error(`Trying to access property "contextURL" before it was set.`);
|
|
71
70
|
}
|
|
72
71
|
return this._contextURL;
|
|
73
72
|
}
|
|
74
73
|
get baseURL() {
|
|
75
74
|
if (!this._baseURL) {
|
|
76
|
-
throw new Error(`Trying access property "baseURL" before it was set.`);
|
|
75
|
+
throw new Error(`Trying to access property "baseURL" before it was set.`);
|
|
77
76
|
}
|
|
78
77
|
return this._baseURL;
|
|
79
78
|
}
|
package/lib/constants/Swagger.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
exports.PROBLEEM_DETAILS_SCHEMA_REF = exports.ERROR_RESPONSES = void 0;
|
|
4
|
+
exports.ERROR_RESPONSES = [
|
|
5
|
+
{ code: '400', description: 'Invalid data supplied.' },
|
|
6
|
+
{ code: '401', description: 'Invalid authorization.' },
|
|
7
|
+
{ code: '403', description: 'Authentication failed.' },
|
|
8
|
+
{ code: '404', description: 'Resource not found.' },
|
|
9
|
+
{ code: '412', description: 'Pre-condition failed.' },
|
|
10
|
+
{ code: '500', description: 'Unexpected Server Error.' },
|
|
11
|
+
{ code: '502', description: 'Bad Gateway.' },
|
|
12
|
+
{ code: '503', description: 'Service unavailable.' },
|
|
13
|
+
{ code: '504', description: 'Gateway Timeout.' },
|
|
14
|
+
];
|
|
15
|
+
exports.PROBLEEM_DETAILS_SCHEMA_REF = '#/components/schemas/ProbleemDetails';
|
|
7
16
|
//# sourceMappingURL=Swagger.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const mapProperties: (datatype: string, label: string,
|
|
1
|
+
export declare const mapProperties: (datatype: string, label: string, subclasses: string[], abstract: boolean) => object;
|
package/lib/enums/Properties.js
CHANGED
|
@@ -1,47 +1,70 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mapProperties = void 0;
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-loss-of-precision */
|
|
4
5
|
const core_1 = require("@oslo-flanders/core");
|
|
5
|
-
|
|
6
|
-
/* eslint-disable */
|
|
6
|
+
/* eslint-disable max-len */
|
|
7
7
|
const Properties = new Map([
|
|
8
|
-
[core_1.ns.rdf('langString').value, { '@value': { type: 'string' }, '@language': { type: 'string',
|
|
8
|
+
[core_1.ns.rdf('langString').value, { '@value': { type: 'string' }, '@language': { type: 'string', pattern: '^nl$' } }],
|
|
9
|
+
[core_1.ns.rdfs('Literal').value, { '@value': { type: 'string' } }],
|
|
9
10
|
[core_1.ns.xsd('string').value, { '@value': { type: 'string' } }],
|
|
10
11
|
[core_1.ns.xsd('anyURI').value, { '@id': { type: 'string', format: 'uri' } }],
|
|
11
|
-
[core_1.ns.xsd('dateTime').value, { '@value': { type: 'string', format: 'date-time' }, '@type': { type: 'string',
|
|
12
|
-
[core_1.ns.xsd('date').value, { '@value': { type: 'string', format: 'date' }, '@type': { type: 'string',
|
|
13
|
-
[core_1.ns.xsd('boolean').value, { '@value': { type: 'boolean' }, '@type': { type: 'string',
|
|
14
|
-
[core_1.ns.xsd('integer').value, { '@value': { type: 'number', format: 'int64' }, '@type': { type: 'string',
|
|
15
|
-
[core_1.ns.xsd('long').value, { '@value': { type: 'number', format: 'int64', minimum: -
|
|
16
|
-
[core_1.ns.xsd('float').value, { '@value': { type: 'number', format: 'float' }, '@type': { type: 'string',
|
|
17
|
-
[core_1.ns.xsd('double').value, { '@value': { type: 'number', format: 'double' }, '@type': { type: 'string',
|
|
18
|
-
[core_1.ns.xsd('int').value, { '@value': { type: 'number', format: 'int32', minimum: -2147483648, maximum: 2147483647 }, '@type': { type: 'string',
|
|
19
|
-
[core_1.ns.xsd('short').value, { '@value': { type: 'number', format: 'int32', minimum: -32768, maximum: 32767 }, '@type': { type: 'string',
|
|
20
|
-
[core_1.ns.xsd('byte').value, { '@value': { type: 'number', format: 'int32', minimum: -128, maximum: 127 }, '@type': { type: 'string',
|
|
21
|
-
[core_1.ns.xsd('hexBinary').value, { '@value': { type: 'string', format: 'binary' }, '@type': { type: 'string',
|
|
22
|
-
[core_1.ns.xsd('base64Binary').value, { '@value': { type: 'string', format: 'byte' }, '@type': { type: 'string',
|
|
23
|
-
[core_1.ns.xsd('nonNegativeInteger').value, { '@value': { type: 'number', format: 'int64' }, '@type': { type: 'string',
|
|
24
|
-
[core_1.ns.xsd('nonPositiveInteger').value, { '@value': { type: 'number', format: 'int64' }, '@type': { type: 'string',
|
|
25
|
-
[core_1.ns.xsd('negativeInteger').value, { '@value': { type: 'number', format: 'int64', maximum: 0 }, '@type': { type: 'string',
|
|
26
|
-
[core_1.ns.xsd('positiveInteger').value, { '@value': { type: 'number', format: 'int64', minimum: 1 }, '@type': { type: 'string',
|
|
27
|
-
[core_1.ns.xsd('unsignedLong').value, { '@value': { type: 'number', format: 'int64', minimum: 0, maximum:
|
|
28
|
-
[core_1.ns.xsd('unsignedInt').value, { '@value': { type: 'number', format: 'int32', minimum: 0, maximum: 4294967295 }, '@type': { type: 'string',
|
|
29
|
-
[core_1.ns.xsd('unsignedShort').value, { '@value': { type: 'number', format: 'int32', minimum: 0, maximum: 65535 }, '@type': { type: 'string',
|
|
30
|
-
[core_1.ns.xsd('unsignedByte').value, { '@value': { type: 'number', format: 'int32', minimum: 0, maximum: 255 }, '@type': { type: 'string',
|
|
31
|
-
[core_1.ns.xsd('decimal').value, { '@value': { type: 'string', pattern: '(+|-)?([0-9]+(.[0-9]*)?|.[0-9]+)' }, '@type': { type: 'string',
|
|
12
|
+
[core_1.ns.xsd('dateTime').value, { '@value': { type: 'string', format: 'date-time' }, '@type': { type: 'string', pattern: '^DateTime$' } }],
|
|
13
|
+
[core_1.ns.xsd('date').value, { '@value': { type: 'string', format: 'date' }, '@type': { type: 'string', pattern: '^Date$' } }],
|
|
14
|
+
[core_1.ns.xsd('boolean').value, { '@value': { type: 'boolean' }, '@type': { type: 'string', pattern: '^Boolean$' } }],
|
|
15
|
+
[core_1.ns.xsd('integer').value, { '@value': { type: 'number', format: 'int64' }, '@type': { type: 'string', pattern: '^Integer$' } }],
|
|
16
|
+
[core_1.ns.xsd('long').value, { '@value': { type: 'number', format: 'int64', minimum: -9223372036854776000, maximum: 9223372036854776000 }, '@type': { type: 'string', pattern: '^Long$' } }],
|
|
17
|
+
[core_1.ns.xsd('float').value, { '@value': { type: 'number', format: 'float' }, '@type': { type: 'string', pattern: '^Float$' } }],
|
|
18
|
+
[core_1.ns.xsd('double').value, { '@value': { type: 'number', format: 'double' }, '@type': { type: 'string', pattern: '^Double$' } }],
|
|
19
|
+
[core_1.ns.xsd('int').value, { '@value': { type: 'number', format: 'int32', minimum: -2147483648, maximum: 2147483647 }, '@type': { type: 'string', pattern: '^Int$' } }],
|
|
20
|
+
[core_1.ns.xsd('short').value, { '@value': { type: 'number', format: 'int32', minimum: -32768, maximum: 32767 }, '@type': { type: 'string', pattern: '^Short$' } }],
|
|
21
|
+
[core_1.ns.xsd('byte').value, { '@value': { type: 'number', format: 'int32', minimum: -128, maximum: 127 }, '@type': { type: 'string', pattern: '^Byte$' } }],
|
|
22
|
+
[core_1.ns.xsd('hexBinary').value, { '@value': { type: 'string', format: 'binary' }, '@type': { type: 'string', pattern: '^HexBinary$' } }],
|
|
23
|
+
[core_1.ns.xsd('base64Binary').value, { '@value': { type: 'string', format: 'byte' }, '@type': { type: 'string', pattern: '^Base64Binary$' } }],
|
|
24
|
+
[core_1.ns.xsd('nonNegativeInteger').value, { '@value': { type: 'number', format: 'int64' }, '@type': { type: 'string', pattern: '^NonNegativeInteger$' } }],
|
|
25
|
+
[core_1.ns.xsd('nonPositiveInteger').value, { '@value': { type: 'number', format: 'int64' }, '@type': { type: 'string', pattern: '^NonPositiveInteger$' } }],
|
|
26
|
+
[core_1.ns.xsd('negativeInteger').value, { '@value': { type: 'number', format: 'int64', maximum: 0 }, '@type': { type: 'string', pattern: '^NegativeInteger$' } }],
|
|
27
|
+
[core_1.ns.xsd('positiveInteger').value, { '@value': { type: 'number', format: 'int64', minimum: 1 }, '@type': { type: 'string', pattern: '^PositiveInteger$' } }],
|
|
28
|
+
[core_1.ns.xsd('unsignedLong').value, { '@value': { type: 'number', format: 'int64', minimum: 0, maximum: 18446744073709552000 }, '@type': { type: 'string', pattern: '^UnsignedLong$' } }],
|
|
29
|
+
[core_1.ns.xsd('unsignedInt').value, { '@value': { type: 'number', format: 'int32', minimum: 0, maximum: 4294967295 }, '@type': { type: 'string', pattern: '^UnsignedInt$' } }],
|
|
30
|
+
[core_1.ns.xsd('unsignedShort').value, { '@value': { type: 'number', format: 'int32', minimum: 0, maximum: 65535 }, '@type': { type: 'string', pattern: '^UnsignedShort$' } }],
|
|
31
|
+
[core_1.ns.xsd('unsignedByte').value, { '@value': { type: 'number', format: 'int32', minimum: 0, maximum: 255 }, '@type': { type: 'string', pattern: '^UnsignedByte$' } }],
|
|
32
|
+
[core_1.ns.xsd('decimal').value, { '@value': { type: 'string', pattern: '(+|-)?([0-9]+(.[0-9]*)?|.[0-9]+)' }, '@type': { type: 'string', pattern: '^Decimal$' } }],
|
|
32
33
|
]);
|
|
33
|
-
/* eslint-enable */
|
|
34
|
-
const mapProperties = (datatype, label,
|
|
34
|
+
/* eslint-enable max-len*/
|
|
35
|
+
const mapProperties = (datatype, label, subclasses, abstract) => {
|
|
36
|
+
/* Primitive data type conversion from Linked Data to Swagger */
|
|
35
37
|
if (Properties.has(datatype)) {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
36
39
|
return Properties.get(datatype);
|
|
37
40
|
}
|
|
41
|
+
/* Multiple subclasses requires a discriminator to link both the superclass and subclasses as schemas */
|
|
42
|
+
if (subclasses.length > 0) {
|
|
43
|
+
const mapping = {};
|
|
44
|
+
const oneOf = [];
|
|
45
|
+
/* Only allow super class if it is not abstract */
|
|
46
|
+
if (!abstract) {
|
|
47
|
+
mapping[label] = `#/components/schemas/${label}`;
|
|
48
|
+
oneOf.push({ $ref: `#/components/schemas/${label}` });
|
|
49
|
+
}
|
|
50
|
+
for (const subclass of subclasses) {
|
|
51
|
+
const ref = `#/components/schemas/${subclass}`;
|
|
52
|
+
oneOf.push({ $ref: ref });
|
|
53
|
+
mapping[subclass] = ref;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
oneOf,
|
|
57
|
+
discriminator: {
|
|
58
|
+
propertyName: '@type',
|
|
59
|
+
mapping,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/* Regular schemas without any subclassing */
|
|
38
64
|
return {
|
|
39
|
-
|
|
40
|
-
type: 'string',
|
|
41
|
-
format: 'uri',
|
|
42
|
-
pattern: `^${baseURI.replace(Swagger_1.RE_SLASH, '\\/').replace(Swagger_1.RE_DOT, '\\.')}id\\/${label}\\/\\d`,
|
|
43
|
-
},
|
|
65
|
+
$ref: `#/components/schemas/${label}`,
|
|
44
66
|
};
|
|
45
67
|
};
|
|
46
68
|
exports.mapProperties = mapProperties;
|
|
69
|
+
/* eslint-enable */
|
|
47
70
|
//# sourceMappingURL=Properties.js.map
|
package/lib/types/Swagger.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export interface SwaggerRoot {
|
|
|
2
2
|
openapi: string;
|
|
3
3
|
info: SwaggerInfo;
|
|
4
4
|
servers: SwaggerServer[];
|
|
5
|
-
paths:
|
|
6
|
-
components
|
|
5
|
+
paths: Record<string, SwaggerPath>;
|
|
6
|
+
components?: SwaggerComponents;
|
|
7
7
|
}
|
|
8
8
|
export interface SwaggerInfo {
|
|
9
9
|
title: string;
|
|
@@ -25,68 +25,69 @@ export interface SwaggerServer {
|
|
|
25
25
|
url: string;
|
|
26
26
|
description?: string;
|
|
27
27
|
}
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
export interface SwaggerComponents {
|
|
29
|
+
schemas: Record<string, SwaggerSchema>;
|
|
30
|
+
links?: Record<string, SwaggerLink>;
|
|
31
|
+
}
|
|
32
|
+
export interface SwaggerPath {
|
|
33
|
+
get?: SwaggerPathOperation;
|
|
34
|
+
post?: SwaggerPathOperation;
|
|
35
|
+
put?: SwaggerPathOperation;
|
|
36
|
+
delete?: SwaggerPathOperation;
|
|
37
|
+
patch?: SwaggerPathOperation;
|
|
35
38
|
}
|
|
36
|
-
export interface
|
|
39
|
+
export interface SwaggerPathOperation {
|
|
37
40
|
tags?: string[];
|
|
38
41
|
summary?: string;
|
|
39
42
|
description?: string;
|
|
40
43
|
operationId?: string;
|
|
41
|
-
parameters?:
|
|
42
|
-
requestBody?:
|
|
43
|
-
responses
|
|
44
|
+
parameters?: SwaggerParameter[];
|
|
45
|
+
requestBody?: SwaggerRequestBody;
|
|
46
|
+
responses: Record<string, SwaggerResponse>;
|
|
44
47
|
}
|
|
45
|
-
export interface
|
|
46
|
-
name
|
|
48
|
+
export interface SwaggerParameter {
|
|
49
|
+
name: string;
|
|
47
50
|
description?: string;
|
|
48
51
|
in: string;
|
|
49
52
|
required: boolean;
|
|
50
|
-
schema:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
type: string;
|
|
53
|
+
schema: {
|
|
54
|
+
type: string;
|
|
55
|
+
};
|
|
54
56
|
}
|
|
55
|
-
export interface
|
|
57
|
+
export interface SwaggerRequestBody {
|
|
56
58
|
description?: string;
|
|
57
|
-
content:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
content: Record<string, {
|
|
60
|
+
schema: SwaggerSchema | SchemaRef;
|
|
61
|
+
example?: Record<string, string>;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
export interface SwaggerResponse {
|
|
65
|
+
description: string;
|
|
66
|
+
content?: Record<string, {
|
|
67
|
+
schema: SchemaRef;
|
|
68
|
+
}>;
|
|
69
|
+
links?: Record<string, SwaggerLink>;
|
|
70
|
+
}
|
|
71
|
+
export interface SwaggerSchema {
|
|
72
|
+
title?: string;
|
|
65
73
|
type: string;
|
|
66
|
-
}
|
|
67
|
-
export type SwaggerPathsOperationRequestBodyContentValueExample = Record<string, string>;
|
|
68
|
-
export interface SwaggerPathsOperationResponses {
|
|
69
|
-
}
|
|
70
|
-
export interface SwaggerComponents {
|
|
71
|
-
schemas: SwaggerComponentsSchemas;
|
|
72
|
-
}
|
|
73
|
-
export type SwaggerComponentsSchemas = Record<string, SwaggerComponentsSchemasValue>;
|
|
74
|
-
export interface SwaggerComponentsSchemasValue {
|
|
75
|
-
type?: string;
|
|
76
74
|
description?: string;
|
|
77
|
-
|
|
75
|
+
pattern?: string;
|
|
76
|
+
format?: string;
|
|
77
|
+
minimum?: number;
|
|
78
|
+
maximum?: number;
|
|
79
|
+
minItems?: number;
|
|
80
|
+
maxItems?: number;
|
|
81
|
+
enum?: string[];
|
|
82
|
+
items?: SwaggerSchema | SchemaRef;
|
|
83
|
+
properties?: Record<string, SwaggerSchema | SchemaRef>;
|
|
78
84
|
required?: string[];
|
|
79
85
|
}
|
|
80
|
-
export
|
|
81
|
-
|
|
82
|
-
type: string;
|
|
83
|
-
description?: string;
|
|
84
|
-
enum?: string[];
|
|
85
|
-
items?: SwaggerComponentsValuePropertiesValueItems;
|
|
86
|
+
export interface SchemaRef {
|
|
87
|
+
$ref: string;
|
|
86
88
|
}
|
|
87
|
-
export interface
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
required?: string[];
|
|
89
|
+
export interface SwaggerLink {
|
|
90
|
+
operationId: string;
|
|
91
|
+
parameters: Record<string, string>;
|
|
92
|
+
description: string;
|
|
92
93
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
1
|
+
import type { SwaggerLink, SwaggerResponse, SwaggerInfoContact, SwaggerInfoLicense } from '../types/Swagger';
|
|
2
|
+
export declare function getContact(contactName?: string, contactURL?: string, contactEmail?: string): SwaggerInfoContact | undefined;
|
|
3
|
+
export declare function getLicense(licenseName?: string, licenseURL?: string): SwaggerInfoLicense | undefined;
|
|
4
|
+
export declare function buildErrorResponses(label: string): Record<string, SwaggerResponse>;
|
|
5
|
+
export declare function filterLinksByClass(links: Record<string, SwaggerLink>, classLabel: string): Record<string, SwaggerLink>;
|
|
@@ -1,32 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
exports.filterLinksByClass = exports.buildErrorResponses = exports.getLicense = exports.getContact = void 0;
|
|
4
|
+
/* eslint-disable max-len */
|
|
5
|
+
const OutputFormat_1 = require("@oslo-flanders/core/lib/enums/OutputFormat");
|
|
6
|
+
const Swagger_1 = require("../constants/Swagger");
|
|
7
|
+
function getProbleemdetailsContent() {
|
|
8
|
+
return {
|
|
9
|
+
[OutputFormat_1.OutputFormat.JsonProblem]: {
|
|
10
|
+
schema: { $ref: Swagger_1.PROBLEEM_DETAILS_SCHEMA_REF },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function getProbleemdetailsLink(label) {
|
|
15
|
+
return {
|
|
16
|
+
'ProbleemDetails.type': {
|
|
17
|
+
operationId: `${label}GET`,
|
|
18
|
+
parameters: { type: '$response.body#/type' },
|
|
19
|
+
description: 'De waarde van het attribuut `type` kan gebruikt worden om het gerefereerde object van het type `ProblemDetails` op te halen.',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
15
22
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
function getContact(contactName, contactURL, contactEmail) {
|
|
24
|
+
if (!contactName && !contactURL && !contactEmail)
|
|
25
|
+
return undefined;
|
|
26
|
+
return { name: contactName, url: contactURL, email: contactEmail };
|
|
19
27
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
exports.getContact = getContact;
|
|
29
|
+
function getLicense(licenseName, licenseURL) {
|
|
30
|
+
if (!licenseName && !licenseURL)
|
|
31
|
+
return undefined;
|
|
32
|
+
return { name: licenseName, url: licenseURL };
|
|
33
|
+
}
|
|
34
|
+
exports.getLicense = getLicense;
|
|
35
|
+
function buildErrorResponses(label) {
|
|
36
|
+
const responses = {};
|
|
37
|
+
for (const { code, description } of Swagger_1.ERROR_RESPONSES) {
|
|
38
|
+
responses[code] = {
|
|
39
|
+
description,
|
|
40
|
+
content: getProbleemdetailsContent(),
|
|
41
|
+
links: getProbleemdetailsLink(label),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return responses;
|
|
24
45
|
}
|
|
25
|
-
exports.
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.replace(/\s+/gu, '');
|
|
46
|
+
exports.buildErrorResponses = buildErrorResponses;
|
|
47
|
+
function filterLinksByClass(links, classLabel) {
|
|
48
|
+
const prefix = `${classLabel}.`;
|
|
49
|
+
return Object.fromEntries(Object.entries(links).filter(([key]) => key.startsWith(prefix)));
|
|
30
50
|
}
|
|
31
|
-
exports.
|
|
51
|
+
exports.filterLinksByClass = filterLinksByClass;
|
|
52
|
+
/* eslint-enable*/
|
|
32
53
|
//# sourceMappingURL=swaggerUtils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslo-flanders/swagger-generator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Generate OpenAPI Swagger YAML documentation.",
|
|
5
5
|
"author": "Digitaal Vlaanderen <https://data.vlaanderen.be/id/organisatie/OVO002949>",
|
|
6
6
|
"homepage": "https://github.com/informatievlaanderen/OSLO-UML-Transformer/tree/main/packages/oslo-generator-swagger#readme",
|