Skip to content
This repository has been archived by the owner on Jun 14, 2021. It is now read-only.

Commit

Permalink
feat: stub generator code (#43)
Browse files Browse the repository at this point in the history
* feat: stub generator code

* fix: use operations on path

* feat: add more ResourcesGenerator code stubs

* docs: add comment

* docs: adds more documentation
  • Loading branch information
aimed authored and gribeiro-freighthub committed Jun 6, 2019
1 parent 06d5985 commit 73f7556
Showing 1 changed file with 134 additions and 10 deletions.
144 changes: 134 additions & 10 deletions codegen/src/generators/ResourcesGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,155 @@ import { isErrorStatusCode, isStatusCodeRange, StatusCodeRange, StatusCode, stat
import { capitalize, isReferenceObject } from '../typescript/module/utils';
import { StatusCodeClassNames } from '../StatusCodesClassNames';
import { TSClassBuilder } from '../typescript/TSClassBuilder';
import { groupBy } from 'lodash';
import * as path from 'path'

export class ResourcesGenerator implements Generator {
dependsOn = [ComponentSchemaTypesGenerator]

generate(_document: OpenAPIV3.Document, _tsModule: TSModule): Promise<void> {
async generate(document: OpenAPIV3.Document, tsModule: TSModule): Promise<void> {
// TODO: This replaces ResourceTemplate
// TODO: Create Responses (done)
// TODO: Create Parameters ()
// TODO: Create ResourceInterface
// TODO: Create Resource ()
// TODO: Create ResourceRouter ()

// const responseTypeFactory = new ResponseTypeFactory()
throw new Error("Method not implemented.");
// throw new Error("Method not implemented.")
const resourceRouterNames: string[] = []

const pathsWithResourceName = Object.entries(document.paths).map(([path, pathItemObject]) => ({ path, pathItemObject, resourceName: this.getResourceNameForPath(path) }))
const groupedPathsWithResourceName = groupBy(pathsWithResourceName, item => item.resourceName)

// For every resource, e.g. /pet create the response type, the parameter type and the resource router.
for (const [resourceName, resourcePathDescriptions] of Object.entries(groupedPathsWithResourceName)) {
const tsFile = tsModule.file(path.join('resources', `${capitalize(resourceName)}.ts`))
const resourceOperations: ResourceOperation[] = []

// For every path on a resource
for (const resourcePathDescription of resourcePathDescriptions) {
const { path, pathItemObject } = resourcePathDescription

// For every operation on a resource, e.g. getPet (GET /pets)
for (const pathOperationKey of httpVerbPathOperations) {
const operationObject = pathItemObject[pathOperationKey]
if (!operationObject) {
continue
}

const responseTypeFactory = new ResponseTypeFactory()
const responseType = responseTypeFactory.declarePathResponseType(operationObject, tsFile)

const parameterTypeFactory = new ParameterTypeFactory()
const parameterType = parameterTypeFactory.declareParameterType(operationObject, tsFile)
resourceOperations.push({ path, responseType, parameterType, pathItemObject, method: pathOperationKey })
}

}

const resourceRouterFactory = new ResourceRouterFactory()
const resourceRouterName = resourceRouterFactory.declareResourceRouter(resourceName, resourceOperations, tsFile)
resourceRouterNames.push(resourceRouterName)
}

// TODO: Do things with resourceRouterNames -> Generate the ResourcesConfiguration
}

/**
* For a given path create a resource name.
* @example
* / -> Index
* /pets -> Pets
* /pets/:id -> Pets
*/
private getResourceNameForPath(path: string): string {
const [, prefix] = path.split('/')
if (!prefix || prefix.startsWith('{')) {
return capitalize('index')
}
return capitalize(prefix)
}
}

/**
* A list of all http verbs that are supported by the OpenApi path.
*/
const httpVerbPathOperations = [
'get' as 'get',
'put' as 'put',
'post' as 'post',
'delete' as 'delete',
'options' as 'options',
'head' as 'head',
'patch' as 'patch',
'trace' as 'trace',
]

export interface ResourceOperation {
parameterType: string
responseType: string
path: string
method: string
pathItemObject: OpenAPIV3.PathItemObject
}

/**
* Creates a resource router (resource definition).
* @example
* export class PetsResourceRouter {
* bind(router: SlushyRouter, resource: PetsResource) {
* router.get('/pets', resource.getPets)
* }
* }
*/
export class ResourceRouterFactory {
declareResourceRouter(_resourceName: string, _resourceOperations: Array<ResourceOperation>, _tsFile: TSFile): string {
throw new Error("Method not implemented.")
// TODO: return the resource router class name
}
}

/**
* Creates a resource interface.
* @example
* export interface PetsResource {
* getPetById(params: GetPetByIdParams): Promise<GetPetByIdResponse>
* }
*/
export class ResourceFactory {
}

/**
* Creates a resource operation parameter.
* @example
* export type GetPetByIdParams = { petId: string }
*/
export class ParameterTypeFactory {
declareParameterType(
_operationObject: OpenAPIV3.OperationObject,
_tsFile: TSFile,
): string {
throw new Error("Method not implemented.")
// TODO: return the paramter type name
}
}

/**
* Creates a resource operation response.
* @example
* export class GetPetOK { status = 200 }
* export class GetPetBadRequest extends SlushyError { status = 400 }
* export type GetPetResponse = GetPetOK | GetPetBadRequest
*/
export class ResponseTypeFactory {
declarePathResponseType(
pathItemObject: OpenAPIV3.OperationObject,
operationObject: OpenAPIV3.OperationObject,
tsFile: TSFile,
): string {
if (!pathItemObject.responses) {
if (!operationObject.responses) {
throw new Error('Missing responses')
}

if (!pathItemObject.operationId) {
if (!operationObject.operationId) {
throw new Error('Missing operationId')
}

Expand All @@ -41,15 +165,15 @@ export class ResponseTypeFactory {
const responseClassNames: string[] = []

// Generate one class for every possible response value
for (const [responseStatusCodeString, response] of Object.entries(pathItemObject.responses)) {
for (const [responseStatusCodeString, response] of Object.entries(operationObject.responses)) {
const responseStatusCode = responseStatusCodeString as keyof typeof StatusCodeRange | StatusCode
const responseClassSuffix = StatusCodeClassNames[responseStatusCode];
const responseClassName = `${capitalize(pathItemObject.operationId)}${responseClassSuffix}`;
const responseClassName = `${capitalize(operationObject.operationId)}${responseClassSuffix}`;
responseClassNames.push(responseClassName)
this.declareStatusCodeResponseClass(responseClassName, response, responseStatusCode, tsFile);
}

const responseTypeName = `${capitalize(pathItemObject.operationId)}Response`
const responseTypeName = `${capitalize(operationObject.operationId)}Response`
const responseTypeDefinition = `export type ${responseTypeName} = ${responseClassNames.join(' | ')}`
tsFile.addSourceText(responseTypeDefinition)
return responseTypeName
Expand Down

0 comments on commit 73f7556

Please sign in to comment.