Skip to content

Instantly share code, notes, and snippets.

@NicolasDurant
Last active June 30, 2020 21:51
Show Gist options
  • Save NicolasDurant/0fbe0601e2f226199532e681646897aa to your computer and use it in GitHub Desktop.
Save NicolasDurant/0fbe0601e2f226199532e681646897aa to your computer and use it in GitHub Desktop.
[Node - MirageJS to Swagger-Generator] A script that can sit in the MirageJS-Folder and generate as much documentation as possible out of the factories configuration. Filepaths might need tweaking according to paths. Can be run with `node swagger-generator.js` #MirageJS #JavaScript #Swagger #yml
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const opt = 'utf8';
const routes = fs.readFileSync('./routes.js', opt);
rl.question('Want to generate Swagger Info Object (y/n)? ', (answer) => {
if (answer === 'y') {
startReadingFiles(true);
} else {
startReadingFiles(false);
}
rl.close();
});
function startReadingFiles(infoObject) {
fs.readFile('./server.js', opt, (error, serverData) => {
if (error) return error;
let fileContent = serverData;
let modelsStart = fileContent.indexOf('models');
if (modelsStart === -1) {
console.error('Couldnt find any model definition in the server.js file');
} else {
const models = getModelsAsArray(fileContent, modelsStart);
if (models) {
fs.readFile('./factories.js', opt, (error, factoriesData) => {
if (error) return error;
writingSwaggerFile(infoObject, models, factoriesData);
});
}
}
});
}
function writingSwaggerFile(infoObject, models, factoriesData) {
console.log('>>> CREATING FILE swagger.yml');
let stream = fs.createWriteStream('./swagger.yml');
stream.once('open', function(fd) {
if (infoObject) {
console.log('>>>>>> WRITING SWAGGER INFO OBJECT');
stream.write(INFO_OBJECT);
}
generateSchematas(models, factoriesData, stream);
generatePaths(models, stream);
stream.end();
});
}
function generateSchematas(models, factoriesData, stream) {
console.log('>>>>>> CREATING SWAGGER SCHEMATA');
stream.write(SCHEMATAS);
console.log('>>>>>> CREATING DEFAULT ERROR SCHEMATA');
stream.write(SCHEMA_ERROR);
models.forEach((element) => {
let fieldNames = analyseFactories(factoriesData, element);
if (fieldNames !== -1) {
console.log('>>>>>>>>> WRITING SWAGGER SCHEMA ' + element);
let els = element + 's';
let elCamel = element.charAt(0).toUpperCase() + element.slice(1);
let elCamels = element.charAt(0).toUpperCase() + element.slice(1) + 's';
let spr = SCHEMA_PLACEHOLDER_REQUIRED.replace('{Placeholder}', elCamel);
console.log('>>>>>>>>> WRITING SWAGGER SCHEMA REQUIRED FIELDS ');
stream.write(spr);
fieldNames.forEach((field) => {
let f = SCHEMA_PLACEHOLDER_REQUIRED_FIELD.replace(
'{placeholder}',
field
);
stream.write(f);
});
stream.write(SCHEMA_PLACEHOLDER_PROPERTIES);
console.log('>>>>>>>>> WRITING SWAGGER SCHEMA PROPERTIES FIELDS ');
fieldNames.forEach((field) => {
let f = SCHEMA_PLACEHOLDER_PROPERTIES_FIELD.replace(
'{placeholder}',
field
);
stream.write(f);
});
console.log('>>>>>>>>> WRITING SWAGGER SCHEMA ' + els);
let spl = SCHEMA_PLACEHOLDER_LIST.replace(
'{Placeholder}',
elCamel
).replace('{Placeholders}', elCamels);
stream.write(spl);
}
});
}
function generatePaths(models, stream) {
console.log('>>>>>> CREATING SWAGGER PATHS');
stream.write(PATHS);
models.forEach((element) => {
console.log('>>>>>>>>> WRITING SWAGGER PATHS ' + element);
let els = element + 's';
let elCamel = element.charAt(0).toUpperCase() + element.slice(1);
let elCamels = element.charAt(0).toUpperCase() + element.slice(1) + 's';
let pp = PATHS_PLACEHOLDER.replace(/\{Placeholder\}/g, elCamel)
.replace(/\{Placeholders\}/g, elCamels)
.replace(/\{placeholder\}/g, element)
.replace(/\{placeholders\}/g, els);
stream.write(pp);
});
}
function getModelsAsArray(fileContent, modelsStart) {
console.log('>>> PARSING MODELS FROM server.js');
let modelsObjectStart = fileContent.indexOf('{', modelsStart);
let modelsObjectEnd = fileContent.indexOf('}', modelsStart);
let modelsString = fileContent.slice(modelsObjectStart + 1, modelsObjectEnd);
let models = modelsString.replace(/ |\n|Model,/g, '').split(':');
models.pop();
if (models.length > 0) {
models = models.sort();
console.log('>>> FOUND MODELS:');
models.forEach((element) => {
console.log('>>>>>> ' + element);
});
return models;
} else {
console.log('>>> NO MODELS DEFINED');
return -1;
}
}
function analyseFactories(fileContent, element) {
console.log('>>> SEARCHING MODEL ' + element + ' IN factories.js');
let modelsStart = fileContent.indexOf(element + ':');
if (modelsStart === -1) {
console.error(
'Couldnt find the model ' + element + ' in the factories.js file'
);
} else {
console.log('>>> FOUND MODEL ' + element + ' IN factories.js');
let modelsObjectStart = fileContent.indexOf('({', modelsStart);
let modelsObjectEnd = fileContent.indexOf('}),', modelsStart);
let modelsString = fileContent.slice(
modelsObjectStart + 2,
modelsObjectEnd
);
let indecisOfFieldNames = [];
let fieldNames = [];
let regex = /\s{3}[A-Z|a-z]+[(]{1}[)]{1}/gi;
let result;
while ((result = regex.exec(modelsString))) {
indecisOfFieldNames.push(result.index);
}
if (indecisOfFieldNames.length > 0) {
console.log('>>> FOUND FIELDNAMES:');
indecisOfFieldNames.forEach((index) => {
fieldEnd = modelsString.indexOf('()', index);
fieldStart = index + 3;
let value = modelsString.slice(fieldStart, fieldEnd);
console.log('>>>>>> ' + value);
fieldNames.push(value);
});
return fieldNames;
} else {
return -1;
}
}
}
const INFO_OBJECT = `
openapi: 3.0.0
info:
title: PWA Aicovo
description: The OpenAPI Specification (OAS) for the PWA Aicovo project. This API is based on the Miragejs Mockdata Service used in the student project and can be added on or modified.
version: 1.0.0
contact:
name: Nicolas Durant
email: [email protected]
servers:
- url: http://localhost:8080/api/v1/
description: Local Development
- url: https://pwa-aicovo-dev.web.app/api/v1/
description: Staging
- url: https://aicovo.de/api/v1/
description: Production
`;
const SCHEMATAS = `
components:
schemas:`;
const SCHEMA_ERROR = `
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string`;
const SCHEMA_PLACEHOLDER_REQUIRED = `
{Placeholder}:
required:`;
const SCHEMA_PLACEHOLDER_REQUIRED_FIELD = `
- {placeholder}`;
const SCHEMA_PLACEHOLDER_PROPERTIES = `
properties:`;
const SCHEMA_PLACEHOLDER_PROPERTIES_FIELD = `
{placeholder}:
type: string`;
const SCHEMA_PLACEHOLDER_LIST = `
{Placeholders}:
type: array
items:
$ref: "#/components/schemas/{Placeholder}"`;
const PATHS = `
paths:`;
const PATHS_PLACEHOLDER = `
/{placeholders}:
get:
summary: List all {placeholders}
operationId: list{Placeholders}
tags:
- {placeholders}
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
"200":
description: A paged array of {placeholders}
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/{Placeholders}"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create an {placeholder}
operationId: create{Placeholders}
tags:
- {placeholders}
parameters:
- name: {placeholder}
in: path
required: true
description: The {placeholder} object
schema:
$ref: "#/components/schemas/{Placeholder}"
responses:
"201":
description: {Placeholder} created
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/{placeholders}/{id}:
get:
summary: Info for a specific {placeholder}
operationId: show{Placeholder}ById
tags:
- {placeholders}
parameters:
- name: id
in: path
required: true
description: The id of the {placeholder} to retrieve
schema:
type: integer
format: int64
responses:
"200":
description: {Placeholder} retrieved
content:
application/json:
schema:
$ref: "#/components/schemas/{Placeholder}"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
put:
summary: Update an {placeholder}
operationId: update{Placeholders}
tags:
- {placeholders}
parameters:
- name: id
in: path
description: ID of {placeholder} to delete
required: true
schema:
type: integer
format: int64
- name: {placeholder}
in: header
required: true
description: The {placeholder} object
schema:
$ref: "#/components/schemas/{Placeholder}"
responses:
"201":
description: {Placeholder} updated
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
summary: Deletes a single {placeholder}
operationId: delete{Placeholder}
tags:
- {placeholders}
parameters:
- name: id
in: path
description: ID of {placeholder} to delete
required: true
schema:
type: integer
format: int64
responses:
"204":
description: {Placeholder} deleted
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"`;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment