Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add elixir tesla generator #53

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const NEVER_DEFINITION = {
name: undefined,
definition: 'never',
};

export const UNKNOWN_DEFINITION = {
name: undefined,
definition: 'unknown',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { OpenAPIV3ReferenceableSchemaObject, OperationObject, PathItemObject, PathItemParameters } from '../../types';
import { OpenAPIV3 } from 'openapi-types';
import { isReferenceObject } from '../../helpers';

export function getResponseSchema(operation: OperationObject): OpenAPIV3ReferenceableSchemaObject | undefined {
const schemas = Object.entries(operation.responses ?? {})
.map(([status, response]) => {
const statusCode = parseInt(status, 10);

if (isNaN(statusCode) || statusCode < 200 || statusCode > 299) {
return;
}

// @ts-ignore Handle $ref content
return response.content?.['application/json'].schema;
})
.filter(Boolean);

return schemas.length === 0
? undefined
: {
oneOf: schemas,
};
}

export function getRequestBodySchema(args: {
operation: OperationObject;
}): OpenAPIV3ReferenceableSchemaObject | undefined {
if (!args.operation.requestBody) {
return;
}

if (isReferenceObject(args.operation.requestBody)) {
return args.operation.requestBody;
}

return {
description: args.operation.requestBody.description,
...args.operation.requestBody.content?.['application/json']?.schema,
};
}

export function getParameterSchemaFor(args: {
pathItem: PathItemObject;
operation: OperationObject;
inName: 'path' | 'query';
}): OpenAPIV3.NonArraySchemaObject | undefined {
const pathParameters = ([] as PathItemParameters)
.concat(args.pathItem.parameters ?? [])
.concat(args.operation.parameters ?? [])
.filter((parameter) => {
// @ts-ignore Handle $ref
return parameter.in === args.inName;
});

if (pathParameters.length === 0) {
return;
}

return pathParameters.reduce(
(schema, parameter) => {
// TODO: Follow up. Technically parameters are never a $ref based on the spec.
const param = parameter as OpenAPIV3.ParameterObject;

if (param.required) {
schema.required!.push(param.name);
}

schema.properties[param.name] = {
description: param.description,
...param.schema,
};

return schema;
},
{
type: 'object',
properties: {},
required: [],
} as Required<Pick<OpenAPIV3.NonArraySchemaObject, 'type' | 'properties' | 'required'>>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { forEachHttpOperation } from '../../helpers';
import { CodegenBase } from '../../codegen-base';
import { snakeCase } from 'change-case';
import { OperationObject, PathItemObject } from '../../types';
import { OutputDir } from '../../output-dir';

export interface ElixirTeslaCodegenOptions {
outputDir: string;
}

export default class ElixirTeslaCodegen extends CodegenBase<ElixirTeslaCodegenOptions> {
readonly #outputDir: OutputDir;

constructor(opts: ElixirTeslaCodegenOptions) {
super(opts);
this.#outputDir = new OutputDir(this.options.outputDir);
}

#processOperation = async (args: {
operationMethod: string;
operationPath: string;
pathItem: PathItemObject;
operation: OperationObject;
}) => {
const functionName = snakeCase(args.operation.operationId);
await this.#outputDir.appendFile(
'api.ex',
`
def ${functionName}()
:ok
end
`
);
};

async generate() {
await this.#outputDir.resetDir();
await this.#outputDir.createDir('components');
await this.#outputDir.writeFile('api.ex', '');
await forEachHttpOperation(this.document, this.#processOperation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`elixir-tesla generator: api.ex 1`] = `
"
def update_pet()
:ok
end

def add_pet()
:ok
end

def find_pets_by_status()
:ok
end

def find_pets_by_tags()
:ok
end

def get_pet_by_id()
:ok
end

def update_pet_with_form()
:ok
end

def delete_pet()
:ok
end

def upload_file()
:ok
end

def get_inventory()
:ok
end

def place_order()
:ok
end

def get_order_by_id()
:ok
end

def delete_order()
:ok
end

def create_user()
:ok
end

def create_users_with_list_input()
:ok
end

def login_user()
:ok
end

def logout_user()
:ok
end

def get_user_by_name()
:ok
end

def update_user()
:ok
end

def delete_user()
:ok
end
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createTmpDir, readPetStoreSpec } from './support-files';
import * as path from 'path';
import ElixirTeslaCodegen from '../../src/generators/elixir-tesla';

test('elixir-tesla generator', async () => {
// GIVEN
const tmpDir = await createTmpDir(['elixir-tesla']);
const openapiDocument = await readPetStoreSpec();

const generator = new ElixirTeslaCodegen({
outputDir: tmpDir.path,
}).setDocument(openapiDocument);

// WHEN
await generator.generate();

// THEN
for await (const filePath of tmpDir.walkFiles('.')) {
const snapshotName = path.relative(tmpDir.path, filePath);
expect(await tmpDir.readFile(filePath)).toMatchSnapshot(snapshotName);
}
});