@kevinoid/openapi-transformers 0.1.0
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/LICENSE.txt +19 -0
- package/README.md +134 -0
- package/add-tag-to-operation-ids.js +60 -0
- package/add-x-ms-enum-name.js +96 -0
- package/add-x-ms-enum-value-names.js +142 -0
- package/additional-properties-to-object.js +35 -0
- package/additional-properties-to-unconstrained.js +115 -0
- package/any-of-null-to-nullable.js +50 -0
- package/assert-properties.js +56 -0
- package/binary-string-to-file.js +54 -0
- package/bool-enum-to-bool.js +100 -0
- package/clear-html-response-schema.js +77 -0
- package/client-params-to-global.js +97 -0
- package/const-to-enum.js +49 -0
- package/escape-enum-values.js +211 -0
- package/exclusive-min-max-to-bool.js +61 -0
- package/format-to-type.js +54 -0
- package/index.js +94 -0
- package/inline-non-object-schemas.js +120 -0
- package/lib/component-manager.js +60 -0
- package/lib/matching-component-manager.js +74 -0
- package/lib/matching-parameter-manager.js +36 -0
- package/merge-all-of.js +60 -0
- package/merge-any-of.js +48 -0
- package/merge-one-of.js +48 -0
- package/nullable-not-required.js +240 -0
- package/nullable-to-type-null.js +46 -0
- package/openapi31to30.js +54 -0
- package/package.json +131 -0
- package/path-parameters-to-operations.js +63 -0
- package/pattern-properties-to-additional-properties.js +62 -0
- package/queries-to-x-ms-paths.js +63 -0
- package/read-only-not-required.js +111 -0
- package/ref-path-parameters.js +73 -0
- package/remove-default-only-response-produces.js +58 -0
- package/remove-paths-with-servers.js +34 -0
- package/remove-query-from-paths.js +526 -0
- package/remove-ref-siblings.js +78 -0
- package/remove-request-body.js +102 -0
- package/remove-response-headers.js +42 -0
- package/remove-security-scheme-if.js +166 -0
- package/remove-type-if.js +65 -0
- package/rename-components.js +285 -0
- package/replaced-by-to-description.js +50 -0
- package/server-vars-to-path-params.js +224 -0
- package/server-vars-to-x-ms-parameterized-host.js +247 -0
- package/type-null-to-enum.js +47 -0
- package/type-null-to-nullable.js +57 -0
- package/urlencoded-to-string.js +160 -0
- package/x-enum-to-ms.js +92 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright 2025 Kevin Locke <kevin@kevinlocke.name>
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @module "openapi-transformers/assert-properties.js"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import OpenApiTransformerBase from 'openapi-transformer-base';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Transformer which throws an Error when given properties are present in the
|
|
11
|
+
* OpenAPI document.
|
|
12
|
+
*/
|
|
13
|
+
export default class AssertPropertiesTransformer
|
|
14
|
+
extends OpenApiTransformerBase {
|
|
15
|
+
options = {};
|
|
16
|
+
|
|
17
|
+
constructor(options) {
|
|
18
|
+
super();
|
|
19
|
+
|
|
20
|
+
if (typeof options !== 'object' || options === null) {
|
|
21
|
+
throw new TypeError('options must be an object');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof options.schema !== 'object' || options.schema === null) {
|
|
25
|
+
throw new TypeError('options.schema must be an object');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!Array.isArray(options.schema.excludes)) {
|
|
29
|
+
throw new TypeError('options.schema.excludes must be an Array');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!options.schema.excludes.every((s) => typeof s === 'string')) {
|
|
33
|
+
throw new TypeError(
|
|
34
|
+
'options.schema.excludes must only contain property names',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.options = options;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
transformSchema(schema) {
|
|
42
|
+
const newSchema = super.transformSchema(schema);
|
|
43
|
+
if (typeof newSchema !== 'object' || newSchema === null) {
|
|
44
|
+
return newSchema;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const propName of this.options.schema.excludes) {
|
|
48
|
+
if (newSchema[propName] !== undefined
|
|
49
|
+
&& Object.hasOwn(newSchema, propName)) {
|
|
50
|
+
throw new Error(`Schema contains ${propName}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return newSchema;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright 2019 Kevin Locke <kevin@kevinlocke.name>
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @module "openapi-transformers/binary-string-to-file.js"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import OpenApiTransformerBase from 'openapi-transformer-base';
|
|
8
|
+
|
|
9
|
+
function transformSchemaType(schema) {
|
|
10
|
+
if (schema.type === 'string'
|
|
11
|
+
&& (schema.format === 'binary' || schema.format === 'file')) {
|
|
12
|
+
const newSchema = {
|
|
13
|
+
...schema,
|
|
14
|
+
type: 'file',
|
|
15
|
+
};
|
|
16
|
+
delete newSchema.format;
|
|
17
|
+
return newSchema;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return schema;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Transformer to replace `type: string, format: binary` (or `format: file`)
|
|
25
|
+
* with `type: file` so that Autorest generates to a Stream instead of string.
|
|
26
|
+
*/
|
|
27
|
+
export default class BinaryStringToFileTransformer
|
|
28
|
+
extends OpenApiTransformerBase {
|
|
29
|
+
// eslint-disable-next-line class-methods-use-this
|
|
30
|
+
transformSchema(schema) {
|
|
31
|
+
// Don't call super, since `type: file` is only allowed on root schema of
|
|
32
|
+
// response.
|
|
33
|
+
// Note: No checking is done to enforce this (due to $ref complications).
|
|
34
|
+
// Note: A response could $ref a property schema. Unlikely.
|
|
35
|
+
return transformSchemaType(schema);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line class-methods-use-this
|
|
39
|
+
transformParameter(parameter) {
|
|
40
|
+
if (parameter === null || typeof parameter !== 'object') {
|
|
41
|
+
return parameter;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let newParameter = transformSchemaType(parameter);
|
|
45
|
+
if (newParameter.schema) {
|
|
46
|
+
newParameter = {
|
|
47
|
+
...newParameter,
|
|
48
|
+
schema: transformSchemaType(newParameter.schema),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return newParameter;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright 2021 Kevin Locke <kevin@kevinlocke.name>
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @module "openapi-transformers/bool-enum-to-bool.js"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import OpenApiTransformerBase from 'openapi-transformer-base';
|
|
8
|
+
|
|
9
|
+
const inStringContextSymbol = Symbol('inStringContext');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Transformer to replace `enum: [true, false]` with `type: boolean` for
|
|
13
|
+
* simplicity and to assist generators.
|
|
14
|
+
*/
|
|
15
|
+
export default class BoolEnumToBoolTransformer
|
|
16
|
+
extends OpenApiTransformerBase {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this[inStringContextSymbol] = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
transformSchemaLike(schema) {
|
|
23
|
+
if (schema === null
|
|
24
|
+
|| typeof schema !== 'object'
|
|
25
|
+
|| Array.isArray(schema)) {
|
|
26
|
+
return schema;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { enum: enumValues, ...schemaNoEnum } = schema;
|
|
30
|
+
if (!Array.isArray(enumValues) || enumValues.length < 2) {
|
|
31
|
+
return schema;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { type } = schema;
|
|
35
|
+
if (!this[inStringContextSymbol]
|
|
36
|
+
&& type !== 'boolean'
|
|
37
|
+
&& !enumValues.every((ev) => typeof ev === 'boolean')) {
|
|
38
|
+
// If schema validates non-boolean values in a type-sensitive context,
|
|
39
|
+
// limiting to boolean would change validation.
|
|
40
|
+
return schema;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!(enumValues.includes(true) || enumValues.includes('true'))
|
|
44
|
+
|| !(enumValues.includes(false) || enumValues.includes('false'))
|
|
45
|
+
|| enumValues.some((ev) => ev !== true
|
|
46
|
+
&& ev !== false
|
|
47
|
+
&& ev !== 'true'
|
|
48
|
+
&& ev !== 'false')) {
|
|
49
|
+
// Enum must contain both true and false, and only true/false
|
|
50
|
+
return schema;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
schemaNoEnum.type = 'boolean';
|
|
54
|
+
|
|
55
|
+
return schemaNoEnum;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
transformSchema(schema) {
|
|
59
|
+
return this.transformSchemaLike(super.transformSchema(schema));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
transformItems(items) {
|
|
63
|
+
return this.transformSchemaLike(super.transformItems(items));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
transformHeader(header) {
|
|
67
|
+
const prevContext = this[inStringContextSymbol];
|
|
68
|
+
try {
|
|
69
|
+
this[inStringContextSymbol] = true;
|
|
70
|
+
return this.transformSchemaLike(super.transformHeader(header));
|
|
71
|
+
} finally {
|
|
72
|
+
this[inStringContextSymbol] = prevContext;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
transformParameter(parameter) {
|
|
77
|
+
const prevContext = this[inStringContextSymbol];
|
|
78
|
+
try {
|
|
79
|
+
this[inStringContextSymbol] = parameter.in !== 'body';
|
|
80
|
+
return this.transformSchemaLike(super.transformParameter(parameter));
|
|
81
|
+
} finally {
|
|
82
|
+
this[inStringContextSymbol] = prevContext;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
transformMediaType(mediaType) {
|
|
87
|
+
const prevContext = this[inStringContextSymbol];
|
|
88
|
+
const mediaTypeStr = this.transformPath.at(-1);
|
|
89
|
+
try {
|
|
90
|
+
this[inStringContextSymbol] =
|
|
91
|
+
mediaTypeStr === 'application/x-www-form-urlencoded'
|
|
92
|
+
|| mediaTypeStr === 'multipart/form-data'
|
|
93
|
+
|| mediaTypeStr === 'text/csv'
|
|
94
|
+
|| mediaTypeStr === 'text/plain';
|
|
95
|
+
return super.transformMediaType(mediaType);
|
|
96
|
+
} finally {
|
|
97
|
+
this[inStringContextSymbol] = prevContext;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright 2020 Kevin Locke <kevin@kevinlocke.name>
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @module "openapi-transformers/clear-html-response-schema.js"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import OpenApiTransformerBase from 'openapi-transformer-base';
|
|
8
|
+
|
|
9
|
+
function isHtml(mediaType) {
|
|
10
|
+
return /^\s*text\/html\s*(?:;.*)?$/i.test(mediaType);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Transformer to remove response content for the text/html media type.
|
|
15
|
+
*
|
|
16
|
+
* Autorest does not provide a good way to consume text/html:
|
|
17
|
+
* - If the schema has type: string (with or without format: binary), the
|
|
18
|
+
* generated method attempts to JSON-decode the HTML, which fails.
|
|
19
|
+
* - If the schema has type: file, Autorest calls HttpContent.ReadAsStreamAsync
|
|
20
|
+
* and returns the Stream, which complicates conversion to string, since the
|
|
21
|
+
* caller can't call HttpContent.ReadAsStringAsync (which would cause
|
|
22
|
+
* "InvalidOperationException: The stream was already consumed. It cannot be
|
|
23
|
+
* read again.") and must reimplement charset detection to use on Stream.
|
|
24
|
+
*/
|
|
25
|
+
export default class ClearHtmlResponseSchemaTransformer
|
|
26
|
+
extends OpenApiTransformerBase {
|
|
27
|
+
// eslint-disable-next-line class-methods-use-this
|
|
28
|
+
transformResponse(response) {
|
|
29
|
+
if (!response) {
|
|
30
|
+
return response;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let newResponse = response;
|
|
34
|
+
|
|
35
|
+
// Remove schema from OpenAPI 3 Media Type Objects
|
|
36
|
+
const { content, schema } = newResponse;
|
|
37
|
+
if (content) {
|
|
38
|
+
const htmlTypes = Object.keys(content).filter(isHtml);
|
|
39
|
+
if (htmlTypes.length > 0) {
|
|
40
|
+
const newContent = { ...content };
|
|
41
|
+
for (const htmlType of htmlTypes) {
|
|
42
|
+
const newMediaType = { ...content[htmlType] };
|
|
43
|
+
delete newMediaType.schema;
|
|
44
|
+
delete newMediaType.encoding;
|
|
45
|
+
newContent[htmlType] = newMediaType;
|
|
46
|
+
}
|
|
47
|
+
newResponse = {
|
|
48
|
+
...newResponse,
|
|
49
|
+
content: newContent,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Remove schema from OpenAPI 2 response.schema
|
|
55
|
+
if (schema !== undefined) {
|
|
56
|
+
newResponse = { ...newResponse };
|
|
57
|
+
delete newResponse.schema;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return newResponse;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
transformOperation(operation) {
|
|
64
|
+
if (!operation || !operation.responses) {
|
|
65
|
+
return operation;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Only transform OpenAPI 2.0 if the operation only produces HTML
|
|
69
|
+
// (since one schema is shared by all)
|
|
70
|
+
const { produces } = operation;
|
|
71
|
+
if (Array.isArray(produces) && !produces.every(isHtml)) {
|
|
72
|
+
return operation;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return super.transformOperation(operation);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright 2020 Kevin Locke <kevin@kevinlocke.name>
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @module "openapi-transformers/client-params-to-global.js"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Note: Undocumented function which is part of the public API:
|
|
8
|
+
// https://github.com/flitbit/json-ptr/issues/29
|
|
9
|
+
import { encodeUriFragmentIdentifier } from 'json-ptr';
|
|
10
|
+
import OpenApiTransformerBase from 'openapi-transformer-base';
|
|
11
|
+
|
|
12
|
+
import MatchingParameterManager from './lib/matching-parameter-manager.js';
|
|
13
|
+
|
|
14
|
+
const parameterManagerSymbol = Symbol('parameterManager');
|
|
15
|
+
const parametersPathSymbol = Symbol('parametersPath');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Transformer to move parameters with x-ms-parameter-location:client defined on
|
|
19
|
+
* Path Item Objects and Operation Objects to the Components or Definitions
|
|
20
|
+
* Object (which is required for x-ms-parameter-location to have any effect).
|
|
21
|
+
*
|
|
22
|
+
* Note: Authors should define such parameters in Components or Definitions.
|
|
23
|
+
* This script is mostly a workaround for api-spec-converter, which inlines
|
|
24
|
+
* all $ref parameters.
|
|
25
|
+
* https://github.com/Azure/autorest/tree/master/docs/extensions#x-ms-parameter-location
|
|
26
|
+
*/
|
|
27
|
+
export default class ClientParamsToGlobalTransformer
|
|
28
|
+
extends OpenApiTransformerBase {
|
|
29
|
+
transformParameter(parameter) {
|
|
30
|
+
if (!parameter || parameter['x-ms-parameter-location'] !== 'client') {
|
|
31
|
+
return parameter;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const defName = this[parameterManagerSymbol].add(parameter, parameter.name);
|
|
35
|
+
return {
|
|
36
|
+
$ref: encodeUriFragmentIdentifier([
|
|
37
|
+
...this[parametersPathSymbol],
|
|
38
|
+
defName,
|
|
39
|
+
]),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
transformOpenApi(openApi) {
|
|
44
|
+
if (typeof openApi !== 'object' || openApi === null || !openApi.paths) {
|
|
45
|
+
return openApi;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { components, openapi, paths } = openApi;
|
|
49
|
+
if (/^3(?:\.|$)/.test(openapi)
|
|
50
|
+
|| (typeof components === 'object' && components !== null)) {
|
|
51
|
+
const newParameters = { ...components && components.parameters };
|
|
52
|
+
this[parameterManagerSymbol] =
|
|
53
|
+
new MatchingParameterManager(newParameters);
|
|
54
|
+
this[parametersPathSymbol] = ['components', 'parameters'];
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...openApi,
|
|
58
|
+
components: {
|
|
59
|
+
...components,
|
|
60
|
+
parameters: newParameters,
|
|
61
|
+
},
|
|
62
|
+
paths: this.transformPaths(paths),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { parameters, swagger } = openApi;
|
|
67
|
+
if (/^2(?:\.|$)/.test(swagger)
|
|
68
|
+
|| (typeof parameters === 'object' && parameters !== null)) {
|
|
69
|
+
const newParameters = { ...parameters };
|
|
70
|
+
this[parameterManagerSymbol] =
|
|
71
|
+
new MatchingParameterManager(newParameters);
|
|
72
|
+
this[parametersPathSymbol] = ['parameters'];
|
|
73
|
+
|
|
74
|
+
const newOpenApi = {
|
|
75
|
+
...openApi,
|
|
76
|
+
parameters: newParameters,
|
|
77
|
+
paths: this.transformPaths(openApi.paths),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const xMsParameterizedHost = openApi['x-ms-parameterized-host'];
|
|
81
|
+
if (xMsParameterizedHost
|
|
82
|
+
&& Array.isArray(xMsParameterizedHost.parameters)) {
|
|
83
|
+
newOpenApi['x-ms-parameterized-host'] = {
|
|
84
|
+
...xMsParameterizedHost,
|
|
85
|
+
parameters: this.transformArray(
|
|
86
|
+
xMsParameterizedHost.parameters,
|
|
87
|
+
this.transformParameter,
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return newOpenApi;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return openApi;
|
|
96
|
+
}
|
|
97
|
+
}
|
package/const-to-enum.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright 2021 Kevin Locke <kevin@kevinlocke.name>
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @module "openapi-transformers/const-to-enum.js"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import OpenApiTransformerBase from 'openapi-transformer-base';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Transformer to convert Schema Objects with `const` (as in OAS 3.1 and JSON
|
|
11
|
+
* Schema) to `enum` for OAS 3.0 and 2.0.
|
|
12
|
+
*/
|
|
13
|
+
export default class ConstToEnumTransformer extends OpenApiTransformerBase {
|
|
14
|
+
transformSchema(schema) {
|
|
15
|
+
const newSchema = super.transformSchema(schema);
|
|
16
|
+
if (newSchema === null
|
|
17
|
+
|| typeof newSchema !== 'object'
|
|
18
|
+
|| Array.isArray(newSchema)) {
|
|
19
|
+
return newSchema;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { const: constValue, ...schemaNoConst } = newSchema;
|
|
23
|
+
if (constValue === undefined) {
|
|
24
|
+
return newSchema;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const enumValues = schemaNoConst.enum;
|
|
28
|
+
if (enumValues === undefined) {
|
|
29
|
+
schemaNoConst.enum = [constValue];
|
|
30
|
+
} else if (Array.isArray(enumValues)) {
|
|
31
|
+
if (enumValues.includes(constValue)) {
|
|
32
|
+
// Schema validation would only succeed for const value.
|
|
33
|
+
// Safe to overwrite.
|
|
34
|
+
schemaNoConst.enum = [constValue];
|
|
35
|
+
} else if (enumValues.length > 0) {
|
|
36
|
+
// Schema validation would always fail (because either const or enum
|
|
37
|
+
// constraint would be unsatisfied). Replace with empty enum, which
|
|
38
|
+
// also always fails.
|
|
39
|
+
this.warn(
|
|
40
|
+
'Using empty enum for schema with const not in enum',
|
|
41
|
+
newSchema,
|
|
42
|
+
);
|
|
43
|
+
schemaNoConst.enum = [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return schemaNoConst;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright 2019-2021 Kevin Locke <kevin@kevinlocke.name>
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @module "openapi-transformers/escape-enum-values.js"
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import OpenApiTransformerBase from 'openapi-transformer-base';
|
|
8
|
+
|
|
9
|
+
const escapeStringSymbol = Symbol('escapeString');
|
|
10
|
+
|
|
11
|
+
function makeEscapeString(lang) {
|
|
12
|
+
let charToEscape = {
|
|
13
|
+
'\0': '\\0',
|
|
14
|
+
'\x07': '\\a',
|
|
15
|
+
'\b': '\\b',
|
|
16
|
+
'\t': '\\t',
|
|
17
|
+
'\n': '\\n',
|
|
18
|
+
'\v': '\\v',
|
|
19
|
+
'\f': '\\f',
|
|
20
|
+
'\r': '\\r',
|
|
21
|
+
"'": "\\'",
|
|
22
|
+
'"': '\\"',
|
|
23
|
+
'\\': '\\\\',
|
|
24
|
+
};
|
|
25
|
+
// Escape control characters for all languages for readability
|
|
26
|
+
let charRange = '\0-\x1F\x7F';
|
|
27
|
+
|
|
28
|
+
let toAstralEscape, toCodeEscape;
|
|
29
|
+
switch (lang) {
|
|
30
|
+
case 'csharp':
|
|
31
|
+
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#string-literals
|
|
32
|
+
charRange += '\n\r"\\\\\x85\u2028\u2029';
|
|
33
|
+
// Note: Although \x can be shorter, must be careful to provide 4
|
|
34
|
+
// digits when next character is hex digit. Use \u for consistency.
|
|
35
|
+
toCodeEscape =
|
|
36
|
+
(c) => `\\u${c.codePointAt(0).toString(16).padStart(4, '0')}`;
|
|
37
|
+
toAstralEscape = (code) => `\\U${code.toString(16).padStart(8, '0')}`;
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
case 'go':
|
|
41
|
+
// https://golang.org/ref/spec#String_literals
|
|
42
|
+
charRange += '\n"\\\\';
|
|
43
|
+
toCodeEscape = (c) => {
|
|
44
|
+
const code = c.codePointAt(0);
|
|
45
|
+
return code <= 0xFF ? `\\x${code.toString(16).padStart(2, '0')}`
|
|
46
|
+
: `\\u${code.toString(16).padStart(4, '0')}`;
|
|
47
|
+
};
|
|
48
|
+
toAstralEscape = (code) => `\\U${code.toString(16).padStart(8, '0')}`;
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case 'java':
|
|
52
|
+
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5
|
|
53
|
+
charRange += '\n"\\\\';
|
|
54
|
+
delete charToEscape['\0'];
|
|
55
|
+
delete charToEscape['\x07'];
|
|
56
|
+
delete charToEscape['\v'];
|
|
57
|
+
toCodeEscape =
|
|
58
|
+
(c) => `\\u${c.codePointAt(0).toString(16).padStart(4, '0')}`;
|
|
59
|
+
break;
|
|
60
|
+
|
|
61
|
+
case 'nodejs':
|
|
62
|
+
case 'typescript':
|
|
63
|
+
// https://www.ecma-international.org/ecma-262/6.0/#sec-literals-string-literals
|
|
64
|
+
// Note: AutoRest currently produces single-quoted strings, but the
|
|
65
|
+
// bloat from extra quoting is minor compared to risk.
|
|
66
|
+
charRange += '\n"\'\\\\';
|
|
67
|
+
delete charToEscape['\x07'];
|
|
68
|
+
toCodeEscape = (c) => {
|
|
69
|
+
const code = c.codePointAt(0);
|
|
70
|
+
return code <= 0xFF ? `\\x${code.toString(16).padStart(2, '0')}`
|
|
71
|
+
: `\\u${code.toString(16).padStart(4, '0')}`;
|
|
72
|
+
};
|
|
73
|
+
toAstralEscape = (code) => `\\u{${code.toString(16)}}`;
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
case 'php':
|
|
77
|
+
// https://www.php.net/manual/en/language.types.string.php
|
|
78
|
+
// FIXME: Does AutoRest produce single- or double-quoted strings?
|
|
79
|
+
// Can't test due to https://github.com/Azure/autorest/issues/3372
|
|
80
|
+
charRange += '\n"\\\\';
|
|
81
|
+
delete charToEscape['\x07'];
|
|
82
|
+
delete charToEscape['\b'];
|
|
83
|
+
charToEscape['\x1B'] = '\\e';
|
|
84
|
+
toCodeEscape = (c) => {
|
|
85
|
+
const code = c.codePointAt(0);
|
|
86
|
+
return code <= 0xFF ? `\\x${code.toString(16).padStart(2, '0')}`
|
|
87
|
+
: `\\u{${code.toString(16)}}`;
|
|
88
|
+
};
|
|
89
|
+
toAstralEscape = (code) => `\\u{${code.toString(16)}}`;
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
case 'python':
|
|
93
|
+
// https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
|
|
94
|
+
// Note: AutoRest currently produces single-quoted strings, but the
|
|
95
|
+
// bloat from extra quoting is minor compared to risk.
|
|
96
|
+
charRange += '\n"\'\\\\';
|
|
97
|
+
toCodeEscape = (c) => {
|
|
98
|
+
const code = c.codePointAt(0);
|
|
99
|
+
return code <= 0xFF ? `\\x${code.toString(16).padStart(2, '0')}`
|
|
100
|
+
: `\\u${code.toString(16).padStart(4, '0')}`;
|
|
101
|
+
};
|
|
102
|
+
toAstralEscape = (code) => `\\U${code.toString(16).padStart(8, '0')}`;
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case 'ruby':
|
|
106
|
+
// Note: AutoRest currently produces single-quoted strings.
|
|
107
|
+
// Single-quoted strings only accept \' and \\ escapes.
|
|
108
|
+
// https://docs.ruby-lang.org/en/2.4.0/syntax/literals_rdoc.html#label-Strings
|
|
109
|
+
charRange = "'\\\\";
|
|
110
|
+
charToEscape = {
|
|
111
|
+
"'": "\\'",
|
|
112
|
+
'\\': '\\\\',
|
|
113
|
+
};
|
|
114
|
+
toCodeEscape = (c) => c;
|
|
115
|
+
break;
|
|
116
|
+
|
|
117
|
+
default:
|
|
118
|
+
throw new RangeError(`Unrecognized language '${lang}'`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function replaceAstral(pair) {
|
|
122
|
+
return toAstralEscape(pair.codePointAt(0));
|
|
123
|
+
}
|
|
124
|
+
function replaceChar(c) {
|
|
125
|
+
return charToEscape[c] || toCodeEscape(c);
|
|
126
|
+
}
|
|
127
|
+
const charPattern = new RegExp(`[${charRange}]`, 'g');
|
|
128
|
+
return function escapeString(str) {
|
|
129
|
+
if (!str || typeof str !== 'string') {
|
|
130
|
+
return str;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const charEscaped = str.replace(charPattern, replaceChar);
|
|
134
|
+
const astralEscaped =
|
|
135
|
+
!toAstralEscape ? charEscaped : charEscaped.replaceAll(
|
|
136
|
+
/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
|
137
|
+
replaceAstral,
|
|
138
|
+
);
|
|
139
|
+
return astralEscaped;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function escapeInSchema(schema, escapeString) {
|
|
144
|
+
const xMsEnum = schema['x-ms-enum'];
|
|
145
|
+
if (!xMsEnum || schema.type !== 'string') {
|
|
146
|
+
// Schema won't generate a class/enum with string values
|
|
147
|
+
return schema;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const xMsEnumValues = xMsEnum.values;
|
|
151
|
+
if (xMsEnumValues) {
|
|
152
|
+
// generator uses .x-ms-enum.values instead of .enum
|
|
153
|
+
return {
|
|
154
|
+
...schema,
|
|
155
|
+
'x-ms-enum': {
|
|
156
|
+
...xMsEnum,
|
|
157
|
+
values: xMsEnumValues.map((xMsEnumValue) => ({
|
|
158
|
+
...xMsEnumValue,
|
|
159
|
+
value: escapeString(xMsEnumValue.value),
|
|
160
|
+
})),
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
...schema,
|
|
167
|
+
enum: schema.enum.map(escapeString),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Transformer to escape string enum values appropriately for C# string literals
|
|
173
|
+
* when code will be generated by Autorest, since it currently does not.
|
|
174
|
+
*
|
|
175
|
+
* https://github.com/Azure/autorest/issues/3371
|
|
176
|
+
*/
|
|
177
|
+
export default class EscapeEnumValuesTransformer
|
|
178
|
+
extends OpenApiTransformerBase {
|
|
179
|
+
constructor({ language }) {
|
|
180
|
+
super();
|
|
181
|
+
|
|
182
|
+
const typeofLanguage = typeof language;
|
|
183
|
+
switch (typeofLanguage) {
|
|
184
|
+
case 'string':
|
|
185
|
+
this[escapeStringSymbol] = makeEscapeString(language);
|
|
186
|
+
break;
|
|
187
|
+
case 'function':
|
|
188
|
+
this[escapeStringSymbol] = language;
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
throw new TypeError(
|
|
192
|
+
`options.language must be a string or function, got ${
|
|
193
|
+
typeofLanguage}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
transformSchema(schema) {
|
|
199
|
+
return escapeInSchema(
|
|
200
|
+
super.transformSchema(schema),
|
|
201
|
+
this[escapeStringSymbol],
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
transformParameter(parameter) {
|
|
206
|
+
return escapeInSchema(
|
|
207
|
+
super.transformParameter(parameter),
|
|
208
|
+
this[escapeStringSymbol],
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|