Skip to content

Commit

Permalink
feat: work with for mock data
Browse files Browse the repository at this point in the history
  • Loading branch information
greeeg committed Dec 24, 2023
1 parent fc1974f commit 558e385
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 34 deletions.
10 changes: 1 addition & 9 deletions packages/openapi-kit/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import yargs from 'yargs'
import { hideBin } from 'yargs/helpers'

import {
dereferenceDocument,
generateAPIClient,
generateMockData,
generateReactQueryHooks,
Expand All @@ -27,13 +26,6 @@ const run = async ({ openAPIFilePath, outputDirectoryPath }: RunOptions) => {
)
return
}
const dereferencedDocument = await dereferenceDocument(filePath)
if (!dereferencedDocument) {
console.log(
`OpenAPI document at "${filePath}" could not be parsed. Make sure it exists & is valid.`,
)
return
}

const typeDefinitionsFileName = 'typeDefinitions.ts'
const typeDefinitionsImportPath = `./${typeDefinitionsFileName.replace(
Expand Down Expand Up @@ -70,7 +62,7 @@ const run = async ({ openAPIFilePath, outputDirectoryPath }: RunOptions) => {
outputPath: apiClientOutputPath,
typeDefinitionsImportPath,
})
generateMockData(dereferencedDocument, {
generateMockData(document, {
outputPath: mockOutputPath,
typeDefinitionsImportPath,
})
Expand Down
1 change: 1 addition & 0 deletions packages/openapi-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"keywords": ["openapi", "typegen", "typescript", "faker", "swagger"],
"homepage": "https://github.com/greeeg/openapi-kit",
"repository": {
"type": "git",
Expand Down
49 changes: 35 additions & 14 deletions packages/openapi-kit/src/generators/mockData/functions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { faker } from '@faker-js/faker'

import { OpenAPISchemaObject } from '../../types'
import {
OpenAPIDocument,
OpenAPIRefObject,
OpenAPISchemaObject,
} from '../../types'
import {
isArraySchemaObject,
isNonArraySchemaObject,
isRefObject,
isSchemaObject,
resolveRef,
} from '../../utils/openAPI'

const getExampleOrFallback = (
Expand Down Expand Up @@ -80,24 +85,38 @@ const randomString = (schema: OpenAPISchemaObject) => {
})
}

export const generateMock = (schema: OpenAPISchemaObject): unknown => {
let object: unknown = {}
export const generateMock = (
currentSchema: OpenAPISchemaObject | OpenAPIRefObject | undefined,
document: OpenAPIDocument,
): unknown => {
const schema = resolveRef(currentSchema, document)
if (!schema) {
if (currentSchema && isRefObject(currentSchema)) {
console.error(`Could not resolve $ref: ${currentSchema.$ref}`)
}
return
}

let object: unknown = {}
if (isArraySchemaObject(schema)) {
const itemShape = schema.items
// @ts-expect-error
if (!isSchemaObject(itemShape)) {
return null
const resolvedItemShape = resolveRef(itemShape, document)

if (!resolvedItemShape) {
if (isRefObject(itemShape)) {
console.error(`Could not resolve $ref: ${itemShape.$ref}`)
}
return
}

return Array.from({ length: randomArrayItemsCount() }).map(() =>
generateMock(itemShape),
generateMock(itemShape, document),
)
}

// TODO: What to do with this?
if (!isNonArraySchemaObject(schema)) {
return null
return
}

if ('nullable' in schema && schema.nullable) {
Expand All @@ -110,21 +129,21 @@ export const generateMock = (schema: OpenAPISchemaObject): unknown => {
const validOptions = schema.anyOf.filter(
(option) => !isRefObject(option),
) as OpenAPISchemaObject[]
return generateMock(randomElementFromArray(validOptions))
return generateMock(randomElementFromArray(validOptions), document)
}

if (schema.oneOf) {
const validOptions = schema.oneOf.filter(
(option) => !isRefObject(option),
) as OpenAPISchemaObject[]
return generateMock(randomElementFromArray(validOptions))
return generateMock(randomElementFromArray(validOptions), document)
}

if (schema.allOf) {
const validOptions = schema.allOf.filter(
(option) => !isRefObject(option),
) as OpenAPISchemaObject[]
return validOptions.map((option) => generateMock(option))
return validOptions.map((option) => generateMock(option, document))
}

switch (schema.type) {
Expand All @@ -151,12 +170,14 @@ export const generateMock = (schema: OpenAPISchemaObject): unknown => {
for (const [propertyName, propertyValue] of Object.entries(
schema.properties,
)) {
if (!isSchemaObject(propertyValue)) {
if (!propertyValue || !isSchemaObject(propertyValue)) {
continue
}

;(object as Record<string, unknown>)[propertyName] =
generateMock(propertyValue)
;(object as Record<string, unknown>)[propertyName] = generateMock(
propertyValue,
document,
)
}
}
break
Expand Down
14 changes: 5 additions & 9 deletions packages/openapi-kit/src/generators/mockData/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ import { capitalize } from 'lodash'
import { OpenAPIDocument } from '../../types'
import { writeFile } from '../../utils/fileSystem'
import { formatOutput } from '../../utils/format'
import {
getOperations,
isResponseObject,
isSchemaObject,
} from '../../utils/openAPI'
import { getOperations, isResponseObject } from '../../utils/openAPI'
import { toTypeName, toValidIdentifier } from '../../utils/typescript'
import { generateMock } from './functions'
import { MockDataGeneratorOptions } from './types'
Expand All @@ -21,16 +17,16 @@ export const generateMockData = async (
`import { Paths } from "${typeDefinitionsImportPath}";`,
]

operations.forEach(({ operation, pascalCaseOperationId, httpMethod }) => {
operations.forEach(({ operation, pascalCaseOperationId }) => {
const responsesToMock = Object.entries(operation.responses)

for (const [name, response] of responsesToMock) {
if (!isResponseObject(response) || !response.content) {
return
}

const schema = response.content['application/json'].schema
if (!schema || !isSchemaObject(schema)) {
const schema = response.content['application/json']?.schema
if (!schema) {
return
}

Expand All @@ -44,7 +40,7 @@ export const generateMockData = async (

lines.push(
`export const ${mockName}: ${type} = ${JSON.stringify(
generateMock(schema),
generateMock(schema, document),
)}`,
)
}
Expand Down
3 changes: 3 additions & 0 deletions packages/openapi-kit/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export type OpenAPIDocument = OpenAPIV3.Document | OpenAPIV3_1.Document
export type OpenAPISchemaObject =
| OpenAPIV3.SchemaObject
| OpenAPIV3_1.SchemaObject
export type OpenAPIRefObject =
| OpenAPIV3.ReferenceObject
| OpenAPIV3_1.ReferenceObject
export type OpenAPINonArraySchemaObject =
| OpenAPIV3.NonArraySchemaObject
| OpenAPIV3_1.NonArraySchemaObject
Expand Down
31 changes: 29 additions & 2 deletions packages/openapi-kit/src/utils/openAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {
OpenAPIArraySchemaObject,
OpenAPIDocument,
OpenAPINonArraySchemaObject,
OpenAPIRefObject,
OpenAPISchemaObject,
} from '../types'
import { fileExists } from './fileSystem'
import { toTypeName, toValidIdentifier } from './typescript'

export const isSchemaObject = (
response: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject,
): response is OpenAPIV3.SchemaObject => {
response: OpenAPIRefObject | OpenAPISchemaObject,
): response is OpenAPISchemaObject => {
return !('$ref' in response)
}

Expand Down Expand Up @@ -185,3 +186,29 @@ export const getResponseType = ({
firstResponseName,
)}`
}

export const resolveRef = (
refObject: OpenAPIRefObject | OpenAPISchemaObject | undefined,
rootSchema: OpenAPIDocument,
): OpenAPISchemaObject | null => {
if (!refObject) {
return null
}

if (isSchemaObject(refObject)) {
return refObject
}

const path = refObject.$ref.replace('#/', '').split('/')
let current: any = rootSchema

for (let part of path) {
if (!(part in current)) {
return null
}

current = current[part]
}

return current
}

0 comments on commit 558e385

Please sign in to comment.