chore: Remove generated client types folder (#7633)
* chore: Remove generated client types folder * chore: Remove typescript-codegen package and client commands for oas
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
"medusa-dev-cli": patch
|
||||
"@medusajs/medusa-oas-cli": patch
|
||||
"@medusajs/oas-github-ci": patch
|
||||
"@medusajs/openapi-typescript-codegen": patch
|
||||
"@medusajs/core-flows": patch
|
||||
"medusa-test-utils": patch
|
||||
"@medusajs/modules-sdk": patch
|
||||
|
||||
@@ -95,7 +95,6 @@ module.exports = {
|
||||
"./packages/cli/medusa-cli/tsconfig.spec/json",
|
||||
"./packages/cli/medusa-dev-cli/tsconfig.spec.json",
|
||||
"./packages/cli/oas/medusa-oas-cli/tsconfig.spec.json",
|
||||
"./packages/cli/oas/openapi-typescript-codegen/tsconfig.spec.json",
|
||||
|
||||
"./packages/core/orchestration/tsconfig.json",
|
||||
"./packages/core/workflows-sdk/tsconfig.spec.json",
|
||||
|
||||
@@ -85,70 +85,6 @@ yarn medusa-oas oas --force
|
||||
|
||||
---
|
||||
|
||||
### Command - `client`
|
||||
|
||||
Will generate API client files from a given OAS file.
|
||||
|
||||
#### `--src-file <path>`
|
||||
|
||||
Specify the path to the OAS JSON file.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas client --src-file ./store.oas.json`
|
||||
```
|
||||
|
||||
#### `--name <name>`
|
||||
|
||||
Namespace for the generated client. Usually `admin` or `store`.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas client --name admin`
|
||||
```
|
||||
|
||||
#### `--out-dir <path>`
|
||||
|
||||
Specify in which directory should the files be outputted. Accepts relative and absolute path.
|
||||
If the directory doesn't exist, it will be created. Defaults to `./`.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas client --out-dir ./client`
|
||||
```
|
||||
|
||||
#### `--type <type>`
|
||||
|
||||
Client component types to generate. Accepts `all`, `types`, `client`, `hooks`.
|
||||
Defaults to `all`.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas client --type types`
|
||||
```
|
||||
|
||||
#### `--types-packages <name>`
|
||||
|
||||
Replace relative import statements by types package name. Mandatory when using `--type client` or `--type hooks`.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas client --types-packages @medusajs/client-types`
|
||||
```
|
||||
|
||||
#### `--client-packages <name>`
|
||||
|
||||
Replace relative import statements by client package name. Mandatory when using `--type hooks`.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas client --client-packages @medusajs/medusa-js`
|
||||
```
|
||||
|
||||
#### `--clean`
|
||||
|
||||
Delete destination directory content before generating client.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas client --clean
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Command - `docs`
|
||||
|
||||
Will sanitize OAS for use with Redocly's API documentation viewer.
|
||||
@@ -188,7 +124,7 @@ yarn medusa-oas docs --src-file ./store.oas.json --dry-run
|
||||
|
||||
#### `--clean`
|
||||
|
||||
Delete destination directory content before generating client.
|
||||
Delete destination directory content before generating the docs.
|
||||
|
||||
```bash
|
||||
yarn medusa-oas docs --src-file ./store.oas.json --clean
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/medusa": "^1.20.4",
|
||||
"@medusajs/openapi-typescript-codegen": "^0.2.1",
|
||||
"@medusajs/utils": "^1.11.8",
|
||||
"@readme/json-schema-ref-parser": "^1.2.0",
|
||||
"@readme/openapi-parser": "^2.4.0",
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
import path from "path"
|
||||
import {
|
||||
generate,
|
||||
HttpClient,
|
||||
Indent,
|
||||
PackageNames,
|
||||
} from "@medusajs/openapi-typescript-codegen"
|
||||
import { upperFirst } from "lodash"
|
||||
import fs, { mkdir, readFile } from "fs/promises"
|
||||
import { OpenAPIObject } from "openapi3-ts"
|
||||
import { Command, Option, OptionValues } from "commander"
|
||||
|
||||
/**
|
||||
* CLI Command declaration
|
||||
*/
|
||||
export const commandName = "client"
|
||||
export const commandDescription = "Generate API clients from OAS."
|
||||
export const commandOptions: Option[] = [
|
||||
new Option(
|
||||
"-t, --type <type>",
|
||||
"Namespace for the generated client. Usually `admin` or `store`."
|
||||
).makeOptionMandatory(),
|
||||
|
||||
new Option(
|
||||
"-s, --src-file <srcFile>",
|
||||
"Path to source OAS JSON file."
|
||||
).makeOptionMandatory(),
|
||||
|
||||
new Option(
|
||||
"-o, --out-dir <outDir>",
|
||||
"Output directory for generated client files."
|
||||
).default(path.resolve(process.cwd(), "client")),
|
||||
|
||||
new Option(
|
||||
"-c, --component <component>",
|
||||
"Client component types to generate."
|
||||
)
|
||||
.choices(["all", "types", "client", "hooks"])
|
||||
.default("all"),
|
||||
|
||||
new Option(
|
||||
"--types-package <name>",
|
||||
"Replace relative import statements by types package name."
|
||||
),
|
||||
|
||||
new Option(
|
||||
"--client-package <name>",
|
||||
"Replace relative import statements by client package name."
|
||||
),
|
||||
|
||||
new Option(
|
||||
"--clean",
|
||||
"Delete destination directory content before generating client."
|
||||
),
|
||||
]
|
||||
|
||||
export function getCommand() {
|
||||
const command = new Command(commandName)
|
||||
command.description(commandDescription)
|
||||
for (const opt of commandOptions) {
|
||||
command.addOption(opt)
|
||||
}
|
||||
command.action(async (options) => await execute(options))
|
||||
command.showHelpAfterError(true)
|
||||
return command
|
||||
}
|
||||
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
export async function execute(cliParams: OptionValues) {
|
||||
/**
|
||||
* Process CLI options
|
||||
*/
|
||||
if (
|
||||
["client", "hooks"].includes(cliParams.component) &&
|
||||
!cliParams.typesPackage
|
||||
) {
|
||||
throw new Error(
|
||||
`--types-package must be declared when using --component=${cliParams.component}`
|
||||
)
|
||||
}
|
||||
if (cliParams.component === "hooks" && !cliParams.clientPackage) {
|
||||
throw new Error(
|
||||
`--client-package must be declared when using --component=${cliParams.component}`
|
||||
)
|
||||
}
|
||||
|
||||
const shouldClean = !!cliParams.clean
|
||||
const srcFile = path.resolve(cliParams.srcFile)
|
||||
const outDir = path.resolve(cliParams.outDir)
|
||||
const apiName = cliParams.type
|
||||
const packageNames: PackageNames = {
|
||||
models: cliParams.typesPackage,
|
||||
client: cliParams.clientPackage,
|
||||
}
|
||||
const exportComponent = cliParams.component
|
||||
|
||||
/**
|
||||
* Command execution
|
||||
*/
|
||||
console.log(`🟣 Generating client - ${apiName} - ${exportComponent}`)
|
||||
|
||||
if (shouldClean) {
|
||||
console.log(`🟠 Cleaning output directory`)
|
||||
await fs.rm(outDir, { recursive: true, force: true })
|
||||
}
|
||||
await mkdir(outDir, { recursive: true })
|
||||
|
||||
const oas = await getOASFromFile(srcFile)
|
||||
await generateClientSDK(oas, outDir, apiName, exportComponent, packageNames)
|
||||
|
||||
console.log(
|
||||
`⚫️ Client generated - ${apiName} - ${exportComponent} - ${outDir}`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
const getOASFromFile = async (jsonFile: string): Promise<OpenAPIObject> => {
|
||||
const jsonString = await readFile(jsonFile, "utf8")
|
||||
return JSON.parse(jsonString)
|
||||
}
|
||||
|
||||
const generateClientSDK = async (
|
||||
oas: OpenAPIObject,
|
||||
targetDir: string,
|
||||
apiName: string,
|
||||
exportComponent: "all" | "types" | "client" | "hooks",
|
||||
packageNames: PackageNames = {}
|
||||
) => {
|
||||
const exports = {
|
||||
exportCore: false,
|
||||
exportServices: false,
|
||||
exportModels: false,
|
||||
exportHooks: false,
|
||||
}
|
||||
|
||||
switch (exportComponent) {
|
||||
case "types":
|
||||
exports.exportModels = true
|
||||
break
|
||||
case "client":
|
||||
exports.exportCore = true
|
||||
exports.exportServices = true
|
||||
break
|
||||
case "hooks":
|
||||
exports.exportHooks = true
|
||||
break
|
||||
default:
|
||||
exports.exportCore = true
|
||||
exports.exportServices = true
|
||||
exports.exportModels = true
|
||||
exports.exportHooks = true
|
||||
}
|
||||
|
||||
await generate({
|
||||
input: oas,
|
||||
output: targetDir,
|
||||
httpClient: HttpClient.AXIOS,
|
||||
useOptions: true,
|
||||
useUnionTypes: true,
|
||||
exportCore: exports.exportCore,
|
||||
exportServices: exports.exportServices,
|
||||
exportModels: exports.exportModels,
|
||||
exportHooks: exports.exportHooks,
|
||||
exportSchemas: false,
|
||||
indent: Indent.SPACE_2,
|
||||
postfixServices: "Service",
|
||||
postfixModels: "",
|
||||
clientName: `Medusa${upperFirst(apiName)}`,
|
||||
request: undefined,
|
||||
packageNames,
|
||||
})
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { Command } from "commander"
|
||||
import { getCommand as oasGetCommand } from "./command-oas"
|
||||
import { getCommand as clientGetCommand } from "./command-client"
|
||||
import { getCommand as docsGetCommand } from "./command-docs"
|
||||
|
||||
const run = async () => {
|
||||
@@ -13,11 +12,6 @@ const run = async () => {
|
||||
*/
|
||||
program.addCommand(oasGetCommand())
|
||||
|
||||
/**
|
||||
* Alias to command-client.ts
|
||||
*/
|
||||
program.addCommand(clientGetCommand())
|
||||
|
||||
/**
|
||||
* Alias to command-docs.ts
|
||||
*/
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/dist
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
.env
|
||||
*.sql
|
||||
/bin
|
||||
@@ -1 +0,0 @@
|
||||
*.hbs
|
||||
@@ -1,33 +0,0 @@
|
||||
# @medusajs/openapi-typescript-codegen
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3675](https://github.com/medusajs/medusa/pull/3675) [`0b3c6fde3`](https://github.com/medusajs/medusa/commit/0b3c6fde30c9bac052a8e1aa641ef925b6937c0e) Thanks [@patrick-medusajs](https://github.com/patrick-medusajs)! - feat(codegen:test): coverage x-expanded-relation + x-codegen.queryParams
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#3477](https://github.com/medusajs/medusa/pull/3477) [`826d4bedf`](https://github.com/medusajs/medusa/commit/826d4bedfe1b6459163711d5173eb8eadfdea26e) Thanks [@patrick-medusajs](https://github.com/patrick-medusajs)! - feat(codegen,types): SetRelation on expanded types
|
||||
|
||||
- [#3442](https://github.com/medusajs/medusa/pull/3442) [`7b57695e0`](https://github.com/medusajs/medusa/commit/7b57695e00433e1d54f8cdc912ef7e5f28fc1071) Thanks [@patrick-medusajs](https://github.com/patrick-medusajs)! - feat(codegen): x-expanded-relations
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3272](https://github.com/medusajs/medusa/pull/3272) [`1c40346e9`](https://github.com/medusajs/medusa/commit/1c40346e9e56718de4a6e2d5c2d52abd388343e3) Thanks [@patrick-medusajs](https://github.com/patrick-medusajs)! - feat(codegen): openapi-typescript-codegen fork
|
||||
|
||||
## 0.2.0-rc.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#3477](https://github.com/medusajs/medusa/pull/3477) [`826d4bedf`](https://github.com/medusajs/medusa/commit/826d4bedfe1b6459163711d5173eb8eadfdea26e) Thanks [@patrick-medusajs](https://github.com/patrick-medusajs)! - feat(codegen,types): SetRelation on expanded types
|
||||
|
||||
- [#3442](https://github.com/medusajs/medusa/pull/3442) [`7b57695e0`](https://github.com/medusajs/medusa/commit/7b57695e00433e1d54f8cdc912ef7e5f28fc1071) Thanks [@patrick-medusajs](https://github.com/patrick-medusajs)! - feat(codegen): x-expanded-relations
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#3272](https://github.com/medusajs/medusa/pull/3272) [`1c40346e9`](https://github.com/medusajs/medusa/commit/1c40346e9e56718de4a6e2d5c2d52abd388343e3) Thanks [@patrick-medusajs](https://github.com/patrick-medusajs)! - feat(codegen): openapi-typescript-codegen fork
|
||||
|
||||
## 0.1.0
|
||||
@@ -1,39 +0,0 @@
|
||||
# OpenAPI Typescript Codegen - 0.1.0 - experimental
|
||||
|
||||
Node.js library that generates Typescript clients based on the OpenAPI specification.
|
||||
|
||||
## About this fork
|
||||
|
||||
This package is a significantly customized fork of
|
||||
the amazing npm [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) package 💜.
|
||||
|
||||
### Brief reasoning
|
||||
|
||||
We wanted a level of customization that was not achievable through the source package's interface.
|
||||
We started with a conventional fork but the development workflow was hindering our ability to iterate quickly.
|
||||
We decided to fold the package within our monorepo to hopefully accelerate development and innovation.
|
||||
|
||||
### Noteworthy differences
|
||||
|
||||
* Added ability to generate React hooks in the style of `medusa-react`.
|
||||
* Added access to the raw OAS on parsed element from the parser.
|
||||
* Added access `operationId` from path.
|
||||
* Added access `x-codegen` from path.
|
||||
* Added renaming of service method when `x-codegen.method` is declared.
|
||||
* Added bundling of query params into a typed object when `x-codegen.queryParams` is declared.
|
||||
* Added parameterization of import path for the client and models.
|
||||
* Added generated `index.ts` files in directories to simplify imports.
|
||||
* Updated npm dependencies' version to match existing versions found in the monorepo.
|
||||
* Updated coding style to match the convention of the monorepo.
|
||||
* Removed support for Angular client generation.
|
||||
* Removed support for OAS v2 parsing.
|
||||
* Removed documentation.
|
||||
* Removed tests (temporarily).
|
||||
|
||||
## Install
|
||||
|
||||
`yarn add --dev @medusajs/openapi-typescript-codegen`
|
||||
|
||||
## How to use
|
||||
|
||||
See `@medusajs/medusa-oas-cli` for usage.
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "12"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-typescript",
|
||||
{
|
||||
"onlyRemoveTypeImports": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
module.exports = {
|
||||
globals: {
|
||||
"ts-jest": {
|
||||
tsconfig: "tsconfig.json",
|
||||
isolatedModules: false,
|
||||
},
|
||||
},
|
||||
transform: {
|
||||
"^.+\\.[jt]s?$": "ts-jest",
|
||||
},
|
||||
testEnvironment: `node`,
|
||||
moduleFileExtensions: [`js`, `ts`],
|
||||
testTimeout: 30000,
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"name": "@medusajs/openapi-typescript-codegen",
|
||||
"version": "0.2.1",
|
||||
"description": "Library that generates Typescript clients based on the OpenAPI specification.",
|
||||
"main": "dist/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"files": [
|
||||
"dist/index.js",
|
||||
"types/index.d.ts"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
"directory": "packages/oas/openapi-typescript-codegen"
|
||||
},
|
||||
"author": "Medusa",
|
||||
"contributors": [
|
||||
"Ferdi Koomen"
|
||||
],
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepare": "cross-env NODE_ENV=production yarn run release",
|
||||
"build": "rollup --config --environment NODE_ENV:development",
|
||||
"release": "rollup --config --environment NODE_ENV:production",
|
||||
"test": "jest src --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": "^6.3.0",
|
||||
"commander": "^9.4.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"json-schema-ref-parser": "^9.0.9",
|
||||
"pascalcase": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.14.3",
|
||||
"@babel/core": "7.14.3",
|
||||
"@babel/preset-env": "7.11.5",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@rollup/plugin-commonjs": "24.0.0",
|
||||
"@rollup/plugin-node-resolve": "15.0.1",
|
||||
"@rollup/plugin-typescript": "9.0.2",
|
||||
"@types/fs-extra": "^9.0.12",
|
||||
"@types/jest": "27.5.0",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/node-fetch": "2.6.2",
|
||||
"@types/pascalcase": "^1.0.1",
|
||||
"@types/qs": "6.9.7",
|
||||
"abort-controller": "3.0.0",
|
||||
"axios": "1.2.0",
|
||||
"form-data": "4.0.0",
|
||||
"jest": "^25.5.4",
|
||||
"node-fetch": "2.6.7",
|
||||
"qs": "6.10.3",
|
||||
"rollup": "3.9.1",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"ts-jest": "^25.5.1",
|
||||
"tslib": "2.3.1",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import commonjs from "@rollup/plugin-commonjs"
|
||||
import { nodeResolve } from "@rollup/plugin-node-resolve"
|
||||
import typescript from "@rollup/plugin-typescript"
|
||||
import { readFileSync } from "fs"
|
||||
import handlebars from "handlebars"
|
||||
import { dirname, extname, resolve } from "path"
|
||||
import { terser } from "rollup-plugin-terser"
|
||||
|
||||
const { precompile } = handlebars
|
||||
|
||||
/**
|
||||
* Custom plugin to parse handlebar imports and precompile
|
||||
* the template on the fly. This reduces runtime by about
|
||||
* half on large projects.
|
||||
*/
|
||||
const handlebarsPlugin = () => ({
|
||||
resolveId: (file, importer) => {
|
||||
if (extname(file) === ".hbs") {
|
||||
return resolve(dirname(importer), file)
|
||||
}
|
||||
return null
|
||||
},
|
||||
load: (file) => {
|
||||
if (extname(file) === ".hbs") {
|
||||
const template = readFileSync(file, "utf8").toString().trim()
|
||||
const templateSpec = precompile(template, {
|
||||
strict: true,
|
||||
noEscape: true,
|
||||
preventIndent: true,
|
||||
knownHelpersOnly: true,
|
||||
knownHelpers: {
|
||||
ifdef: true,
|
||||
equals: true,
|
||||
notEquals: true,
|
||||
containsSpaces: true,
|
||||
union: true,
|
||||
intersection: true,
|
||||
enumerator: true,
|
||||
escapeComment: true,
|
||||
escapeDescription: true,
|
||||
camelCase: true,
|
||||
pascalCase: true,
|
||||
},
|
||||
})
|
||||
return `export default ${templateSpec};`
|
||||
}
|
||||
return null
|
||||
},
|
||||
})
|
||||
|
||||
const getPlugins = () => {
|
||||
const plugins = [
|
||||
nodeResolve(),
|
||||
commonjs({
|
||||
sourceMap: false,
|
||||
}),
|
||||
handlebarsPlugin(),
|
||||
typescript({
|
||||
module: "esnext",
|
||||
}),
|
||||
]
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return plugins
|
||||
}
|
||||
return [...plugins, terser()]
|
||||
}
|
||||
|
||||
export default {
|
||||
input: "./src/index.ts",
|
||||
output: {
|
||||
exports: "named",
|
||||
file: "./dist/index.js",
|
||||
format: "cjs",
|
||||
},
|
||||
external: [
|
||||
"camelcase",
|
||||
"commander",
|
||||
"fs-extra",
|
||||
"handlebars",
|
||||
"json-schema-ref-parser",
|
||||
],
|
||||
plugins: getPlugins(),
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export enum HttpClient {
|
||||
FETCH = "fetch",
|
||||
XHR = "xhr",
|
||||
NODE = "node",
|
||||
AXIOS = "axios",
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export enum Indent {
|
||||
SPACE_4 = "4",
|
||||
SPACE_2 = "2",
|
||||
TAB = "tab",
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { Model } from "./Model"
|
||||
import type { Service } from "./Service"
|
||||
|
||||
export interface Client {
|
||||
version: string
|
||||
server: string
|
||||
models: Model[]
|
||||
services: Service[]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export interface Enum {
|
||||
name: string
|
||||
value: string
|
||||
type: string
|
||||
description: string | null
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import type { Enum } from "./Enum"
|
||||
import type { Schema } from "./Schema"
|
||||
|
||||
export type NestedRelation = {
|
||||
field: string
|
||||
nestedRelations: NestedRelation[]
|
||||
base?: string
|
||||
isArray?: boolean
|
||||
hasDepth?: boolean
|
||||
}
|
||||
|
||||
export interface Model extends Schema {
|
||||
name: string
|
||||
export:
|
||||
| "reference"
|
||||
| "generic"
|
||||
| "enum"
|
||||
| "array"
|
||||
| "dictionary"
|
||||
| "interface"
|
||||
| "one-of"
|
||||
| "any-of"
|
||||
| "all-of"
|
||||
type: string
|
||||
base: string
|
||||
template: string | null
|
||||
link: Model | null
|
||||
description: string | null
|
||||
deprecated?: boolean
|
||||
default?: string
|
||||
imports: string[]
|
||||
enum: Enum[]
|
||||
enums: Model[]
|
||||
properties: Model[]
|
||||
nestedRelations?: NestedRelation[]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { Model } from "./Model"
|
||||
|
||||
export interface ModelComposition {
|
||||
type: "one-of" | "any-of" | "all-of"
|
||||
imports: string[]
|
||||
enums: Model[]
|
||||
properties: Model[]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import type { OperationError } from "./OperationError"
|
||||
import type { OperationParameters } from "./OperationParameters"
|
||||
import type { OperationResponse } from "./OperationResponse"
|
||||
import type { OperationCodegen } from "./OperationCodegen"
|
||||
|
||||
export interface Operation extends OperationParameters {
|
||||
service: string
|
||||
name: string
|
||||
operationId: string | null
|
||||
summary: string | null
|
||||
description: string | null
|
||||
deprecated: boolean
|
||||
method: string
|
||||
path: string
|
||||
errors: OperationError[]
|
||||
results: OperationResponse[]
|
||||
responseHeader: string | null
|
||||
codegen: OperationCodegen
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface OperationCodegen {
|
||||
method?: string
|
||||
queryParams?: string
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface OperationError {
|
||||
code: number
|
||||
description: string
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { Model } from "./Model"
|
||||
import { OpenApiParameter } from "../../openApi/v3/interfaces/OpenApiParameter"
|
||||
import { OpenApiRequestBody } from "../../openApi/v3/interfaces/OpenApiRequestBody"
|
||||
|
||||
export interface OperationParameter extends Model {
|
||||
spec: OpenApiParameter | OpenApiRequestBody
|
||||
in: "path" | "query" | "header" | "formData" | "body" | "cookie"
|
||||
prop: string
|
||||
mediaType: string | null
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { OperationParameter } from "./OperationParameter"
|
||||
|
||||
export interface OperationParameters {
|
||||
imports: string[]
|
||||
parameters: OperationParameter[]
|
||||
parametersPath: OperationParameter[]
|
||||
parametersQuery: OperationParameter[]
|
||||
parametersForm: OperationParameter[]
|
||||
parametersCookie: OperationParameter[]
|
||||
parametersHeader: OperationParameter[]
|
||||
parametersBody: OperationParameter | null
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { Model } from "./Model"
|
||||
import { OpenApiResponse } from "../../openApi/v3/interfaces/OpenApiResponse"
|
||||
|
||||
export interface OperationResponse extends Model {
|
||||
spec: OpenApiResponse
|
||||
in: "response" | "header"
|
||||
code: number
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { OpenApiSchema } from "../../openApi/v3/interfaces/OpenApiSchema"
|
||||
|
||||
export interface Schema {
|
||||
spec: OpenApiSchema
|
||||
isDefinition: boolean
|
||||
isReadOnly: boolean
|
||||
isRequired: boolean
|
||||
isNullable: boolean
|
||||
format?:
|
||||
| "int32"
|
||||
| "int64"
|
||||
| "float"
|
||||
| "double"
|
||||
| "string"
|
||||
| "boolean"
|
||||
| "byte"
|
||||
| "binary"
|
||||
| "date"
|
||||
| "date-time"
|
||||
| "password"
|
||||
maximum?: number
|
||||
exclusiveMaximum?: boolean
|
||||
minimum?: number
|
||||
exclusiveMinimum?: boolean
|
||||
multipleOf?: number
|
||||
maxLength?: number
|
||||
minLength?: number
|
||||
pattern?: string
|
||||
maxItems?: number
|
||||
minItems?: number
|
||||
uniqueItems?: boolean
|
||||
maxProperties?: number
|
||||
minProperties?: number
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { Operation } from "./Operation"
|
||||
|
||||
export interface Service {
|
||||
name: string
|
||||
operations: Operation[]
|
||||
imports: string[]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export interface Type {
|
||||
type: string
|
||||
base: string
|
||||
template: string | null
|
||||
imports: string[]
|
||||
isNullable: boolean
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
import { HttpClient } from "./HttpClient"
|
||||
import { Indent } from "./Indent"
|
||||
import { parse } from "./openApi/v3"
|
||||
import { getOpenApiSpec } from "./utils/getOpenApiSpec"
|
||||
import { getOpenApiVersion } from "./utils/getOpenApiVersion"
|
||||
import { isString } from "./utils/isString"
|
||||
import { postProcessClient } from "./utils/postProcessClient"
|
||||
import { registerHandlebarTemplates } from "./utils/registerHandlebarTemplates"
|
||||
import { writeClient } from "./utils/writeClient"
|
||||
|
||||
export { HttpClient } from "./HttpClient"
|
||||
export { Indent } from "./Indent"
|
||||
|
||||
export type PackageNames = {
|
||||
models?: string
|
||||
client?: string
|
||||
}
|
||||
|
||||
export type Options = {
|
||||
input: string | Record<string, any>
|
||||
output: string
|
||||
httpClient?: HttpClient
|
||||
clientName?: string
|
||||
useOptions?: boolean
|
||||
useUnionTypes?: boolean
|
||||
exportCore?: boolean
|
||||
exportServices?: boolean
|
||||
exportModels?: boolean
|
||||
exportHooks?: boolean
|
||||
exportSchemas?: boolean
|
||||
indent?: Indent
|
||||
packageNames?: PackageNames
|
||||
postfixServices?: string
|
||||
postfixModels?: string
|
||||
request?: string
|
||||
write?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the OpenAPI client. This method will read the OpenAPI specification and based on the
|
||||
* given language it will generate the client, including the typed models, validation schemas,
|
||||
* service layer, etc.
|
||||
* @param input The relative location of the OpenAPI spec
|
||||
* @param output The relative location of the output directory
|
||||
* @param httpClient The selected httpClient (fetch, xhr, node or axios)
|
||||
* @param clientName Custom client class name
|
||||
* @param useOptions Use options or arguments functions
|
||||
* @param useUnionTypes Use union types instead of enums
|
||||
* @param exportCore Generate core client classes
|
||||
* @param exportServices Generate services
|
||||
* @param exportModels Generate models
|
||||
* @param exportHooks Generate hooks
|
||||
* @param exportSchemas Generate schemas
|
||||
* @param indent Indentation options (4, 2 or tab)
|
||||
* @param packageNames Package name to use in import statements.
|
||||
* @param postfixServices Service name postfix
|
||||
* @param postfixModels Model name postfix
|
||||
* @param request Path to custom request file
|
||||
* @param write Write the files to disk (true or false)
|
||||
*/
|
||||
export const generate = async ({
|
||||
input,
|
||||
output,
|
||||
httpClient = HttpClient.FETCH,
|
||||
clientName,
|
||||
useOptions = false,
|
||||
useUnionTypes = false,
|
||||
exportCore = true,
|
||||
exportServices = true,
|
||||
exportModels = true,
|
||||
exportHooks = true,
|
||||
exportSchemas = false,
|
||||
indent = Indent.SPACE_4,
|
||||
packageNames = {},
|
||||
postfixServices = "Service",
|
||||
postfixModels = "",
|
||||
request,
|
||||
write = true,
|
||||
}: Options): Promise<void> => {
|
||||
const openApi = isString(input) ? await getOpenApiSpec(input) : input
|
||||
const openApiVersion = getOpenApiVersion(openApi)
|
||||
const templates = registerHandlebarTemplates({
|
||||
httpClient,
|
||||
useUnionTypes,
|
||||
useOptions,
|
||||
})
|
||||
|
||||
const client = parse(openApi)
|
||||
const clientFinal = postProcessClient(client)
|
||||
if (write) {
|
||||
await writeClient(
|
||||
clientFinal,
|
||||
templates,
|
||||
output,
|
||||
httpClient,
|
||||
useOptions,
|
||||
useUnionTypes,
|
||||
exportCore,
|
||||
exportServices,
|
||||
exportModels,
|
||||
exportHooks,
|
||||
exportSchemas,
|
||||
indent,
|
||||
packageNames,
|
||||
postfixServices,
|
||||
postfixModels,
|
||||
clientName,
|
||||
request
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
HttpClient,
|
||||
generate,
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { Client } from "../../client/interfaces/Client"
|
||||
import type { OpenApi } from "./interfaces/OpenApi"
|
||||
import { getModels } from "./parser/getModels"
|
||||
import { getServer } from "./parser/getServer"
|
||||
import { getServices } from "./parser/getServices"
|
||||
import { getServiceVersion } from "./parser/getServiceVersion"
|
||||
|
||||
/**
|
||||
* Parse the OpenAPI specification to a Client model that contains
|
||||
* all the models, services and schema's we should output.
|
||||
* @param openApi The OpenAPI spec that we have loaded from disk.
|
||||
*/
|
||||
export const parse = (openApi: OpenApi): Client => {
|
||||
const version = getServiceVersion(openApi.info.version)
|
||||
const server = getServer(openApi)
|
||||
const models = getModels(openApi)
|
||||
const services = getServices(openApi)
|
||||
|
||||
return { version, server, models, services }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export interface WithExtendedRelationsExtension {
|
||||
"x-expanded-relations"?: {
|
||||
field: string
|
||||
relations?: string[]
|
||||
totals?: string[]
|
||||
implicit?: string[]
|
||||
eager?: string[]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface WithEnumExtension {
|
||||
"x-enum-varnames"?: string[]
|
||||
"x-enum-descriptions"?: string[]
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { OpenApiComponents } from "./OpenApiComponents"
|
||||
import type { OpenApiExternalDocs } from "./OpenApiExternalDocs"
|
||||
import type { OpenApiInfo } from "./OpenApiInfo"
|
||||
import type { OpenApiPaths } from "./OpenApiPaths"
|
||||
import type { OpenApiSecurityRequirement } from "./OpenApiSecurityRequirement"
|
||||
import type { OpenApiServer } from "./OpenApiServer"
|
||||
import type { OpenApiTag } from "./OpenApiTag"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md
|
||||
*/
|
||||
export interface OpenApi {
|
||||
openapi: string
|
||||
info: OpenApiInfo
|
||||
servers?: OpenApiServer[]
|
||||
paths: OpenApiPaths
|
||||
components?: OpenApiComponents
|
||||
security?: OpenApiSecurityRequirement[]
|
||||
tags?: OpenApiTag[]
|
||||
externalDocs?: OpenApiExternalDocs
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { OpenApiPath } from "./OpenApiPath"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#callbackObject
|
||||
*/
|
||||
export interface OpenApiCallback extends OpenApiReference {
|
||||
[key: string]: OpenApiPath
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiCallback } from "./OpenApiCallback"
|
||||
import type { OpenApiExample } from "./OpenApiExample"
|
||||
import type { OpenApiHeader } from "./OpenApiHeader"
|
||||
import type { OpenApiLink } from "./OpenApiLink"
|
||||
import type { OpenApiParameter } from "./OpenApiParameter"
|
||||
import type { OpenApiRequestBody } from "./OpenApiRequestBody"
|
||||
import type { OpenApiResponses } from "./OpenApiResponses"
|
||||
import type { OpenApiSchema } from "./OpenApiSchema"
|
||||
import type { OpenApiSecurityScheme } from "./OpenApiSecurityScheme"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#componentsObject
|
||||
*/
|
||||
export interface OpenApiComponents {
|
||||
schemas?: Dictionary<OpenApiSchema>
|
||||
responses?: Dictionary<OpenApiResponses>
|
||||
parameters?: Dictionary<OpenApiParameter>
|
||||
examples?: Dictionary<OpenApiExample>
|
||||
requestBodies?: Dictionary<OpenApiRequestBody>
|
||||
headers?: Dictionary<OpenApiHeader>
|
||||
securitySchemes?: Dictionary<OpenApiSecurityScheme>
|
||||
links?: Dictionary<OpenApiLink>
|
||||
callbacks?: Dictionary<OpenApiCallback>
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#contactObject
|
||||
*/
|
||||
export interface OpenApiContact {
|
||||
name?: string
|
||||
url?: string
|
||||
email?: string
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminatorObject
|
||||
*/
|
||||
export interface OpenApiDiscriminator {
|
||||
propertyName: string
|
||||
mapping?: Dictionary<string>
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiHeader } from "./OpenApiHeader"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#encodingObject
|
||||
*/
|
||||
export interface OpenApiEncoding {
|
||||
contentType?: string
|
||||
headers?: Dictionary<OpenApiHeader>
|
||||
style?: string
|
||||
explode?: boolean
|
||||
allowReserved?: boolean
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#exampleObject
|
||||
*/
|
||||
export interface OpenApiExample extends OpenApiReference {
|
||||
summary?: string
|
||||
description?: string
|
||||
value?: any
|
||||
externalValue?: string
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#externalDocumentationObject
|
||||
*/
|
||||
export interface OpenApiExternalDocs {
|
||||
description?: string
|
||||
url: string
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiExample } from "./OpenApiExample"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
import type { OpenApiSchema } from "./OpenApiSchema"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#headerObject
|
||||
*/
|
||||
export interface OpenApiHeader extends OpenApiReference {
|
||||
description?: string
|
||||
required?: boolean
|
||||
deprecated?: boolean
|
||||
allowEmptyValue?: boolean
|
||||
style?: string
|
||||
explode?: boolean
|
||||
allowReserved?: boolean
|
||||
schema?: OpenApiSchema
|
||||
example?: any
|
||||
examples?: Dictionary<OpenApiExample>
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import type { OpenApiContact } from "./OpenApiContact"
|
||||
import type { OpenApiLicense } from "./OpenApiLicense"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#infoObject
|
||||
*/
|
||||
export interface OpenApiInfo {
|
||||
title: string
|
||||
description?: string
|
||||
termsOfService?: string
|
||||
contact?: OpenApiContact
|
||||
license?: OpenApiLicense
|
||||
version: string
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#licenseObject
|
||||
*/
|
||||
export interface OpenApiLicense {
|
||||
name: string
|
||||
url?: string
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
import type { OpenApiServer } from "./OpenApiServer"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#linkObject
|
||||
*/
|
||||
export interface OpenApiLink extends OpenApiReference {
|
||||
operationRef?: string
|
||||
operationId?: string
|
||||
parameters?: Dictionary<any>
|
||||
requestBody?: any
|
||||
description?: string
|
||||
server?: OpenApiServer
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiEncoding } from "./OpenApiEncoding"
|
||||
import type { OpenApiExample } from "./OpenApiExample"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
import type { OpenApiSchema } from "./OpenApiSchema"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#mediaTypeObject
|
||||
*/
|
||||
export interface OpenApiMediaType extends OpenApiReference {
|
||||
schema?: OpenApiSchema
|
||||
example?: any
|
||||
examples?: Dictionary<OpenApiExample>
|
||||
encoding?: Dictionary<OpenApiEncoding>
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#oauthFlowObject
|
||||
*/
|
||||
export interface OpenApiOAuthFlow {
|
||||
authorizationUrl: string
|
||||
tokenUrl: string
|
||||
refreshUrl?: string
|
||||
scopes: Dictionary<string>
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { OpenApiOAuthFlow } from "./OpenApiOAuthFlow"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#oauthFlowsObject
|
||||
*/
|
||||
export interface OpenApiOAuthFlows {
|
||||
implicit?: OpenApiOAuthFlow
|
||||
password?: OpenApiOAuthFlow
|
||||
clientCredentials?: OpenApiOAuthFlow
|
||||
authorizationCode?: OpenApiOAuthFlow
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiCallback } from "./OpenApiCallback"
|
||||
import type { OpenApiExternalDocs } from "./OpenApiExternalDocs"
|
||||
import type { OpenApiParameter } from "./OpenApiParameter"
|
||||
import type { OpenApiRequestBody } from "./OpenApiRequestBody"
|
||||
import type { OpenApiResponses } from "./OpenApiResponses"
|
||||
import type { OpenApiSecurityRequirement } from "./OpenApiSecurityRequirement"
|
||||
import type { OpenApiServer } from "./OpenApiServer"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
|
||||
*/
|
||||
export interface OpenApiOperation {
|
||||
tags?: string[]
|
||||
summary?: string
|
||||
description?: string
|
||||
externalDocs?: OpenApiExternalDocs
|
||||
operationId?: string
|
||||
parameters?: OpenApiParameter[]
|
||||
requestBody?: OpenApiRequestBody
|
||||
responses: OpenApiResponses
|
||||
callbacks?: Dictionary<OpenApiCallback>
|
||||
deprecated?: boolean
|
||||
security?: OpenApiSecurityRequirement[]
|
||||
servers?: OpenApiServer[]
|
||||
"x-codegen"?: Record<string, unknown>
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiExample } from "./OpenApiExample"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
import type { OpenApiSchema } from "./OpenApiSchema"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject
|
||||
*/
|
||||
export interface OpenApiParameter extends OpenApiReference {
|
||||
name: string
|
||||
in: "path" | "query" | "header" | "formData" | "cookie"
|
||||
description?: string
|
||||
required?: boolean
|
||||
nullable?: boolean
|
||||
deprecated?: boolean
|
||||
allowEmptyValue?: boolean
|
||||
style?: string
|
||||
explode?: boolean
|
||||
allowReserved?: boolean
|
||||
schema?: OpenApiSchema
|
||||
example?: any
|
||||
examples?: Dictionary<OpenApiExample>
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { OpenApiOperation } from "./OpenApiOperation"
|
||||
import type { OpenApiParameter } from "./OpenApiParameter"
|
||||
import type { OpenApiServer } from "./OpenApiServer"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#pathItemObject
|
||||
*/
|
||||
export interface OpenApiPath {
|
||||
summary?: string
|
||||
description?: string
|
||||
get?: OpenApiOperation
|
||||
put?: OpenApiOperation
|
||||
post?: OpenApiOperation
|
||||
delete?: OpenApiOperation
|
||||
options?: OpenApiOperation
|
||||
head?: OpenApiOperation
|
||||
patch?: OpenApiOperation
|
||||
trace?: OpenApiOperation
|
||||
servers?: OpenApiServer[]
|
||||
parameters?: OpenApiParameter[]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { OpenApiPath } from "./OpenApiPath"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#pathsObject
|
||||
*/
|
||||
export interface OpenApiPaths {
|
||||
[path: string]: OpenApiPath
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#referenceObject
|
||||
*/
|
||||
export interface OpenApiReference {
|
||||
$ref?: string
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiMediaType } from "./OpenApiMediaType"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#requestBodyObject
|
||||
*/
|
||||
export interface OpenApiRequestBody extends OpenApiReference {
|
||||
description?: string
|
||||
content: Dictionary<OpenApiMediaType>
|
||||
required?: boolean
|
||||
nullable?: boolean
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiHeader } from "./OpenApiHeader"
|
||||
import type { OpenApiLink } from "./OpenApiLink"
|
||||
import type { OpenApiMediaType } from "./OpenApiMediaType"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject
|
||||
*/
|
||||
export interface OpenApiResponse extends OpenApiReference {
|
||||
description: string
|
||||
headers?: Dictionary<OpenApiHeader>
|
||||
content?: Dictionary<OpenApiMediaType>
|
||||
links?: Dictionary<OpenApiLink>
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
import type { OpenApiResponse } from "./OpenApiResponse"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject
|
||||
*/
|
||||
export interface OpenApiResponses extends OpenApiReference {
|
||||
default?: OpenApiResponse
|
||||
|
||||
[httpcode: string]: OpenApiResponse
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { WithEnumExtension } from "./Extensions/WithEnumExtension"
|
||||
import type { OpenApiDiscriminator } from "./OpenApiDiscriminator"
|
||||
import type { OpenApiExternalDocs } from "./OpenApiExternalDocs"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
import type { OpenApiXml } from "./OpenApiXml"
|
||||
import { WithExtendedRelationsExtension } from "./Extensions/WithDefaultRelationsExtension"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject
|
||||
*/
|
||||
export interface OpenApiSchema
|
||||
extends OpenApiReference,
|
||||
WithEnumExtension,
|
||||
WithExtendedRelationsExtension {
|
||||
title?: string
|
||||
multipleOf?: number
|
||||
maximum?: number
|
||||
exclusiveMaximum?: boolean
|
||||
minimum?: number
|
||||
exclusiveMinimum?: boolean
|
||||
maxLength?: number
|
||||
minLength?: number
|
||||
pattern?: string
|
||||
maxItems?: number
|
||||
minItems?: number
|
||||
uniqueItems?: boolean
|
||||
maxProperties?: number
|
||||
minProperties?: number
|
||||
required?: string[]
|
||||
enum?: (string | number)[]
|
||||
type?: string | string[]
|
||||
allOf?: OpenApiSchema[]
|
||||
oneOf?: OpenApiSchema[]
|
||||
anyOf?: OpenApiSchema[]
|
||||
not?: OpenApiSchema[]
|
||||
items?: OpenApiSchema
|
||||
properties?: Dictionary<OpenApiSchema>
|
||||
additionalProperties?: boolean | OpenApiSchema
|
||||
description?: string
|
||||
format?:
|
||||
| "int32"
|
||||
| "int64"
|
||||
| "float"
|
||||
| "double"
|
||||
| "string"
|
||||
| "boolean"
|
||||
| "byte"
|
||||
| "binary"
|
||||
| "date"
|
||||
| "date-time"
|
||||
| "password"
|
||||
default?: any
|
||||
nullable?: boolean
|
||||
discriminator?: OpenApiDiscriminator
|
||||
readOnly?: boolean
|
||||
writeOnly?: boolean
|
||||
xml?: OpenApiXml
|
||||
externalDocs?: OpenApiExternalDocs
|
||||
example?: any
|
||||
deprecated?: boolean
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#securityRequirementObject
|
||||
*/
|
||||
export interface OpenApiSecurityRequirement {
|
||||
[name: string]: string
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { OpenApiOAuthFlows } from "./OpenApiOAuthFlows"
|
||||
import type { OpenApiReference } from "./OpenApiReference"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#securitySchemeObject
|
||||
*/
|
||||
export interface OpenApiSecurityScheme extends OpenApiReference {
|
||||
type: "apiKey" | "http" | "oauth2" | "openIdConnect"
|
||||
description?: string
|
||||
name?: string
|
||||
in?: "query" | "header" | "cookie"
|
||||
scheme?: string
|
||||
bearerFormat?: string
|
||||
flows?: OpenApiOAuthFlows
|
||||
openIdConnectUrl?: string
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApiServerVariable } from "./OpenApiServerVariable"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverObject
|
||||
*/
|
||||
export interface OpenApiServer {
|
||||
url: string
|
||||
description?: string
|
||||
variables?: Dictionary<OpenApiServerVariable>
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { WithEnumExtension } from "./Extensions/WithEnumExtension"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverVariableObject
|
||||
*/
|
||||
export interface OpenApiServerVariable extends WithEnumExtension {
|
||||
enum?: (string | number)[]
|
||||
default: string
|
||||
description?: string
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { OpenApiExternalDocs } from "./OpenApiExternalDocs"
|
||||
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#tagObject
|
||||
*/
|
||||
export interface OpenApiTag {
|
||||
name: string
|
||||
description?: string
|
||||
externalDocs?: OpenApiExternalDocs
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#xmlObject
|
||||
*/
|
||||
export interface OpenApiXml {
|
||||
name?: string
|
||||
namespace?: string
|
||||
prefix?: string
|
||||
attribute?: boolean
|
||||
wrapped?: boolean
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { getModel } from "../getModel"
|
||||
import { OpenApi } from "../../interfaces/OpenApi"
|
||||
import { Model } from "../../../../client/interfaces/Model"
|
||||
import { OpenApiSchema } from "../../interfaces/OpenApiSchema"
|
||||
|
||||
describe("getModel", () => {
|
||||
let openApi: OpenApi
|
||||
beforeEach(async () => {
|
||||
openApi = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Test",
|
||||
version: "1.0.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {},
|
||||
}
|
||||
})
|
||||
|
||||
it("should set model spec with definition", () => {
|
||||
const modelName = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
expect(model.spec).toEqual(definition)
|
||||
})
|
||||
|
||||
it("should set property spec with definition", () => {
|
||||
const modelName = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
order: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
expect(model.properties[0].spec).toEqual(definition.properties!.order)
|
||||
})
|
||||
})
|
||||
@@ -1,169 +0,0 @@
|
||||
import { getModels } from "../getModels"
|
||||
import { OpenApi } from "../../interfaces/OpenApi"
|
||||
|
||||
describe("getModels", () => {
|
||||
let openApi: OpenApi
|
||||
beforeEach(async () => {
|
||||
openApi = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Test",
|
||||
version: "1.0.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {},
|
||||
}
|
||||
})
|
||||
|
||||
it("should return an empty array if no models are found", () => {
|
||||
const models = getModels(openApi)
|
||||
expect(models).toEqual([])
|
||||
})
|
||||
|
||||
it("should return an array of models", () => {
|
||||
openApi.components = {
|
||||
schemas: {
|
||||
OrderRes: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const models = getModels(openApi)
|
||||
expect(models).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "OrderRes",
|
||||
properties: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "id",
|
||||
type: "string",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should return an array of models with expanded relations", () => {
|
||||
openApi.components = {
|
||||
schemas: {
|
||||
OrderRes: {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["region", "region.country"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
},
|
||||
Order: {
|
||||
type: "object",
|
||||
properties: {
|
||||
region: {
|
||||
$ref: "#/components/schemas/Region",
|
||||
},
|
||||
},
|
||||
},
|
||||
Region: {
|
||||
type: "object",
|
||||
properties: {
|
||||
country: {
|
||||
$ref: "#/components/schemas/Country",
|
||||
},
|
||||
},
|
||||
},
|
||||
Country: {
|
||||
type: "object",
|
||||
},
|
||||
},
|
||||
}
|
||||
const models = getModels(openApi)
|
||||
expect(models).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "OrderRes",
|
||||
properties: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "order",
|
||||
base: "Order",
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
base: "Order",
|
||||
field: "order",
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "region",
|
||||
base: "Region",
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
base: "Country",
|
||||
field: "country",
|
||||
nestedRelations: [],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should convert query parameters into a schema when x-codegen.queryParams is declared", () => {
|
||||
openApi.paths = {
|
||||
"/": {
|
||||
get: {
|
||||
operationId: "GetOrder",
|
||||
"x-codegen": {
|
||||
queryParams: "GetOrderQueryParams",
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
description: "Limit the number of results",
|
||||
in: "query",
|
||||
name: "limit",
|
||||
schema: {
|
||||
type: "integer",
|
||||
},
|
||||
required: true,
|
||||
deprecated: true,
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const models = getModels(openApi)
|
||||
expect(models).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "GetOrderQueryParams",
|
||||
properties: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
description: "Limit the number of results",
|
||||
name: "limit",
|
||||
type: "number",
|
||||
isRequired: true,
|
||||
deprecated: true,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,436 +0,0 @@
|
||||
import { Model } from "../../../../client/interfaces/Model"
|
||||
import { handleExpandedRelations } from "../getModelsExpandedRelations"
|
||||
import { getModel } from "../getModel"
|
||||
import { OpenApi } from "../../interfaces/OpenApi"
|
||||
import { OpenApiSchema } from "../../interfaces/OpenApiSchema"
|
||||
import { getType } from "../getType"
|
||||
|
||||
function getModelsTest(openApi: OpenApi): Model[] {
|
||||
const models: Model[] = []
|
||||
if (openApi.components) {
|
||||
for (const definitionName in openApi.components.schemas) {
|
||||
if (openApi.components.schemas.hasOwnProperty(definitionName)) {
|
||||
const definition = openApi.components.schemas[definitionName]
|
||||
const definitionType = getType(definitionName)
|
||||
const model = getModel(openApi, definition, true, definitionType.base)
|
||||
models.push(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
return models
|
||||
}
|
||||
|
||||
describe("getModelsExpandedRelations", () => {
|
||||
let openApi: OpenApi
|
||||
beforeEach(async () => {
|
||||
openApi = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Test",
|
||||
version: "1.0.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {
|
||||
schemas: {
|
||||
Order: {
|
||||
type: "object",
|
||||
properties: {
|
||||
region: {
|
||||
$ref: "#/components/schemas/Region",
|
||||
},
|
||||
total: {
|
||||
type: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
Region: {
|
||||
type: "object",
|
||||
properties: {
|
||||
country: {
|
||||
$ref: "#/components/schemas/Country",
|
||||
},
|
||||
},
|
||||
},
|
||||
Country: {
|
||||
type: "object",
|
||||
},
|
||||
Customer: {
|
||||
type: "object",
|
||||
properties: {
|
||||
orders: {
|
||||
type: "array",
|
||||
items: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe("basic use cases", () => {
|
||||
it("should find nested relation - model", () => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["region"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.properties[0].nestedRelations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "order",
|
||||
base: "Order",
|
||||
isArray: false,
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "region",
|
||||
base: "Region",
|
||||
isArray: false,
|
||||
nestedRelations: [],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should find nested relation - shallow", () => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["total"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.properties[0].nestedRelations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "order",
|
||||
base: "Order",
|
||||
isArray: false,
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "total",
|
||||
nestedRelations: [],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should find nested relation - array", () => {
|
||||
const modelName: string = "CustomerRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "customer",
|
||||
relations: ["orders"],
|
||||
},
|
||||
properties: {
|
||||
customer: {
|
||||
$ref: "#/components/schemas/Customer",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.properties[0].nestedRelations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "customer",
|
||||
base: "Customer",
|
||||
isArray: false,
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "orders",
|
||||
base: "Order",
|
||||
isArray: true,
|
||||
nestedRelations: [],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("misc usage", () => {
|
||||
it.each([["allOf"], ["anyOf"], ["oneOf"]])(
|
||||
"should findPropInCombination - %s",
|
||||
(combination) => {
|
||||
openApi.components!.schemas!.ExpandedOrder = {
|
||||
[combination]: [{ $ref: "#/components/schemas/Order" }],
|
||||
}
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["region"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/ExpandedOrder",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.properties[0].nestedRelations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "region",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
it.each([["relations"], ["totals"], ["implicit"], ["eager"]])(
|
||||
"should find nested relation with relation type - %s",
|
||||
(relationType) => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
[relationType]: ["region"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.properties[0].nestedRelations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "region",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
it("should set field hasDepth - true", () => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["region.country"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.properties[0].nestedRelations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
hasDepth: true,
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "region",
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "country",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should set relation hasDepth - true", () => {
|
||||
const modelName: string = "CustomerRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "customer",
|
||||
relations: ["orders.region.country"],
|
||||
},
|
||||
properties: {
|
||||
customer: {
|
||||
$ref: "#/components/schemas/Customer",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.properties[0].nestedRelations).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
hasDepth: true,
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "orders",
|
||||
hasDepth: true,
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "region",
|
||||
nestedRelations: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
field: "country",
|
||||
nestedRelations: [],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("should add models with relation to root model imports, only once", () => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["region", "region.country"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
handleExpandedRelations(model, models)
|
||||
|
||||
expect(model.imports).toEqual(expect.arrayContaining(["Order", "Region"]))
|
||||
})
|
||||
})
|
||||
|
||||
describe("errors", () => {
|
||||
it("should throw if field is not found", () => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "nope",
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
expect(() => handleExpandedRelations(model, models)).toThrow(
|
||||
"x-expanded-relations - field not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw if relation is not found", () => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["nope"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
$ref: "#/components/schemas/Order",
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
expect(() => handleExpandedRelations(model, models)).toThrow(
|
||||
"x-expanded-relations - relation not found"
|
||||
)
|
||||
})
|
||||
|
||||
it.each([["allOf"], ["anyOf"], ["oneOf"]])(
|
||||
"should throw if field exports as a combination - %s",
|
||||
(combination) => {
|
||||
const modelName: string = "OrderRes"
|
||||
const definition: OpenApiSchema = {
|
||||
type: "object",
|
||||
"x-expanded-relations": {
|
||||
field: "order",
|
||||
relations: ["region"],
|
||||
},
|
||||
properties: {
|
||||
order: {
|
||||
[combination]: [{ $ref: "#/components/schemas/Order" }],
|
||||
},
|
||||
},
|
||||
}
|
||||
const model: Model = getModel(openApi, definition, true, modelName)
|
||||
const models: Model[] = [...getModelsTest(openApi), model]
|
||||
|
||||
expect(() => handleExpandedRelations(model, models)).toThrow(
|
||||
"x-expanded-relations - unsupported - field referencing multiple models"
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,71 +0,0 @@
|
||||
import { OpenApi } from "../../interfaces/OpenApi"
|
||||
import { OpenApiOperation } from "../../interfaces/OpenApiOperation"
|
||||
import { getOperation } from "../getOperation"
|
||||
import { getOperationParameters } from "../getOperationParameters"
|
||||
|
||||
describe("getOperation", () => {
|
||||
let openApi: OpenApi
|
||||
beforeEach(async () => {
|
||||
openApi = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Test",
|
||||
version: "1.0.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {},
|
||||
}
|
||||
})
|
||||
|
||||
it("should parse x-codegen", () => {
|
||||
const op: OpenApiOperation = {
|
||||
"x-codegen": {
|
||||
method: "list",
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK",
|
||||
},
|
||||
},
|
||||
}
|
||||
const pathParams = getOperationParameters(openApi, [])
|
||||
const operation = getOperation(
|
||||
openApi,
|
||||
"/orders",
|
||||
"get",
|
||||
"Orders",
|
||||
op,
|
||||
pathParams
|
||||
)
|
||||
expect(operation).toEqual(
|
||||
expect.objectContaining({
|
||||
codegen: { method: "list" },
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should add x-codegen.queryParams to imports", () => {
|
||||
const op: OpenApiOperation = {
|
||||
"x-codegen": {
|
||||
queryParams: "OrdersQueryParams",
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "OK",
|
||||
},
|
||||
},
|
||||
}
|
||||
const pathParams = getOperationParameters(openApi, [])
|
||||
const operation = getOperation(
|
||||
openApi,
|
||||
"/orders",
|
||||
"get",
|
||||
"Orders",
|
||||
op,
|
||||
pathParams
|
||||
)
|
||||
expect(operation.imports).toEqual(
|
||||
expect.arrayContaining(["OrdersQueryParams"])
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,27 +0,0 @@
|
||||
import { OpenApi } from "../../interfaces/OpenApi"
|
||||
import { getOperationParameter } from "../getOperationParameter"
|
||||
import { OpenApiParameter } from "../../interfaces/OpenApiParameter"
|
||||
|
||||
describe("getOperation", () => {
|
||||
let openApi: OpenApi
|
||||
beforeEach(async () => {
|
||||
openApi = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Test",
|
||||
version: "1.0.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {},
|
||||
}
|
||||
})
|
||||
|
||||
it("should set spec with definition", () => {
|
||||
const parameter: OpenApiParameter = {
|
||||
name: "id",
|
||||
in: "path",
|
||||
}
|
||||
const operationParameter = getOperationParameter(openApi, parameter)
|
||||
expect(operationParameter.spec).toEqual(parameter)
|
||||
})
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import { OpenApi } from "../../interfaces/OpenApi"
|
||||
import { getOperationRequestBody } from "../getOperationRequestBody"
|
||||
import { OpenApiRequestBody } from "../../interfaces/OpenApiRequestBody"
|
||||
|
||||
describe("getOperation", () => {
|
||||
let openApi: OpenApi
|
||||
beforeEach(async () => {
|
||||
openApi = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Test",
|
||||
version: "1.0.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {},
|
||||
}
|
||||
})
|
||||
|
||||
it("should set spec with definition", () => {
|
||||
const body: OpenApiRequestBody = {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const operationRequestBody = getOperationRequestBody(openApi, body)
|
||||
expect(operationRequestBody.spec).toEqual(body)
|
||||
})
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
import { OpenApi } from "../../interfaces/OpenApi"
|
||||
import { getOperationResponse } from "../getOperationResponse"
|
||||
import { OpenApiResponse } from "../../interfaces/OpenApiResponse"
|
||||
|
||||
describe("getOperation", () => {
|
||||
let openApi: OpenApi
|
||||
beforeEach(async () => {
|
||||
openApi = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Test",
|
||||
version: "1.0.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {},
|
||||
}
|
||||
})
|
||||
|
||||
it("should set spec with definition", () => {
|
||||
const response: OpenApiResponse = {
|
||||
description: "OK",
|
||||
}
|
||||
const operationResponse = getOperationResponse(openApi, response, 200)
|
||||
expect(operationResponse.spec).toEqual(response)
|
||||
})
|
||||
})
|
||||
@@ -1,9 +0,0 @@
|
||||
export const escapeName = (value: string): string => {
|
||||
if (value || value === "") {
|
||||
const validName = /^[a-zA-Z_$][\w$]+$/g.test(value)
|
||||
if (!validName) {
|
||||
return `'${value}'`
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { Enum } from "../../../client/interfaces/Enum"
|
||||
import { isString } from "../../../utils/isString"
|
||||
import type { WithEnumExtension } from "../interfaces/Extensions/WithEnumExtension"
|
||||
|
||||
/**
|
||||
* Extend the enum with the x-enum properties. This adds the capability
|
||||
* to use names and descriptions inside the generated enums.
|
||||
* @param enumerators
|
||||
* @param definition
|
||||
*/
|
||||
export const extendEnum = (
|
||||
enumerators: Enum[],
|
||||
definition: WithEnumExtension
|
||||
): Enum[] => {
|
||||
const names = definition["x-enum-varnames"]?.filter(isString)
|
||||
const descriptions = definition["x-enum-descriptions"]?.filter(isString)
|
||||
|
||||
return enumerators.map((enumerator, index) => ({
|
||||
name: names?.[index] || enumerator.name,
|
||||
description: descriptions?.[index] || enumerator.description,
|
||||
value: enumerator.value,
|
||||
type: enumerator.type,
|
||||
}))
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { isDefined } from "../../../utils/isDefined"
|
||||
import type { Dictionary } from "../../../utils/types"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiMediaType } from "../interfaces/OpenApiMediaType"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
|
||||
export interface Content {
|
||||
mediaType: string
|
||||
schema: OpenApiSchema
|
||||
}
|
||||
|
||||
const BASIC_MEDIA_TYPES = [
|
||||
"application/json-patch+json",
|
||||
"application/json",
|
||||
"application/x-www-form-urlencoded",
|
||||
"text/json",
|
||||
"text/plain",
|
||||
"multipart/form-data",
|
||||
"multipart/mixed",
|
||||
"multipart/related",
|
||||
"multipart/batch",
|
||||
]
|
||||
|
||||
export const getContent = (
|
||||
openApi: OpenApi,
|
||||
content: Dictionary<OpenApiMediaType>
|
||||
): Content | null => {
|
||||
const basicMediaTypeWithSchema = Object.keys(content)
|
||||
.filter((mediaType) => {
|
||||
const cleanMediaType = mediaType.split(";")[0].trim()
|
||||
return BASIC_MEDIA_TYPES.includes(cleanMediaType)
|
||||
})
|
||||
.find((mediaType) => isDefined(content[mediaType]?.schema))
|
||||
if (basicMediaTypeWithSchema) {
|
||||
return {
|
||||
mediaType: basicMediaTypeWithSchema,
|
||||
schema: content[basicMediaTypeWithSchema].schema as OpenApiSchema,
|
||||
}
|
||||
}
|
||||
|
||||
const firstMediaTypeWithSchema = Object.keys(content).find((mediaType) =>
|
||||
isDefined(content[mediaType]?.schema)
|
||||
)
|
||||
if (firstMediaTypeWithSchema) {
|
||||
return {
|
||||
mediaType: firstMediaTypeWithSchema,
|
||||
schema: content[firstMediaTypeWithSchema].schema as OpenApiSchema,
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { Enum } from "../../../client/interfaces/Enum"
|
||||
|
||||
export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
if (Array.isArray(values)) {
|
||||
return values
|
||||
.filter((value, index, arr) => {
|
||||
return arr.indexOf(value) === index
|
||||
})
|
||||
.filter((value: any) => {
|
||||
return typeof value === "number" || typeof value === "string"
|
||||
})
|
||||
.map((value) => {
|
||||
if (typeof value === "number") {
|
||||
return {
|
||||
name: `'_${value}'`,
|
||||
value: String(value),
|
||||
type: "number",
|
||||
description: null,
|
||||
}
|
||||
}
|
||||
return {
|
||||
name: String(value)
|
||||
.replace(/\W+/g, "_")
|
||||
.replace(/^(\d+)/g, "_$1")
|
||||
.replace(/([a-z])([A-Z]+)/g, "$1_$2")
|
||||
.toUpperCase(),
|
||||
value: `'${value.replace(/'/g, "\\'")}'`,
|
||||
type: "string",
|
||||
description: null,
|
||||
}
|
||||
})
|
||||
}
|
||||
return []
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
const TYPE_MAPPINGS = new Map<string, string>([
|
||||
["file", "binary"],
|
||||
["any", "any"],
|
||||
["object", "any"],
|
||||
["array", "any[]"],
|
||||
["boolean", "boolean"],
|
||||
["byte", "number"],
|
||||
["int", "number"],
|
||||
["integer", "number"],
|
||||
["float", "number"],
|
||||
["double", "number"],
|
||||
["short", "number"],
|
||||
["long", "number"],
|
||||
["number", "number"],
|
||||
["char", "string"],
|
||||
["date", "string"],
|
||||
["date-time", "string"],
|
||||
["password", "string"],
|
||||
["string", "string"],
|
||||
["void", "void"],
|
||||
["null", "null"],
|
||||
])
|
||||
|
||||
/**
|
||||
* Get mapped type for given type to any basic Typescript/Javascript type.
|
||||
*/
|
||||
export const getMappedType = (
|
||||
type: string,
|
||||
format?: string
|
||||
): string | undefined => {
|
||||
if (format === "binary") {
|
||||
return "binary"
|
||||
}
|
||||
return TYPE_MAPPINGS.get(type)
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
import type { Model } from "../../../client/interfaces/Model"
|
||||
import { getPattern } from "../../../utils/getPattern"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
import { extendEnum } from "./extendEnum"
|
||||
import { getEnum } from "./getEnum"
|
||||
import { getModelComposition } from "./getModelComposition"
|
||||
import { getModelDefault } from "./getModelDefault"
|
||||
import { getModelProperties } from "./getModelProperties"
|
||||
import { getType } from "./getType"
|
||||
|
||||
export const getModel = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
isDefinition: boolean = false,
|
||||
name: string = ""
|
||||
): Model => {
|
||||
const model: Model = {
|
||||
spec: definition,
|
||||
name,
|
||||
export: "interface",
|
||||
type: "any",
|
||||
base: "any",
|
||||
template: null,
|
||||
link: null,
|
||||
description: definition.description || null,
|
||||
deprecated: definition.deprecated === true,
|
||||
isDefinition,
|
||||
isReadOnly: definition.readOnly === true,
|
||||
isNullable: definition.nullable === true,
|
||||
isRequired: false,
|
||||
format: definition.format,
|
||||
maximum: definition.maximum,
|
||||
exclusiveMaximum: definition.exclusiveMaximum,
|
||||
minimum: definition.minimum,
|
||||
exclusiveMinimum: definition.exclusiveMinimum,
|
||||
multipleOf: definition.multipleOf,
|
||||
maxLength: definition.maxLength,
|
||||
minLength: definition.minLength,
|
||||
maxItems: definition.maxItems,
|
||||
minItems: definition.minItems,
|
||||
uniqueItems: definition.uniqueItems,
|
||||
maxProperties: definition.maxProperties,
|
||||
minProperties: definition.minProperties,
|
||||
pattern: getPattern(definition.pattern),
|
||||
imports: [],
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
}
|
||||
|
||||
if (definition.$ref) {
|
||||
const definitionRef = getType(definition.$ref)
|
||||
model.export = "reference"
|
||||
model.type = definitionRef.type
|
||||
model.base = definitionRef.base
|
||||
model.template = definitionRef.template
|
||||
model.imports.push(...definitionRef.imports)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
}
|
||||
|
||||
if (definition.enum && definition.type !== "boolean") {
|
||||
const enumerators = getEnum(definition.enum)
|
||||
const extendedEnumerators = extendEnum(enumerators, definition)
|
||||
if (extendedEnumerators.length) {
|
||||
model.export = "enum"
|
||||
model.type = "string"
|
||||
model.base = "string"
|
||||
model.enum.push(...extendedEnumerators)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.type === "array" && definition.items) {
|
||||
if (definition.items.$ref) {
|
||||
const arrayItems = getType(definition.items.$ref)
|
||||
model.export = "array"
|
||||
model.type = arrayItems.type
|
||||
model.base = arrayItems.base
|
||||
model.template = arrayItems.template
|
||||
model.imports.push(...arrayItems.imports)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
} else {
|
||||
const arrayItems = getModel(openApi, definition.items)
|
||||
model.export = "array"
|
||||
model.type = arrayItems.type
|
||||
model.base = arrayItems.base
|
||||
model.template = arrayItems.template
|
||||
model.link = arrayItems
|
||||
model.imports.push(...arrayItems.imports)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
definition.type === "object" &&
|
||||
(typeof definition.additionalProperties === "object" ||
|
||||
definition.additionalProperties === true)
|
||||
) {
|
||||
const ap =
|
||||
typeof definition.additionalProperties === "object"
|
||||
? definition.additionalProperties
|
||||
: {}
|
||||
if (ap.$ref) {
|
||||
const additionalProperties = getType(ap.$ref)
|
||||
model.export = "dictionary"
|
||||
model.type = additionalProperties.type
|
||||
model.base = additionalProperties.base
|
||||
model.template = additionalProperties.template
|
||||
model.imports.push(...additionalProperties.imports)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
} else {
|
||||
const additionalProperties = getModel(openApi, ap)
|
||||
model.export = "dictionary"
|
||||
model.type = additionalProperties.type
|
||||
model.base = additionalProperties.base
|
||||
model.template = additionalProperties.template
|
||||
model.link = additionalProperties
|
||||
model.imports.push(...additionalProperties.imports)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.oneOf?.length) {
|
||||
const composition = getModelComposition(
|
||||
openApi,
|
||||
definition,
|
||||
definition.oneOf,
|
||||
"one-of",
|
||||
getModel
|
||||
)
|
||||
model.export = composition.type
|
||||
model.imports.push(...composition.imports)
|
||||
model.properties.push(...composition.properties)
|
||||
model.enums.push(...composition.enums)
|
||||
return model
|
||||
}
|
||||
|
||||
if (definition.anyOf?.length) {
|
||||
const composition = getModelComposition(
|
||||
openApi,
|
||||
definition,
|
||||
definition.anyOf,
|
||||
"any-of",
|
||||
getModel
|
||||
)
|
||||
model.export = composition.type
|
||||
model.imports.push(...composition.imports)
|
||||
model.properties.push(...composition.properties)
|
||||
model.enums.push(...composition.enums)
|
||||
return model
|
||||
}
|
||||
|
||||
if (definition.allOf?.length) {
|
||||
const composition = getModelComposition(
|
||||
openApi,
|
||||
definition,
|
||||
definition.allOf,
|
||||
"all-of",
|
||||
getModel
|
||||
)
|
||||
model.export = composition.type
|
||||
model.imports.push(...composition.imports)
|
||||
model.properties.push(...composition.properties)
|
||||
model.enums.push(...composition.enums)
|
||||
return model
|
||||
}
|
||||
|
||||
if (definition.type === "object") {
|
||||
if (definition.properties) {
|
||||
model.export = "interface"
|
||||
model.type = "any"
|
||||
model.base = "any"
|
||||
model.default = getModelDefault(definition, model)
|
||||
|
||||
const modelProperties = getModelProperties(
|
||||
openApi,
|
||||
definition,
|
||||
getModel,
|
||||
model
|
||||
)
|
||||
modelProperties.forEach((modelProperty) => {
|
||||
model.imports.push(...modelProperty.imports)
|
||||
model.enums.push(...modelProperty.enums)
|
||||
model.properties.push(modelProperty)
|
||||
if (modelProperty.export === "enum") {
|
||||
model.enums.push(modelProperty)
|
||||
}
|
||||
})
|
||||
return model
|
||||
} else {
|
||||
const additionalProperties = getModel(openApi, {})
|
||||
model.export = "dictionary"
|
||||
model.type = additionalProperties.type
|
||||
model.base = additionalProperties.base
|
||||
model.template = additionalProperties.template
|
||||
model.link = additionalProperties
|
||||
model.imports.push(...additionalProperties.imports)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
}
|
||||
}
|
||||
|
||||
// If the schema has a type than it can be a basic or generic type.
|
||||
if (definition.type) {
|
||||
const definitionType = getType(definition.type, definition.format)
|
||||
model.export = "generic"
|
||||
model.type = definitionType.type
|
||||
model.base = definitionType.base
|
||||
model.template = definitionType.template
|
||||
model.isNullable = definitionType.isNullable || model.isNullable
|
||||
model.imports.push(...definitionType.imports)
|
||||
model.default = getModelDefault(definition, model)
|
||||
return model
|
||||
}
|
||||
|
||||
return model
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import type { Model } from "../../../client/interfaces/Model"
|
||||
import type { ModelComposition } from "../../../client/interfaces/ModelComposition"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
import type { getModel } from "./getModel"
|
||||
import { getModelProperties } from "./getModelProperties"
|
||||
import { getRequiredPropertiesFromComposition } from "./getRequiredPropertiesFromComposition"
|
||||
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel
|
||||
|
||||
export const getModelComposition = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
definitions: OpenApiSchema[],
|
||||
type: "one-of" | "any-of" | "all-of",
|
||||
getModel: GetModelFn
|
||||
): ModelComposition => {
|
||||
const composition: ModelComposition = {
|
||||
type,
|
||||
imports: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
}
|
||||
|
||||
const properties: Model[] = []
|
||||
|
||||
definitions
|
||||
.map((definition) => getModel(openApi, definition))
|
||||
.filter((model) => {
|
||||
const hasProperties = model.properties.length
|
||||
const hasEnums = model.enums.length
|
||||
const isObject = model.type === "any"
|
||||
const isDictionary = model.export === "dictionary"
|
||||
const isEmpty = isObject && !hasProperties && !hasEnums
|
||||
return !isEmpty || isDictionary
|
||||
})
|
||||
.forEach((model) => {
|
||||
composition.imports.push(...model.imports)
|
||||
composition.enums.push(...model.enums)
|
||||
composition.properties.push(model)
|
||||
})
|
||||
|
||||
if (definition.required) {
|
||||
const requiredProperties = getRequiredPropertiesFromComposition(
|
||||
openApi,
|
||||
definition.required,
|
||||
definitions,
|
||||
getModel
|
||||
)
|
||||
requiredProperties.forEach((requiredProperty) => {
|
||||
composition.imports.push(...requiredProperty.imports)
|
||||
composition.enums.push(...requiredProperty.enums)
|
||||
})
|
||||
properties.push(...requiredProperties)
|
||||
}
|
||||
|
||||
if (definition.properties) {
|
||||
const modelProperties = getModelProperties(openApi, definition, getModel)
|
||||
modelProperties.forEach((modelProperty) => {
|
||||
composition.imports.push(...modelProperty.imports)
|
||||
composition.enums.push(...modelProperty.enums)
|
||||
if (modelProperty.export === "enum") {
|
||||
composition.enums.push(modelProperty)
|
||||
}
|
||||
})
|
||||
properties.push(...modelProperties)
|
||||
}
|
||||
|
||||
if (properties.length) {
|
||||
composition.properties.push({
|
||||
spec: definition,
|
||||
name: "properties",
|
||||
export: "interface",
|
||||
type: "any",
|
||||
base: "any",
|
||||
template: null,
|
||||
link: null,
|
||||
description: "",
|
||||
isDefinition: false,
|
||||
isReadOnly: false,
|
||||
isNullable: false,
|
||||
isRequired: false,
|
||||
imports: [],
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties,
|
||||
})
|
||||
}
|
||||
|
||||
return composition
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import type { Model } from "../../../client/interfaces/Model"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
|
||||
export const getModelDefault = (
|
||||
definition: OpenApiSchema,
|
||||
model?: Model
|
||||
): string | undefined => {
|
||||
if (definition.default === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (definition.default === null) {
|
||||
return "null"
|
||||
}
|
||||
|
||||
const type = definition.type || typeof definition.default
|
||||
|
||||
switch (type) {
|
||||
case "int":
|
||||
case "integer":
|
||||
case "number":
|
||||
if (model?.export === "enum" && model.enum?.[definition.default]) {
|
||||
return model.enum[definition.default].value
|
||||
}
|
||||
return definition.default
|
||||
|
||||
case "boolean":
|
||||
return JSON.stringify(definition.default)
|
||||
|
||||
case "string":
|
||||
return `'${definition.default}'`
|
||||
|
||||
case "object":
|
||||
try {
|
||||
return JSON.stringify(definition.default, null, 4)
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import type { Model } from "../../../client/interfaces/Model"
|
||||
import {
|
||||
findOneOfParentDiscriminator,
|
||||
mapPropertyValue,
|
||||
} from "../../../utils/discriminator"
|
||||
import { getPattern } from "../../../utils/getPattern"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
import { escapeName } from "./escapeName"
|
||||
import type { getModel } from "./getModel"
|
||||
import { getType } from "./getType"
|
||||
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel
|
||||
|
||||
export const getModelProperties = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
getModel: GetModelFn,
|
||||
parent?: Model
|
||||
): Model[] => {
|
||||
const models: Model[] = []
|
||||
const discriminator = findOneOfParentDiscriminator(openApi, parent)
|
||||
for (const propertyName in definition.properties) {
|
||||
if (definition.properties.hasOwnProperty(propertyName)) {
|
||||
const property = definition.properties[propertyName]
|
||||
const propertyRequired = !!definition.required?.includes(propertyName)
|
||||
const propertyValues: Omit<
|
||||
Model,
|
||||
| "export"
|
||||
| "type"
|
||||
| "base"
|
||||
| "template"
|
||||
| "link"
|
||||
| "isNullable"
|
||||
| "imports"
|
||||
| "enum"
|
||||
| "enums"
|
||||
| "properties"
|
||||
> = {
|
||||
spec: property,
|
||||
name: escapeName(propertyName),
|
||||
description: property.description || null,
|
||||
deprecated: property.deprecated === true,
|
||||
isDefinition: false,
|
||||
isReadOnly: property.readOnly === true,
|
||||
isRequired: propertyRequired,
|
||||
format: property.format,
|
||||
maximum: property.maximum,
|
||||
exclusiveMaximum: property.exclusiveMaximum,
|
||||
minimum: property.minimum,
|
||||
exclusiveMinimum: property.exclusiveMinimum,
|
||||
multipleOf: property.multipleOf,
|
||||
maxLength: property.maxLength,
|
||||
minLength: property.minLength,
|
||||
maxItems: property.maxItems,
|
||||
minItems: property.minItems,
|
||||
uniqueItems: property.uniqueItems,
|
||||
maxProperties: property.maxProperties,
|
||||
minProperties: property.minProperties,
|
||||
pattern: getPattern(property.pattern),
|
||||
}
|
||||
if (parent && discriminator?.propertyName == propertyName) {
|
||||
models.push({
|
||||
export: "reference",
|
||||
type: "string",
|
||||
base: `'${mapPropertyValue(discriminator, parent)}'`,
|
||||
template: null,
|
||||
isNullable: property.nullable === true,
|
||||
link: null,
|
||||
imports: [],
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
...propertyValues,
|
||||
})
|
||||
} else if (property.$ref) {
|
||||
const model = getType(property.$ref)
|
||||
models.push({
|
||||
export: "reference",
|
||||
type: model.type,
|
||||
base: model.base,
|
||||
template: model.template,
|
||||
link: null,
|
||||
isNullable: model.isNullable || property.nullable === true,
|
||||
imports: model.imports,
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
...propertyValues,
|
||||
})
|
||||
} else {
|
||||
const model = getModel(openApi, property)
|
||||
models.push({
|
||||
export: model.export,
|
||||
type: model.type,
|
||||
base: model.base,
|
||||
template: model.template,
|
||||
link: model.link,
|
||||
isNullable: model.isNullable || property.nullable === true,
|
||||
imports: model.imports,
|
||||
enum: model.enum,
|
||||
enums: model.enums,
|
||||
properties: model.properties,
|
||||
...propertyValues,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return models
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { Type } from "../../../client/interfaces/Type"
|
||||
|
||||
/**
|
||||
* If our model has a template type, then we want to generalize that!
|
||||
* In that case we should return "<T>" as our template type.
|
||||
* @param modelClass The parsed model class type.
|
||||
* @returns The model template type (<T> or empty).
|
||||
*/
|
||||
export const getModelTemplate = (modelClass: Type): string => {
|
||||
return modelClass.template ? "<T>" : ""
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
import type { Model } from "../../../client/interfaces/Model"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import { getModel } from "./getModel"
|
||||
import { reservedWords } from "./getOperationParameterName"
|
||||
import { getType } from "./getType"
|
||||
import { OperationParameter } from "../../../client/interfaces/OperationParameter"
|
||||
import { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
import { Dictionary } from "../../../utils/types"
|
||||
import { OpenApiParameter } from "../interfaces/OpenApiParameter"
|
||||
import { listOperations } from "./listOperations"
|
||||
import { handleExpandedRelations } from "./getModelsExpandedRelations"
|
||||
|
||||
export const getModels = (openApi: OpenApi): Model[] => {
|
||||
const models: Model[] = []
|
||||
if (openApi.components) {
|
||||
for (const definitionName in openApi.components.schemas) {
|
||||
if (openApi.components.schemas.hasOwnProperty(definitionName)) {
|
||||
const definition = openApi.components.schemas[definitionName]
|
||||
const definitionType = getType(definitionName)
|
||||
const model = getModel(
|
||||
openApi,
|
||||
definition,
|
||||
true,
|
||||
definitionType.base.replace(reservedWords, "_$1")
|
||||
)
|
||||
models.push(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const model of models) {
|
||||
handleExpandedRelations(model, models)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundle all query parameters in a single typed object
|
||||
* when x-codegen.queryParams is declared on the operation.
|
||||
*/
|
||||
const operations = listOperations(openApi)
|
||||
for (const operation of operations) {
|
||||
if (operation.codegen.queryParams) {
|
||||
const definition = getDefinitionFromParametersQuery(
|
||||
operation.parametersQuery
|
||||
)
|
||||
const model = getModel(
|
||||
openApi,
|
||||
definition,
|
||||
true,
|
||||
operation.codegen.queryParams
|
||||
)
|
||||
models.push(model)
|
||||
}
|
||||
}
|
||||
|
||||
return models
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and converts query parameters into schema properties.
|
||||
* Given a typical parameter OAS:
|
||||
* ```
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 10
|
||||
* required: true
|
||||
* description: Limit the number of results returned.
|
||||
* ```
|
||||
* Convert into a schema property:
|
||||
* ```
|
||||
* UnnamedSchema:
|
||||
* type: object
|
||||
* required:
|
||||
* - limit
|
||||
* properties:
|
||||
* limit:
|
||||
* description: Limit the number of results returned.
|
||||
* type: integer
|
||||
* default: 10
|
||||
* ```
|
||||
*/
|
||||
const getDefinitionFromParametersQuery = (
|
||||
parametersQuery: OperationParameter[]
|
||||
): OpenApiSchema => {
|
||||
/**
|
||||
* Identify query parameters that are required (non-optional).
|
||||
*/
|
||||
const required = parametersQuery
|
||||
.filter((parameter) => (parameter.spec as OpenApiParameter).required)
|
||||
.map((parameter) => (parameter.spec as OpenApiParameter).name)
|
||||
|
||||
const properties: Dictionary<OpenApiSchema> = {}
|
||||
for (const parameter of parametersQuery) {
|
||||
const spec = parameter.spec as OpenApiParameter
|
||||
/**
|
||||
* Augment a copy of schema with description and deprecated
|
||||
* and assign it as a named property on the schema.
|
||||
*/
|
||||
properties[spec.name] = Object.assign(
|
||||
{ ...spec.schema },
|
||||
{
|
||||
description: spec.description,
|
||||
deprecated: spec.deprecated,
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Return an unnamed schema definition.
|
||||
*/
|
||||
return {
|
||||
type: "object",
|
||||
required,
|
||||
properties,
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import { Model, NestedRelation } from "../../../client/interfaces/Model"
|
||||
|
||||
export const handleExpandedRelations = (model: Model, allModels: Model[]) => {
|
||||
const xExpandedRelation = model.spec["x-expanded-relations"]
|
||||
if (!xExpandedRelation) {
|
||||
return
|
||||
}
|
||||
const field = xExpandedRelation.field
|
||||
const relations = xExpandedRelation.relations ?? []
|
||||
const totals = xExpandedRelation.totals ?? []
|
||||
const implicit = xExpandedRelation.implicit ?? []
|
||||
const eager = xExpandedRelation.eager ?? []
|
||||
|
||||
const nestedRelation: NestedRelation = {
|
||||
field,
|
||||
nestedRelations: [],
|
||||
}
|
||||
|
||||
for (const relation of [...relations, ...totals, ...implicit, ...eager]) {
|
||||
const splitRelation = relation.split(".")
|
||||
walkSplitRelations(nestedRelation, splitRelation, 0)
|
||||
}
|
||||
|
||||
const prop = getPropertyByName(nestedRelation.field, model)
|
||||
if (!prop) {
|
||||
throw new Error(`x-expanded-relations - field not found
|
||||
Schema: ${model.name}
|
||||
NestedRelation: ${JSON.stringify(nestedRelation, null, 2)}
|
||||
Model: ${JSON.stringify(model.spec, null, 2)}`)
|
||||
}
|
||||
|
||||
walkNestedRelations(allModels, model, model, nestedRelation)
|
||||
model.imports = [...new Set(model.imports)]
|
||||
/**
|
||||
* To reduce complexity in the exportInterface template, nestedRelations is
|
||||
* set on the property that is the root of the nested relations instead of
|
||||
* setting it on the model.
|
||||
*/
|
||||
prop.nestedRelations = [nestedRelation]
|
||||
}
|
||||
|
||||
const walkSplitRelations = (
|
||||
parentNestedRelation: NestedRelation,
|
||||
splitRelation: string[],
|
||||
depthIndex: number
|
||||
) => {
|
||||
const field = splitRelation[depthIndex]
|
||||
let nestedRelation: NestedRelation | undefined =
|
||||
parentNestedRelation.nestedRelations.find(
|
||||
(nestedRelation) => nestedRelation.field === field
|
||||
)
|
||||
if (!nestedRelation) {
|
||||
nestedRelation = {
|
||||
field,
|
||||
nestedRelations: [],
|
||||
}
|
||||
parentNestedRelation.nestedRelations.push(nestedRelation)
|
||||
}
|
||||
depthIndex++
|
||||
if (depthIndex < splitRelation.length) {
|
||||
walkSplitRelations(nestedRelation, splitRelation, depthIndex)
|
||||
}
|
||||
}
|
||||
|
||||
const walkNestedRelations = (
|
||||
allModels: Model[],
|
||||
rootModel: Model,
|
||||
model: Model,
|
||||
nestedRelation: NestedRelation,
|
||||
parentNestedRelation?: NestedRelation
|
||||
): void => {
|
||||
const prop = ["all-of", "any-of", "one-of"].includes(model.export)
|
||||
? findPropInCombination(nestedRelation.field, model, allModels)
|
||||
: getPropertyByName(nestedRelation.field, model)
|
||||
if (!prop) {
|
||||
throw new Error(`x-expanded-relations - relation not found
|
||||
Schema: ${rootModel.name}
|
||||
NestedRelation: ${JSON.stringify(nestedRelation, null, 2)}
|
||||
Model: ${JSON.stringify(model.spec, null, 2)}`)
|
||||
}
|
||||
if (["all-of", "any-of", "one-of"].includes(prop.export)) {
|
||||
/**
|
||||
* Root property for nested relations can not use combination strategies.
|
||||
* To use combination, they must be defined in referenced models instead.
|
||||
*/
|
||||
throw new Error(`x-expanded-relations - unsupported - field referencing multiple models
|
||||
Schema: ${rootModel.name}
|
||||
NestedRelation: ${JSON.stringify(nestedRelation, null, 2)}
|
||||
Model: ${JSON.stringify(model.spec, null, 2)}`)
|
||||
}
|
||||
if (!["reference", "array"].includes(prop.export)) {
|
||||
return
|
||||
}
|
||||
|
||||
nestedRelation.base = prop.type
|
||||
nestedRelation.isArray = prop.export === "array"
|
||||
|
||||
if (!nestedRelation.nestedRelations.length) {
|
||||
return
|
||||
}
|
||||
const childModel = getModelByName(prop.type, allModels)
|
||||
if (!childModel) {
|
||||
throw new Error(`x-expanded-relations - field referencing unknown model
|
||||
Schema: ${rootModel.name}
|
||||
NestedRelation: ${JSON.stringify(nestedRelation, null, 2)}
|
||||
Model: ${JSON.stringify(model.spec, null, 2)}`)
|
||||
}
|
||||
rootModel.imports.push(prop.type)
|
||||
if (parentNestedRelation) {
|
||||
parentNestedRelation.hasDepth = true
|
||||
}
|
||||
for (const childNestedRelation of nestedRelation.nestedRelations) {
|
||||
walkNestedRelations(
|
||||
allModels,
|
||||
rootModel,
|
||||
childModel,
|
||||
childNestedRelation,
|
||||
nestedRelation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const findPropInCombination = (
|
||||
fieldName: string,
|
||||
model: Model,
|
||||
allModels: Model[]
|
||||
) => {
|
||||
for (const property of model.properties) {
|
||||
switch (property.export) {
|
||||
case "interface":
|
||||
return getPropertyByName(fieldName, model)
|
||||
case "reference":
|
||||
const tmpModel = getModelByName(property.type, allModels)
|
||||
if (tmpModel) {
|
||||
return getPropertyByName(fieldName, tmpModel)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getModelByName(name: string, models: Model[]): Model | void {
|
||||
for (const model of models) {
|
||||
if (model.name === name) {
|
||||
return model
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertyByName(name: string, model: Model): Model | void {
|
||||
for (const property of model.properties) {
|
||||
if (property.name === name) {
|
||||
return property
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import type { Operation } from "../../../client/interfaces/Operation"
|
||||
import type { OperationParameters } from "../../../client/interfaces/OperationParameters"
|
||||
import type { OperationCodegen } from "../../../client/interfaces/OperationCodegen"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiOperation } from "../interfaces/OpenApiOperation"
|
||||
import type { OpenApiRequestBody } from "../interfaces/OpenApiRequestBody"
|
||||
import { getOperationErrors } from "./getOperationErrors"
|
||||
import { getOperationName } from "./getOperationName"
|
||||
import { getOperationParameters } from "./getOperationParameters"
|
||||
import { getOperationRequestBody } from "./getOperationRequestBody"
|
||||
import { getOperationResponseHeader } from "./getOperationResponseHeader"
|
||||
import { getOperationResponses } from "./getOperationResponses"
|
||||
import { getOperationResults } from "./getOperationResults"
|
||||
import { getRef } from "./getRef"
|
||||
import { getServiceName } from "./getServiceName"
|
||||
import { sortByRequired } from "./sortByRequired"
|
||||
|
||||
export const getOperation = (
|
||||
openApi: OpenApi,
|
||||
url: string,
|
||||
method: string,
|
||||
tag: string,
|
||||
op: OpenApiOperation,
|
||||
pathParams: OperationParameters
|
||||
): Operation => {
|
||||
const serviceName = getServiceName(tag)
|
||||
const operationName = getOperationName(url, method, op.operationId)
|
||||
const codegen: OperationCodegen = op["x-codegen"] || {}
|
||||
|
||||
// Create a new operation object for this method.
|
||||
const operation: Operation = {
|
||||
service: serviceName,
|
||||
name: codegen.method || operationName,
|
||||
operationId: op.operationId || null,
|
||||
summary: op.summary || null,
|
||||
description: op.description || null,
|
||||
deprecated: op.deprecated === true,
|
||||
method: method.toUpperCase(),
|
||||
path: url,
|
||||
parameters: [...pathParams.parameters],
|
||||
parametersPath: [...pathParams.parametersPath],
|
||||
parametersQuery: [...pathParams.parametersQuery],
|
||||
parametersForm: [...pathParams.parametersForm],
|
||||
parametersHeader: [...pathParams.parametersHeader],
|
||||
parametersCookie: [...pathParams.parametersCookie],
|
||||
parametersBody: pathParams.parametersBody,
|
||||
imports: [],
|
||||
errors: [],
|
||||
results: [],
|
||||
responseHeader: null,
|
||||
codegen,
|
||||
}
|
||||
|
||||
// Parse the operation parameters (path, query, body, etc).
|
||||
if (op.parameters) {
|
||||
const parameters = getOperationParameters(openApi, op.parameters)
|
||||
operation.imports.push(...parameters.imports)
|
||||
operation.parameters.push(...parameters.parameters)
|
||||
operation.parametersPath.push(...parameters.parametersPath)
|
||||
operation.parametersQuery.push(...parameters.parametersQuery)
|
||||
operation.parametersForm.push(...parameters.parametersForm)
|
||||
operation.parametersHeader.push(...parameters.parametersHeader)
|
||||
operation.parametersCookie.push(...parameters.parametersCookie)
|
||||
operation.parametersBody = parameters.parametersBody
|
||||
}
|
||||
|
||||
if (op.requestBody) {
|
||||
const requestBodyDef = getRef<OpenApiRequestBody>(openApi, op.requestBody)
|
||||
const requestBody = getOperationRequestBody(openApi, requestBodyDef)
|
||||
operation.imports.push(...requestBody.imports)
|
||||
operation.parameters.push(requestBody)
|
||||
operation.parametersBody = requestBody
|
||||
}
|
||||
|
||||
// Parse the operation responses.
|
||||
if (op.responses) {
|
||||
const operationResponses = getOperationResponses(openApi, op.responses)
|
||||
const operationResults = getOperationResults(operationResponses)
|
||||
operation.errors = getOperationErrors(operationResponses)
|
||||
operation.responseHeader = getOperationResponseHeader(operationResults)
|
||||
|
||||
operationResults.forEach((operationResult) => {
|
||||
operation.results.push(operationResult)
|
||||
operation.imports.push(...operationResult.imports)
|
||||
})
|
||||
}
|
||||
|
||||
if (codegen.queryParams) {
|
||||
operation.imports.push(codegen.queryParams)
|
||||
}
|
||||
|
||||
operation.parameters = operation.parameters.sort(sortByRequired)
|
||||
|
||||
return operation
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { OperationError } from "../../../client/interfaces/OperationError"
|
||||
import type { OperationResponse } from "../../../client/interfaces/OperationResponse"
|
||||
|
||||
export const getOperationErrors = (
|
||||
operationResponses: OperationResponse[]
|
||||
): OperationError[] => {
|
||||
return operationResponses
|
||||
.filter((operationResponse) => {
|
||||
return operationResponse.code >= 300 && operationResponse.description
|
||||
})
|
||||
.map((response) => ({
|
||||
code: response.code,
|
||||
description: response.description!,
|
||||
}))
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import camelCase from "camelcase"
|
||||
|
||||
/**
|
||||
* Convert the input value to a correct operation (method) classname.
|
||||
* This will use the operation ID - if available - and otherwise fallback
|
||||
* on a generated name from the URL
|
||||
*/
|
||||
export const getOperationName = (
|
||||
url: string,
|
||||
method: string,
|
||||
operationId?: string
|
||||
): string => {
|
||||
if (operationId) {
|
||||
return camelCase(
|
||||
operationId
|
||||
.replace(/^[^a-zA-Z]+/g, "")
|
||||
.replace(/[^\w\-]+/g, "-")
|
||||
.trim()
|
||||
)
|
||||
}
|
||||
|
||||
const urlWithoutPlaceholders = url
|
||||
.replace(/[^/]*?{api-version}.*?\//g, "")
|
||||
.replace(/{(.*?)}/g, "")
|
||||
.replace(/\//g, "-")
|
||||
|
||||
return camelCase(`${method}-${urlWithoutPlaceholders}`)
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import type { OperationParameter } from "../../../client/interfaces/OperationParameter"
|
||||
import { getPattern } from "../../../utils/getPattern"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiParameter } from "../interfaces/OpenApiParameter"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
import { getModel } from "./getModel"
|
||||
import { getModelDefault } from "./getModelDefault"
|
||||
import { getOperationParameterName } from "./getOperationParameterName"
|
||||
import { getRef } from "./getRef"
|
||||
import { getType } from "./getType"
|
||||
|
||||
export const getOperationParameter = (
|
||||
openApi: OpenApi,
|
||||
parameter: OpenApiParameter
|
||||
): OperationParameter => {
|
||||
const operationParameter: OperationParameter = {
|
||||
spec: parameter,
|
||||
in: parameter.in,
|
||||
prop: parameter.name,
|
||||
export: "interface",
|
||||
name: getOperationParameterName(parameter.name),
|
||||
type: "any",
|
||||
base: "any",
|
||||
template: null,
|
||||
link: null,
|
||||
description: parameter.description || null,
|
||||
deprecated: parameter.deprecated === true,
|
||||
isDefinition: false,
|
||||
isReadOnly: false,
|
||||
isRequired: parameter.required === true,
|
||||
isNullable: parameter.nullable === true,
|
||||
imports: [],
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
mediaType: null,
|
||||
}
|
||||
|
||||
if (parameter.$ref) {
|
||||
const definitionRef = getType(parameter.$ref)
|
||||
operationParameter.export = "reference"
|
||||
operationParameter.type = definitionRef.type
|
||||
operationParameter.base = definitionRef.base
|
||||
operationParameter.template = definitionRef.template
|
||||
operationParameter.imports.push(...definitionRef.imports)
|
||||
return operationParameter
|
||||
}
|
||||
|
||||
let schema = parameter.schema
|
||||
if (schema) {
|
||||
if (schema.$ref?.startsWith("#/components/parameters/")) {
|
||||
schema = getRef<OpenApiSchema>(openApi, schema)
|
||||
}
|
||||
if (schema.$ref) {
|
||||
const model = getType(schema.$ref)
|
||||
operationParameter.export = "reference"
|
||||
operationParameter.type = model.type
|
||||
operationParameter.base = model.base
|
||||
operationParameter.template = model.template
|
||||
operationParameter.imports.push(...model.imports)
|
||||
operationParameter.default = getModelDefault(schema)
|
||||
return operationParameter
|
||||
} else {
|
||||
const model = getModel(openApi, schema)
|
||||
operationParameter.export = model.export
|
||||
operationParameter.type = model.type
|
||||
operationParameter.base = model.base
|
||||
operationParameter.template = model.template
|
||||
operationParameter.link = model.link
|
||||
operationParameter.isReadOnly = model.isReadOnly
|
||||
operationParameter.isRequired =
|
||||
operationParameter.isRequired || model.isRequired
|
||||
operationParameter.isNullable =
|
||||
operationParameter.isNullable || model.isNullable
|
||||
operationParameter.format = model.format
|
||||
operationParameter.maximum = model.maximum
|
||||
operationParameter.exclusiveMaximum = model.exclusiveMaximum
|
||||
operationParameter.minimum = model.minimum
|
||||
operationParameter.exclusiveMinimum = model.exclusiveMinimum
|
||||
operationParameter.multipleOf = model.multipleOf
|
||||
operationParameter.maxLength = model.maxLength
|
||||
operationParameter.minLength = model.minLength
|
||||
operationParameter.maxItems = model.maxItems
|
||||
operationParameter.minItems = model.minItems
|
||||
operationParameter.uniqueItems = model.uniqueItems
|
||||
operationParameter.maxProperties = model.maxProperties
|
||||
operationParameter.minProperties = model.minProperties
|
||||
operationParameter.pattern = getPattern(model.pattern)
|
||||
operationParameter.default = model.default
|
||||
operationParameter.imports.push(...model.imports)
|
||||
operationParameter.enum.push(...model.enum)
|
||||
operationParameter.enums.push(...model.enums)
|
||||
operationParameter.properties.push(...model.properties)
|
||||
return operationParameter
|
||||
}
|
||||
}
|
||||
|
||||
return operationParameter
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import camelCase from "camelcase"
|
||||
|
||||
export const reservedWords =
|
||||
/^(arguments|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|eval|export|extends|false|finally|for|function|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)$/g
|
||||
|
||||
/**
|
||||
* Replaces any invalid characters from a parameter name.
|
||||
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
|
||||
*/
|
||||
export const getOperationParameterName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, "")
|
||||
.replace(/[^\w\-]+/g, "-")
|
||||
.trim()
|
||||
return camelCase(clean).replace(reservedWords, "_$1")
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import type { OperationParameters } from "../../../client/interfaces/OperationParameters"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiParameter } from "../interfaces/OpenApiParameter"
|
||||
import { getOperationParameter } from "./getOperationParameter"
|
||||
import { getRef } from "./getRef"
|
||||
|
||||
export const getOperationParameters = (
|
||||
openApi: OpenApi,
|
||||
parameters: OpenApiParameter[]
|
||||
): OperationParameters => {
|
||||
const operationParameters: OperationParameters = {
|
||||
imports: [],
|
||||
parameters: [],
|
||||
parametersPath: [],
|
||||
parametersQuery: [],
|
||||
parametersForm: [],
|
||||
parametersCookie: [],
|
||||
parametersHeader: [],
|
||||
parametersBody: null, // Not used in V3 -> @see requestBody
|
||||
}
|
||||
|
||||
// Iterate over the parameters
|
||||
parameters.forEach((parameterOrReference) => {
|
||||
const parameterDef = getRef<OpenApiParameter>(openApi, parameterOrReference)
|
||||
const parameter = getOperationParameter(openApi, parameterDef)
|
||||
|
||||
// We ignore the "api-version" param, since we do not want to add this
|
||||
// as the first / default parameter for each of the service calls.
|
||||
if (parameter.prop !== "api-version") {
|
||||
switch (parameterDef.in) {
|
||||
case "path":
|
||||
operationParameters.parametersPath.push(parameter)
|
||||
operationParameters.parameters.push(parameter)
|
||||
operationParameters.imports.push(...parameter.imports)
|
||||
break
|
||||
|
||||
case "query":
|
||||
operationParameters.parametersQuery.push(parameter)
|
||||
operationParameters.parameters.push(parameter)
|
||||
operationParameters.imports.push(...parameter.imports)
|
||||
break
|
||||
|
||||
case "formData":
|
||||
operationParameters.parametersForm.push(parameter)
|
||||
operationParameters.parameters.push(parameter)
|
||||
operationParameters.imports.push(...parameter.imports)
|
||||
break
|
||||
|
||||
case "cookie":
|
||||
operationParameters.parametersCookie.push(parameter)
|
||||
operationParameters.parameters.push(parameter)
|
||||
operationParameters.imports.push(...parameter.imports)
|
||||
break
|
||||
|
||||
case "header":
|
||||
operationParameters.parametersHeader.push(parameter)
|
||||
operationParameters.parameters.push(parameter)
|
||||
operationParameters.imports.push(...parameter.imports)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
return operationParameters
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import type { OperationParameter } from "../../../client/interfaces/OperationParameter"
|
||||
import { getPattern } from "../../../utils/getPattern"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiRequestBody } from "../interfaces/OpenApiRequestBody"
|
||||
import { getContent } from "./getContent"
|
||||
import { getModel } from "./getModel"
|
||||
import { getType } from "./getType"
|
||||
|
||||
export const getOperationRequestBody = (
|
||||
openApi: OpenApi,
|
||||
body: OpenApiRequestBody
|
||||
): OperationParameter => {
|
||||
const requestBody: OperationParameter = {
|
||||
spec: body,
|
||||
in: "body",
|
||||
export: "interface",
|
||||
prop: "requestBody",
|
||||
name: "requestBody",
|
||||
type: "any",
|
||||
base: "any",
|
||||
template: null,
|
||||
link: null,
|
||||
description: body.description || null,
|
||||
default: undefined,
|
||||
isDefinition: false,
|
||||
isReadOnly: false,
|
||||
isRequired: body.required === true,
|
||||
isNullable: body.nullable === true,
|
||||
imports: [],
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
mediaType: null,
|
||||
}
|
||||
|
||||
if (body.content) {
|
||||
const content = getContent(openApi, body.content)
|
||||
if (content) {
|
||||
requestBody.mediaType = content.mediaType
|
||||
switch (requestBody.mediaType) {
|
||||
case "application/x-www-form-urlencoded":
|
||||
case "multipart/form-data":
|
||||
requestBody.in = "formData"
|
||||
requestBody.name = "formData"
|
||||
requestBody.prop = "formData"
|
||||
break
|
||||
}
|
||||
if (content.schema.$ref) {
|
||||
const model = getType(content.schema.$ref)
|
||||
requestBody.export = "reference"
|
||||
requestBody.type = model.type
|
||||
requestBody.base = model.base
|
||||
requestBody.template = model.template
|
||||
requestBody.imports.push(...model.imports)
|
||||
return requestBody
|
||||
} else {
|
||||
const model = getModel(openApi, content.schema)
|
||||
requestBody.export = model.export
|
||||
requestBody.type = model.type
|
||||
requestBody.base = model.base
|
||||
requestBody.template = model.template
|
||||
requestBody.link = model.link
|
||||
requestBody.isReadOnly = model.isReadOnly
|
||||
requestBody.isRequired = requestBody.isRequired || model.isRequired
|
||||
requestBody.isNullable = requestBody.isNullable || model.isNullable
|
||||
requestBody.format = model.format
|
||||
requestBody.maximum = model.maximum
|
||||
requestBody.exclusiveMaximum = model.exclusiveMaximum
|
||||
requestBody.minimum = model.minimum
|
||||
requestBody.exclusiveMinimum = model.exclusiveMinimum
|
||||
requestBody.multipleOf = model.multipleOf
|
||||
requestBody.maxLength = model.maxLength
|
||||
requestBody.minLength = model.minLength
|
||||
requestBody.maxItems = model.maxItems
|
||||
requestBody.minItems = model.minItems
|
||||
requestBody.uniqueItems = model.uniqueItems
|
||||
requestBody.maxProperties = model.maxProperties
|
||||
requestBody.minProperties = model.minProperties
|
||||
requestBody.pattern = getPattern(model.pattern)
|
||||
requestBody.imports.push(...model.imports)
|
||||
requestBody.enum.push(...model.enum)
|
||||
requestBody.enums.push(...model.enums)
|
||||
requestBody.properties.push(...model.properties)
|
||||
return requestBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return requestBody
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import type { OperationResponse } from "../../../client/interfaces/OperationResponse"
|
||||
import { getPattern } from "../../../utils/getPattern"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiResponse } from "../interfaces/OpenApiResponse"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
import { getContent } from "./getContent"
|
||||
import { getModel } from "./getModel"
|
||||
import { getRef } from "./getRef"
|
||||
import { getType } from "./getType"
|
||||
|
||||
export const getOperationResponse = (
|
||||
openApi: OpenApi,
|
||||
response: OpenApiResponse,
|
||||
responseCode: number
|
||||
): OperationResponse => {
|
||||
const operationResponse: OperationResponse = {
|
||||
spec: response,
|
||||
in: "response",
|
||||
name: "",
|
||||
code: responseCode,
|
||||
description: response.description || null,
|
||||
export: "generic",
|
||||
type: "any",
|
||||
base: "any",
|
||||
template: null,
|
||||
link: null,
|
||||
isDefinition: false,
|
||||
isReadOnly: false,
|
||||
isRequired: false,
|
||||
isNullable: false,
|
||||
imports: [],
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
}
|
||||
|
||||
if (response.content) {
|
||||
const content = getContent(openApi, response.content)
|
||||
if (content) {
|
||||
if (content.schema.$ref?.startsWith("#/components/responses/")) {
|
||||
content.schema = getRef<OpenApiSchema>(openApi, content.schema)
|
||||
}
|
||||
if (content.schema.$ref) {
|
||||
const model = getType(content.schema.$ref)
|
||||
operationResponse.export = "reference"
|
||||
operationResponse.type = model.type
|
||||
operationResponse.base = model.base
|
||||
operationResponse.template = model.template
|
||||
operationResponse.imports.push(...model.imports)
|
||||
return operationResponse
|
||||
} else {
|
||||
const model = getModel(openApi, content.schema)
|
||||
operationResponse.export = model.export
|
||||
operationResponse.type = model.type
|
||||
operationResponse.base = model.base
|
||||
operationResponse.template = model.template
|
||||
operationResponse.link = model.link
|
||||
operationResponse.isReadOnly = model.isReadOnly
|
||||
operationResponse.isRequired = model.isRequired
|
||||
operationResponse.isNullable = model.isNullable
|
||||
operationResponse.format = model.format
|
||||
operationResponse.maximum = model.maximum
|
||||
operationResponse.exclusiveMaximum = model.exclusiveMaximum
|
||||
operationResponse.minimum = model.minimum
|
||||
operationResponse.exclusiveMinimum = model.exclusiveMinimum
|
||||
operationResponse.multipleOf = model.multipleOf
|
||||
operationResponse.maxLength = model.maxLength
|
||||
operationResponse.minLength = model.minLength
|
||||
operationResponse.maxItems = model.maxItems
|
||||
operationResponse.minItems = model.minItems
|
||||
operationResponse.uniqueItems = model.uniqueItems
|
||||
operationResponse.maxProperties = model.maxProperties
|
||||
operationResponse.minProperties = model.minProperties
|
||||
operationResponse.pattern = getPattern(model.pattern)
|
||||
operationResponse.imports.push(...model.imports)
|
||||
operationResponse.enum.push(...model.enum)
|
||||
operationResponse.enums.push(...model.enums)
|
||||
operationResponse.properties.push(...model.properties)
|
||||
return operationResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We support basic properties from response headers, since both
|
||||
// fetch and XHR client just support string types.
|
||||
if (response.headers) {
|
||||
for (const name in response.headers) {
|
||||
if (response.headers.hasOwnProperty(name)) {
|
||||
operationResponse.in = "header"
|
||||
operationResponse.name = name
|
||||
operationResponse.type = "string"
|
||||
operationResponse.base = "string"
|
||||
return operationResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operationResponse
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
export const getOperationResponseCode = (
|
||||
value: string | "default"
|
||||
): number | null => {
|
||||
// You can specify a "default" response, this is treated as HTTP code 200
|
||||
if (value === "default") {
|
||||
return 200
|
||||
}
|
||||
|
||||
// Check if we can parse the code and return of successful.
|
||||
if (/[0-9]+/g.test(value)) {
|
||||
const code = parseInt(value)
|
||||
if (Number.isInteger(code)) {
|
||||
return Math.abs(code)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { OperationResponse } from "../../../client/interfaces/OperationResponse"
|
||||
|
||||
export const getOperationResponseHeader = (
|
||||
operationResponses: OperationResponse[]
|
||||
): string | null => {
|
||||
const header = operationResponses.find((operationResponses) => {
|
||||
return operationResponses.in === "header"
|
||||
})
|
||||
if (header) {
|
||||
return header.name
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import type { OperationResponse } from "../../../client/interfaces/OperationResponse"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiResponse } from "../interfaces/OpenApiResponse"
|
||||
import type { OpenApiResponses } from "../interfaces/OpenApiResponses"
|
||||
import { getOperationResponse } from "./getOperationResponse"
|
||||
import { getOperationResponseCode } from "./getOperationResponseCode"
|
||||
import { getRef } from "./getRef"
|
||||
|
||||
export const getOperationResponses = (
|
||||
openApi: OpenApi,
|
||||
responses: OpenApiResponses
|
||||
): OperationResponse[] => {
|
||||
const operationResponses: OperationResponse[] = []
|
||||
|
||||
// Iterate over each response code and get the
|
||||
// status code and response message (if any).
|
||||
for (const code in responses) {
|
||||
if (responses.hasOwnProperty(code)) {
|
||||
const responseOrReference = responses[code]
|
||||
const response = getRef<OpenApiResponse>(openApi, responseOrReference)
|
||||
const responseCode = getOperationResponseCode(code)
|
||||
|
||||
if (responseCode) {
|
||||
const operationResponse = getOperationResponse(
|
||||
openApi,
|
||||
response,
|
||||
responseCode
|
||||
)
|
||||
operationResponses.push(operationResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the responses to 2XX success codes come before 4XX and 5XX error codes.
|
||||
return operationResponses.sort((a, b): number => {
|
||||
return a.code < b.code ? -1 : a.code > b.code ? 1 : 0
|
||||
})
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import type { Model } from "../../../client/interfaces/Model"
|
||||
import type { OperationResponse } from "../../../client/interfaces/OperationResponse"
|
||||
|
||||
const areEqual = (a: Model, b: Model): boolean => {
|
||||
const equal =
|
||||
a.type === b.type && a.base === b.base && a.template === b.template
|
||||
if (equal && a.link && b.link) {
|
||||
return areEqual(a.link, b.link)
|
||||
}
|
||||
return equal
|
||||
}
|
||||
|
||||
export const getOperationResults = (
|
||||
operationResponses: OperationResponse[]
|
||||
): OperationResponse[] => {
|
||||
const operationResults: OperationResponse[] = []
|
||||
|
||||
// Filter out success response codes, but skip "204 No Content"
|
||||
operationResponses.forEach((operationResponse) => {
|
||||
const { code } = operationResponse
|
||||
if (code && code !== 204 && code >= 200 && code < 300) {
|
||||
operationResults.push(operationResponse)
|
||||
}
|
||||
})
|
||||
|
||||
if (!operationResults.length) {
|
||||
operationResults.push({
|
||||
spec: { description: "" },
|
||||
in: "response",
|
||||
name: "",
|
||||
code: 200,
|
||||
description: "",
|
||||
export: "generic",
|
||||
type: "void",
|
||||
base: "void",
|
||||
template: null,
|
||||
link: null,
|
||||
isDefinition: false,
|
||||
isReadOnly: false,
|
||||
isRequired: false,
|
||||
isNullable: false,
|
||||
imports: [],
|
||||
enum: [],
|
||||
enums: [],
|
||||
properties: [],
|
||||
})
|
||||
}
|
||||
|
||||
return operationResults.filter((operationResult, index, arr) => {
|
||||
return (
|
||||
arr.findIndex((item) => {
|
||||
return areEqual(item, operationResult)
|
||||
}) === index
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiReference } from "../interfaces/OpenApiReference"
|
||||
|
||||
const ESCAPED_REF_SLASH = /~1/g
|
||||
const ESCAPED_REF_TILDE = /~0/g
|
||||
|
||||
export const getRef = <T>(openApi: OpenApi, item: T & OpenApiReference): T => {
|
||||
if (item.$ref) {
|
||||
// Fetch the paths to the definitions, this converts:
|
||||
// "#/components/schemas/Form" to ["components", "schemas", "Form"]
|
||||
const paths = item.$ref
|
||||
.replace(/^#/g, "")
|
||||
.split("/")
|
||||
.filter((item) => item)
|
||||
|
||||
// Try to find the reference by walking down the path,
|
||||
// if we cannot find it, then we throw an error.
|
||||
let result: any = openApi
|
||||
paths.forEach((path) => {
|
||||
const decodedPath = decodeURIComponent(
|
||||
path.replace(ESCAPED_REF_SLASH, "/").replace(ESCAPED_REF_TILDE, "~")
|
||||
)
|
||||
if (result.hasOwnProperty(decodedPath)) {
|
||||
result = result[decodedPath]
|
||||
} else {
|
||||
throw new Error(`Could not find reference: "${item.$ref}"`)
|
||||
}
|
||||
})
|
||||
return result as T
|
||||
}
|
||||
return item as T
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import type { Model } from "../../../client/interfaces/Model"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import type { OpenApiSchema } from "../interfaces/OpenApiSchema"
|
||||
import type { getModel } from "./getModel"
|
||||
import { getRef } from "./getRef"
|
||||
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel
|
||||
|
||||
export const getRequiredPropertiesFromComposition = (
|
||||
openApi: OpenApi,
|
||||
required: string[],
|
||||
definitions: OpenApiSchema[],
|
||||
getModel: GetModelFn
|
||||
): Model[] => {
|
||||
return definitions
|
||||
.reduce((properties, definition) => {
|
||||
if (definition.$ref) {
|
||||
const schema = getRef<OpenApiSchema>(openApi, definition)
|
||||
return [...properties, ...getModel(openApi, schema).properties]
|
||||
}
|
||||
return [...properties, ...getModel(openApi, definition).properties]
|
||||
}, [] as Model[])
|
||||
.filter((property) => {
|
||||
return !property.isRequired && required.includes(property.name)
|
||||
})
|
||||
.map((property) => {
|
||||
return {
|
||||
...property,
|
||||
isRequired: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
|
||||
export const getServer = (openApi: OpenApi): string => {
|
||||
const server = openApi.servers?.[0]
|
||||
const variables = server?.variables || {}
|
||||
let url = server?.url || ""
|
||||
for (const variable in variables) {
|
||||
if (variables.hasOwnProperty(variable)) {
|
||||
url = url.replace(`{${variable}}`, variables[variable].default)
|
||||
}
|
||||
}
|
||||
return url.replace(/\/$/g, "")
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import camelCase from "camelcase"
|
||||
|
||||
/**
|
||||
* Convert the input value to a correct service name. This converts
|
||||
* the input string to PascalCase.
|
||||
*/
|
||||
export const getServiceName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, "")
|
||||
.replace(/[^\w\-]+/g, "-")
|
||||
.trim()
|
||||
return camelCase(clean, { pascalCase: true })
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Convert the service version to 'normal' version.
|
||||
* This basically removes any "v" prefix from the version string.
|
||||
* @param version
|
||||
*/
|
||||
export const getServiceVersion = (version = "1.0"): string => {
|
||||
return String(version).replace(/^v/gi, "")
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { Service } from "../../../client/interfaces/Service"
|
||||
import type { OpenApi } from "../interfaces/OpenApi"
|
||||
import { listOperations } from "./listOperations"
|
||||
|
||||
/**
|
||||
* Get the OpenAPI services
|
||||
*/
|
||||
export const getServices = (openApi: OpenApi): Service[] => {
|
||||
const services = new Map<string, Service>()
|
||||
const operations = listOperations(openApi)
|
||||
for (const operation of operations) {
|
||||
// If we have already declared a service, then we should fetch that and
|
||||
// append the new method to it. Otherwise we should create a new service object.
|
||||
const service: Service = services.get(operation.service) || {
|
||||
name: operation.service,
|
||||
operations: [],
|
||||
imports: [],
|
||||
}
|
||||
|
||||
// Push the operation in the service
|
||||
service.operations.push(operation)
|
||||
service.imports.push(...operation.imports)
|
||||
services.set(operation.service, service)
|
||||
}
|
||||
return Array.from(services.values())
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user