mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Upgraded dependencies
- Removed depricated enum model generation - Fixed issue with "null" value inside comments for OpenAPI v2 enums - Fixed issue with compatibility for latest version of Axios (0.27.x)
This commit is contained in:
parent
33c1ff5e26
commit
59254d94a9
@ -1,6 +1,14 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.22.0] - 2022-04-26
|
||||
### Fixed
|
||||
- Upgraded dependencies
|
||||
- Fixed issue with `null` value inside comments for OpenAPI v2 enums
|
||||
- Fixed issue with compatibility for latest version of Axios (0.27.x)
|
||||
### Removed
|
||||
- Removed deprecated enum model generation
|
||||
|
||||
## [0.21.0] - 2022-04-06
|
||||
### Fixed
|
||||
- Return `undefined` to match `noImplicitReturns` rule
|
||||
|
||||
5997
package-lock.json
generated
5997
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
66
package.json
66
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openapi-typescript-codegen",
|
||||
"version": "0.21.0",
|
||||
"version": "0.22.0",
|
||||
"description": "Library that generates Typescript clients based on the OpenAPI specification.",
|
||||
"author": "Ferdi Koomen",
|
||||
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",
|
||||
@ -60,63 +60,63 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": "^6.3.0",
|
||||
"commander": "^9.0.0",
|
||||
"commander": "^9.2.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"json-schema-ref-parser": "^9.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "13.2.5",
|
||||
"@angular/animations": "13.2.4",
|
||||
"@angular/cli": "13.2.5",
|
||||
"@angular/common": "13.2.4",
|
||||
"@angular/compiler": "13.2.4",
|
||||
"@angular/compiler-cli": "13.2.4",
|
||||
"@angular/core": "13.2.4",
|
||||
"@angular/forms": "13.2.4",
|
||||
"@angular/platform-browser": "13.2.4",
|
||||
"@angular/platform-browser-dynamic": "13.2.4",
|
||||
"@angular/router": "13.2.4",
|
||||
"@angular-devkit/build-angular": "13.3.3",
|
||||
"@angular/animations": "13.3.4",
|
||||
"@angular/cli": "13.3.3",
|
||||
"@angular/common": "13.3.4",
|
||||
"@angular/compiler": "13.3.4",
|
||||
"@angular/compiler-cli": "13.3.4",
|
||||
"@angular/core": "13.3.4",
|
||||
"@angular/forms": "13.3.4",
|
||||
"@angular/platform-browser": "13.3.4",
|
||||
"@angular/platform-browser-dynamic": "13.3.4",
|
||||
"@angular/router": "13.3.4",
|
||||
"@babel/cli": "7.17.6",
|
||||
"@babel/core": "7.17.8",
|
||||
"@babel/core": "7.17.9",
|
||||
"@babel/preset-env": "7.16.11",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"@rollup/plugin-commonjs": "21.0.3",
|
||||
"@rollup/plugin-node-resolve": "13.1.3",
|
||||
"@rollup/plugin-typescript": "8.3.1",
|
||||
"@rollup/plugin-commonjs": "22.0.0",
|
||||
"@rollup/plugin-node-resolve": "13.2.1",
|
||||
"@rollup/plugin-typescript": "8.3.2",
|
||||
"@types/cross-spawn": "6.0.2",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/glob": "7.2.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/node": "17.0.21",
|
||||
"@types/node": "17.0.27",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/qs": "6.9.7",
|
||||
"@typescript-eslint/eslint-plugin": "5.18.0",
|
||||
"@typescript-eslint/parser": "5.17.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.21.0",
|
||||
"@typescript-eslint/parser": "5.21.0",
|
||||
"abort-controller": "3.0.0",
|
||||
"axios": "0.26.1",
|
||||
"axios": "0.27.1",
|
||||
"codecov": "3.8.3",
|
||||
"cross-spawn": "7.0.3",
|
||||
"eslint": "8.10.0",
|
||||
"eslint": "8.14.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-prettier": "4.0.0",
|
||||
"eslint-plugin-simple-import-sort": "7.0.0",
|
||||
"express": "4.17.3",
|
||||
"express": "4.18.0",
|
||||
"form-data": "4.0.0",
|
||||
"glob": "7.2.0",
|
||||
"jest": "27.5.1",
|
||||
"jest-cli": "27.5.1",
|
||||
"glob": "8.0.1",
|
||||
"jest": "28.0.1",
|
||||
"jest-cli": "28.0.1",
|
||||
"node-fetch": "2.6.7",
|
||||
"prettier": "2.5.1",
|
||||
"puppeteer": "13.5.2",
|
||||
"prettier": "2.6.2",
|
||||
"puppeteer": "13.6.0",
|
||||
"qs": "6.10.3",
|
||||
"rimraf": "3.0.2",
|
||||
"rollup": "2.68.0",
|
||||
"rollup": "2.70.2",
|
||||
"rollup-plugin-node-externals": "4.0.0",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rxjs": "7.5.5",
|
||||
"ts-node": "10.5.0",
|
||||
"tslib": "2.3.1",
|
||||
"typescript": "4.5.5",
|
||||
"zone.js": "0.11.4"
|
||||
"ts-node": "10.7.0",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.6.3",
|
||||
"zone.js": "0.11.5"
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ export const extendEnum = (enumerators: Enum[], definition: WithEnumExtension):
|
||||
|
||||
return enumerators.map((enumerator, index) => ({
|
||||
name: names?.[index] || enumerator.name,
|
||||
description: JSON.stringify(descriptions?.[index] || enumerator.description),
|
||||
description: descriptions?.[index] || enumerator.description,
|
||||
value: enumerator.value,
|
||||
type: enumerator.type,
|
||||
}));
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const getEnumFromDescription = (description: string): Enum[] => {
|
||||
// Check if we can find this special format string:
|
||||
// None=0,Something=1,AnotherThing=2
|
||||
if (/^(\w+=[0-9]+)/g.test(description)) {
|
||||
const matches = description.match(/(\w+=[0-9]+,?)/g);
|
||||
if (matches) {
|
||||
// Grab the values from the description
|
||||
const symbols: Enum[] = [];
|
||||
matches.forEach(match => {
|
||||
const name = match.split('=')[0];
|
||||
const value = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
|
||||
if (name && Number.isInteger(value)) {
|
||||
symbols.push({
|
||||
name: name
|
||||
.replace(/\W+/g, '_')
|
||||
.replace(/^(\d+)/g, '_$1')
|
||||
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
|
||||
.toUpperCase(),
|
||||
value: String(value),
|
||||
type: 'number',
|
||||
description: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Filter out any duplicate names
|
||||
return symbols.filter((symbol, index, arr) => {
|
||||
return arr.map(item => item.name).indexOf(symbol.name) === index;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
@ -4,7 +4,6 @@ import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { extendEnum } from './extendEnum';
|
||||
import { getEnum } from './getEnum';
|
||||
import { getEnumFromDescription } from './getEnumFromDescription';
|
||||
import { getModelComposition } from './getModelComposition';
|
||||
import { getModelProperties } from './getModelProperties';
|
||||
import { getType } from './getType';
|
||||
@ -69,17 +68,6 @@ export const getModel = (
|
||||
}
|
||||
}
|
||||
|
||||
if ((definition.type === 'int' || definition.type === 'integer') && definition.description) {
|
||||
const enumerators = getEnumFromDescription(definition.description);
|
||||
if (enumerators.length) {
|
||||
model.export = 'enum';
|
||||
model.type = 'number';
|
||||
model.base = 'number';
|
||||
model.enum.push(...enumerators);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.type === 'array' && definition.items) {
|
||||
if (definition.items.$ref) {
|
||||
const arrayItems = getType(definition.items.$ref);
|
||||
|
||||
@ -5,7 +5,6 @@ import type { OpenApiParameter } from '../interfaces/OpenApiParameter';
|
||||
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { extendEnum } from './extendEnum';
|
||||
import { getEnum } from './getEnum';
|
||||
import { getEnumFromDescription } from './getEnumFromDescription';
|
||||
import { getModel } from './getModel';
|
||||
import { getOperationParameterDefault } from './getOperationParameterDefault';
|
||||
import { getOperationParameterName } from './getOperationParameterName';
|
||||
@ -70,18 +69,6 @@ export const getOperationParameter = (openApi: OpenApi, parameter: OpenApiParame
|
||||
}
|
||||
}
|
||||
|
||||
if ((parameter.type === 'int' || parameter.type === 'integer') && parameter.description) {
|
||||
const enumerators = getEnumFromDescription(parameter.description);
|
||||
if (enumerators.length) {
|
||||
operationParameter.export = 'enum';
|
||||
operationParameter.type = 'number';
|
||||
operationParameter.base = 'number';
|
||||
operationParameter.enum.push(...enumerators);
|
||||
operationParameter.default = getOperationParameterDefault(parameter, operationParameter);
|
||||
return operationParameter;
|
||||
}
|
||||
}
|
||||
|
||||
if (parameter.type === 'array' && parameter.items) {
|
||||
const items = getType(parameter.items.type, parameter.items.format);
|
||||
operationParameter.export = 'array';
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const getEnumFromDescription = (description: string): Enum[] => {
|
||||
// Check if we can find this special format string:
|
||||
// None=0,Something=1,AnotherThing=2
|
||||
if (/^(\w+=[0-9]+)/g.test(description)) {
|
||||
const matches = description.match(/(\w+=[0-9]+,?)/g);
|
||||
if (matches) {
|
||||
// Grab the values from the description
|
||||
const symbols: Enum[] = [];
|
||||
matches.forEach(match => {
|
||||
const name = match.split('=')[0];
|
||||
const value = parseInt(match.split('=')[1].replace(/[^0-9]/g, ''));
|
||||
if (name && Number.isInteger(value)) {
|
||||
symbols.push({
|
||||
name: name
|
||||
.replace(/\W+/g, '_')
|
||||
.replace(/^(\d+)/g, '_$1')
|
||||
.replace(/([a-z])([A-Z]+)/g, '$1_$2')
|
||||
.toUpperCase(),
|
||||
value: String(value),
|
||||
type: 'number',
|
||||
description: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Filter out any duplicate names
|
||||
return symbols.filter((symbol, index, arr) => {
|
||||
return arr.map(item => item.name).indexOf(symbol.name) === index;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
@ -4,7 +4,6 @@ import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
import { extendEnum } from './extendEnum';
|
||||
import { getEnum } from './getEnum';
|
||||
import { getEnumFromDescription } from './getEnumFromDescription';
|
||||
import { getModelComposition } from './getModelComposition';
|
||||
import { getModelDefault } from './getModelDefault';
|
||||
import { getModelProperties } from './getModelProperties';
|
||||
@ -72,18 +71,6 @@ export const getModel = (
|
||||
}
|
||||
}
|
||||
|
||||
if ((definition.type === 'int' || definition.type === 'integer') && definition.description) {
|
||||
const enumerators = getEnumFromDescription(definition.description);
|
||||
if (enumerators.length) {
|
||||
model.export = 'enum';
|
||||
model.type = 'number';
|
||||
model.base = 'number';
|
||||
model.enum.push(...enumerators);
|
||||
model.default = getModelDefault(definition, model);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.type === 'array' && definition.items) {
|
||||
if (definition.items.$ref) {
|
||||
const arrayItems = getType(definition.items.$ref);
|
||||
|
||||
@ -23,7 +23,7 @@ const sendRequest = async <T>(
|
||||
try {
|
||||
return await axios.request(requestConfig);
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError;
|
||||
const axiosError = error as AxiosError<T>;
|
||||
if (axiosError.response) {
|
||||
return axiosError.response;
|
||||
}
|
||||
|
||||
@ -570,7 +570,7 @@ export type { ModelWithDictionary } from './models/ModelWithDictionary';
|
||||
export type { ModelWithDuplicateImports } from './models/ModelWithDuplicateImports';
|
||||
export type { ModelWithDuplicateProperties } from './models/ModelWithDuplicateProperties';
|
||||
export { ModelWithEnum } from './models/ModelWithEnum';
|
||||
export { ModelWithEnumFromDescription } from './models/ModelWithEnumFromDescription';
|
||||
export type { ModelWithEnumFromDescription } from './models/ModelWithEnumFromDescription';
|
||||
export type { ModelWithInteger } from './models/ModelWithInteger';
|
||||
export type { ModelWithNestedEnums } from './models/ModelWithNestedEnums';
|
||||
export type { ModelWithNestedProperties } from './models/ModelWithNestedProperties';
|
||||
@ -890,15 +890,15 @@ exports[`v2 should generate: ./test/generated/v2/models/EnumWithExtensions.ts 1`
|
||||
*/
|
||||
export enum EnumWithExtensions {
|
||||
/**
|
||||
* \\"Used when the status of something is successful\\"
|
||||
* Used when the status of something is successful
|
||||
*/
|
||||
CUSTOM_SUCCESS = 200,
|
||||
/**
|
||||
* \\"Used when the status of something has a warning\\"
|
||||
* Used when the status of something has a warning
|
||||
*/
|
||||
CUSTOM_WARNING = 400,
|
||||
/**
|
||||
* \\"Used when the status of something has an error\\"
|
||||
* Used when the status of something has an error
|
||||
*/
|
||||
CUSTOM_ERROR = 500,
|
||||
}"
|
||||
@ -913,65 +913,20 @@ exports[`v2 should generate: ./test/generated/v2/models/EnumWithNumbers.ts 1`] =
|
||||
* This is a simple enum with numbers
|
||||
*/
|
||||
export enum EnumWithNumbers {
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_1' = 1,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_2' = 2,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_3' = 3,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_1.1' = 1.1,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_1.2' = 1.2,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_1.3' = 1.3,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_100' = 100,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_200' = 200,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_300' = 300,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_-100' = -100,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_-200' = -200,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_-300' = -300,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_-1.1' = -1.1,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_-1.2' = -1.2,
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
'_-1.3' = -1.3,
|
||||
}"
|
||||
`;
|
||||
@ -985,25 +940,10 @@ exports[`v2 should generate: ./test/generated/v2/models/EnumWithStrings.ts 1`] =
|
||||
* This is a simple enum with strings
|
||||
*/
|
||||
export enum EnumWithStrings {
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
SUCCESS = 'Success',
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
WARNING = 'Warning',
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
ERROR = 'Error',
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
_SINGLE_QUOTE_ = '\\\\'Single Quote\\\\'',
|
||||
/**
|
||||
* null
|
||||
*/
|
||||
_DOUBLE_QUOTES_ = '\\"Double Quotes\\"',
|
||||
}"
|
||||
`;
|
||||
@ -1203,22 +1143,8 @@ export type ModelWithEnumFromDescription = {
|
||||
/**
|
||||
* Success=1,Warning=2,Error=3
|
||||
*/
|
||||
test?: ModelWithEnumFromDescription.test;
|
||||
test?: number;
|
||||
};
|
||||
|
||||
export namespace ModelWithEnumFromDescription {
|
||||
|
||||
/**
|
||||
* Success=1,Warning=2,Error=3
|
||||
*/
|
||||
export enum test {
|
||||
SUCCESS = 1,
|
||||
WARNING = 2,
|
||||
ERROR = 3,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
@ -1249,9 +1175,9 @@ exports[`v2 should generate: ./test/generated/v2/models/ModelWithNestedEnums.ts
|
||||
*/
|
||||
export type ModelWithNestedEnums = {
|
||||
dictionaryWithEnum?: Record<string, 'Success' | 'Warning' | 'Error'>;
|
||||
dictionaryWithEnumFromDescription?: Record<string, 1 | 2 | 3>;
|
||||
dictionaryWithEnumFromDescription?: Record<string, number>;
|
||||
arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>;
|
||||
arrayWithDescription?: Array<1 | 2 | 3>;
|
||||
arrayWithDescription?: Array<number>;
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -1906,7 +1832,8 @@ export const $ModelWithEnumFromDescription = {
|
||||
description: \`This is a model with one enum\`,
|
||||
properties: {
|
||||
test: {
|
||||
type: 'Enum',
|
||||
type: 'number',
|
||||
description: \`Success=1,Warning=2,Error=3\`,
|
||||
},
|
||||
},
|
||||
} as const;"
|
||||
@ -1943,7 +1870,8 @@ export const $ModelWithNestedEnums = {
|
||||
dictionaryWithEnumFromDescription: {
|
||||
type: 'dictionary',
|
||||
contains: {
|
||||
type: 'Enum',
|
||||
type: 'number',
|
||||
description: \`Success=1,Warning=2,Error=3\`,
|
||||
},
|
||||
},
|
||||
arrayWithEnum: {
|
||||
@ -1955,7 +1883,8 @@ export const $ModelWithNestedEnums = {
|
||||
arrayWithDescription: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
type: 'Enum',
|
||||
type: 'number',
|
||||
description: \`Success=1,Warning=2,Error=3\`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -3596,7 +3525,7 @@ export type { ModelWithDictionary } from './models/ModelWithDictionary';
|
||||
export type { ModelWithDuplicateImports } from './models/ModelWithDuplicateImports';
|
||||
export type { ModelWithDuplicateProperties } from './models/ModelWithDuplicateProperties';
|
||||
export { ModelWithEnum } from './models/ModelWithEnum';
|
||||
export { ModelWithEnumFromDescription } from './models/ModelWithEnumFromDescription';
|
||||
export type { ModelWithEnumFromDescription } from './models/ModelWithEnumFromDescription';
|
||||
export type { ModelWithInteger } from './models/ModelWithInteger';
|
||||
export type { ModelWithNestedEnums } from './models/ModelWithNestedEnums';
|
||||
export type { ModelWithNestedProperties } from './models/ModelWithNestedProperties';
|
||||
@ -4444,22 +4373,8 @@ export type ModelWithEnumFromDescription = {
|
||||
/**
|
||||
* Success=1,Warning=2,Error=3
|
||||
*/
|
||||
test?: ModelWithEnumFromDescription.test;
|
||||
test?: number;
|
||||
};
|
||||
|
||||
export namespace ModelWithEnumFromDescription {
|
||||
|
||||
/**
|
||||
* Success=1,Warning=2,Error=3
|
||||
*/
|
||||
export enum test {
|
||||
SUCCESS = 1,
|
||||
WARNING = 2,
|
||||
ERROR = 3,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
@ -4490,9 +4405,9 @@ exports[`v3 should generate: ./test/generated/v3/models/ModelWithNestedEnums.ts
|
||||
*/
|
||||
export type ModelWithNestedEnums = {
|
||||
dictionaryWithEnum?: Record<string, 'Success' | 'Warning' | 'Error'>;
|
||||
dictionaryWithEnumFromDescription?: Record<string, 1 | 2 | 3>;
|
||||
dictionaryWithEnumFromDescription?: Record<string, number>;
|
||||
arrayWithEnum?: Array<'Success' | 'Warning' | 'Error'>;
|
||||
arrayWithDescription?: Array<1 | 2 | 3>;
|
||||
arrayWithDescription?: Array<number>;
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -5554,7 +5469,8 @@ export const $ModelWithEnumFromDescription = {
|
||||
description: \`This is a model with one enum\`,
|
||||
properties: {
|
||||
test: {
|
||||
type: 'Enum',
|
||||
type: 'number',
|
||||
description: \`Success=1,Warning=2,Error=3\`,
|
||||
},
|
||||
},
|
||||
} as const;"
|
||||
@ -5591,7 +5507,8 @@ export const $ModelWithNestedEnums = {
|
||||
dictionaryWithEnumFromDescription: {
|
||||
type: 'dictionary',
|
||||
contains: {
|
||||
type: 'Enum',
|
||||
type: 'number',
|
||||
description: \`Success=1,Warning=2,Error=3\`,
|
||||
},
|
||||
},
|
||||
arrayWithEnum: {
|
||||
@ -5603,7 +5520,8 @@ export const $ModelWithNestedEnums = {
|
||||
arrayWithDescription: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
type: 'Enum',
|
||||
type: 'number',
|
||||
description: \`Success=1,Warning=2,Error=3\`,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user