Files
system-builder/src/views/views-creator.ts

395 lines
14 KiB
TypeScript

import {
ColumnRef,
ComponentDef,
EndpointDef,
Filter,
JoinDef,
Order,
SystemDef,
} from '../systemGenService';
import * as path from 'path';
import * as fs from 'fs';
import pluralize from 'pluralize';
const ncp = require('ncp').ncp;
export function createViews(systemDef: SystemDef) {
let viewsPromises = [];
for (let i in systemDef.components) {
viewsPromises.push(createComponent(systemDef.components[i], systemDef));
}
return Promise.all(viewsPromises);
}
function createComponent(component: ComponentDef, systemDef: SystemDef) {
return new Promise<void>((componentResolve, componentReject) => {
ncp(path.join(process.cwd(), 'frame', 'src', 'components', '{{component}}'), path.join(process.cwd(), systemDef.name, 'src', 'components', component.component), (err: any) => {
if (err) {
console.log(err);
} else {
let mapperPromise = initializeComponentFile(path.join(process.cwd(), systemDef.name, 'src', 'components', component.component, '{{component}}Mapper.ets'), path.join(process.cwd(), systemDef.name, 'src', 'components', component.component, `${component.component}Mapper.ts`), component.component, systemDef.name);
for (let i in component.endpoints) {
mapperPromise = mapperPromise.then(() => {
return insertMapperCode(component.endpoints[i], systemDef.name);
});
}
let routesPromise = initializeComponentFile(path.join(process.cwd(), systemDef.name, 'src', 'components', component.component, '{{component}}Routes.ets'), path.join(process.cwd(), systemDef.name, 'src', 'components', component.component, `${component.component}Routes.ts`), component.component, systemDef.name);
addInitializeRoutesCode(component.component, systemDef.name);
let serviceFileLocation: string = path.join(process.cwd(), systemDef.name, 'src', 'components', component.component, `${component.component}Service.ts`);
let servicePromise = initializeComponentFile(path.join(process.cwd(), systemDef.name, 'src', 'components', component.component, '{{component}}Service.ets'), serviceFileLocation, component.component, systemDef.name);
addInitializeServiceCode(component.component, systemDef.name);
Promise.all([mapperPromise, routesPromise, servicePromise]).then(() => {
console.log(`success creating component ${component.component}`);
componentResolve();
});
}
});
});
}
function initializeComponentFile(sourceFile: string, destinationFile: string, component: string, outDir: string) {
return new Promise<void>((resolve, reject) => {
fs.rename(sourceFile, destinationFile, (err: any) => {
let fileContents = fs.readFileSync(destinationFile, 'utf8');
var uppercaseFirstLetterComponentName = uppercaseFirstLetter(component);
var lowercaseFirstLetterComponentName = lowercaseFirstLetter(component);
let newFileContents = fileContents.split('{{Component}}').join(uppercaseFirstLetterComponentName);
newFileContents = newFileContents.split('{{component}}').join(lowercaseFirstLetterComponentName);
fs.writeFileSync(destinationFile, newFileContents, 'utf8');
resolve();
});
})
}
function addInitializeServiceCode(component: string, outDir: string) {
let fileLocation = path.join(process.cwd(), outDir, 'src', 'initializeServices.ts');
let initServicesFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServicesFile.split('// SYSTEM-BUILDER-initialize-import');
parts[0] = parts[0] + `import { ${component}Service } from './components/${component}/${component}Service';
`;
let newServicesFile = parts.join('// SYSTEM-BUILDER-initialize-import');
parts = newServicesFile.split('// SYSTEM-BUILDER-initialize-service');
parts[0] = parts[0] + `${component}Service.initialize(),
`;
newServicesFile = parts.join(' // SYSTEM-BUILDER-initialize-service');
fs.writeFileSync(fileLocation, newServicesFile, 'utf8');
}
function addInitializeRoutesCode(component: string, outDir: string) {
let fileLocation = path.join(process.cwd(), outDir, 'src', 'app.ts');
let initRoutesFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initRoutesFile.split('// SYSTEM-BUILDER-router.require');
parts[0] = parts[0] + `var ${component}Router = require('./components/${component}/${component}Routes');
`;
let newRoutesFile = parts.join('// SYSTEM-BUILDER-router.require');
parts = newRoutesFile.split('// SYSTEM-BUILDER-app.use');
parts[0] = parts[0] + `app.use('/api/v1/${component}', ${component}Router);
`;
newRoutesFile = parts.join('// SYSTEM-BUILDER-app.use');
fs.writeFileSync(fileLocation, newRoutesFile, 'utf8');
}
function insertMapperCode(view: EndpointDef, outDir: string): Promise<void> {
return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-${view.component}-mapper`;
let fileLocation = path.join(process.cwd(), outDir, 'src', 'components', view.component, `${view.component}Mapper.ts`);
let initMapperFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initMapperFile.split(separator);
parts[0] = parts[0] + createMapperFunc(view);
let newMapperFile = parts.join(separator);
fs.writeFileSync(fileLocation, newMapperFile, 'utf8');
resolve();
});
}
function createMapperFunc(view: EndpointDef): string {
let func: string = '';
if (view.type === 'list' || view.type === 'count') {
func = buildGetList(view);
} else if (view.type === 'item') {
func = buildGetItem(view);
} else if (view.type === 'update') {
func = buildUpdateFunc(view);
} else if (view.type === 'create') {
func = buildCreateFunc(view);
} else if (view.type === 'delete') {
func = buildDeleteItem(view);
}
return func;
}
function buildDeleteItem(view: EndpointDef): string {
let func: string = '';
let funcName = buildFunctionName(view);
let tables: string = `${view.table}`;
let filtersObj = buildFilters(view);
let filters: string = filtersObj.filters;
let requiredFilterValues: string = filtersObj.requiredFilterValues;
let optionalFilterParts = filtersObj.optionalFiltersCode;
let query: string = `DELETE FROM ${tables}${filters}`;
let funcParams = 'params: {[key: string]: string}';
func = `
public ${funcName}(${funcParams}) {
let query: string = '${query}';
let queryValues: string[] = [${requiredFilterValues}];
// optional params?
${optionalFilterParts}
return super.runQuery(query, [...queryValues]);
}
`;
return func;
}
function buildGetItem(view: EndpointDef): string {
let func: string = '';
let funcName = buildFunctionName(view);
let columns: string = `${view.component}.${view.component}_id, ${view.columns.map(c => `${c.table}.${c.name}`).join(', ')}`;
let tables: string = view.join?.length ? buildJoin(view) : `${view.table}`;
let filtersObj = buildFilters(view);
let filters: string = filtersObj.filters;
let requiredFilterValues: string = filtersObj.requiredFilterValues;
let optionalFilterParts = filtersObj.optionalFiltersCode;
let query: string = `SELECT ${columns} FROM ${tables}${filters} LIMIT 1`;
let funcParams = 'params: {[key: string]: string}';
func = `
public ${funcName}(${funcParams}) {
let query: string = '${query}';
let queryValues: string[] = [${requiredFilterValues}];
// optional params?
${optionalFilterParts}
return super.runQuery(query, [...queryValues]);
}
`;
return func;
}
function buildCreateFunc(view: EndpointDef): string {
let func: string = '';
let query: string = `INSERT INTO ${view.table} SET `;
func = getCreateOrUpdateFunc(query, view);
return func;
}
function buildUpdateFunc(view: EndpointDef): string {
let func: string = '';
let query: string = `UPDATE ${view.table} SET `;
func = getCreateOrUpdateFunc(query, view);
return func;
}
function getCreateOrUpdateFunc(query: string, view: EndpointDef): string {
let func: string = '';
let funcName = buildFunctionName(view);
let requiredQueryValues: string = '';
let optionalValuesCode: string = '';
for (let i in view.columns) {
let c: ColumnRef = view.columns[i];
if (c.required) {
if (!query.endsWith('SET ')) {
query = query + `, `;
}
query = query + `${c.table}.${c.name} = ?`;
if (requiredQueryValues.length) {
requiredQueryValues = requiredQueryValues + `, `;
}
requiredQueryValues = requiredQueryValues + `params.${c.param}`;
} else {
optionalValuesCode = optionalValuesCode + `
if (params.${c.param}) {
query = query + ', ${c.table}.${c.name} = ?';
queryValues.push(params.${c.param});
}
`;
}
}
func = `
public ${funcName}(params: {[key: string]: string}) {
let query: string = '${query}';
let queryValues: string[] = [${requiredQueryValues}];
// optional params?
${optionalValuesCode}
return super.runQuery(query, [...queryValues]);
}
`;
return func;
}
function buildGetList(view: EndpointDef): string {
// TODO: optionally allow a raw query definition with predefined params
// This would be so much simpler and easy if it were just the queries wanted
let func: string = '';
let funcName: string = '';
let columns: string = view.type === 'count' ? `count(*)` : `${view.component}.${view.component}_id, ${view.columns.map(c => `${c.table}.${c.name}`).join(', ')}`;
let query: string = '';
let tables: string = view.join?.length ? buildJoin(view) : `${view.table}`;
let filters: string = '';
funcName = buildFunctionName(view);
let requiredFilterStrings = [];
let requiredQueryValues = '';
let optionalFilterParts = '';
if (view.filters) {
for (let i in view.filters) {
let f: Filter = view.filters[i];
if (f.required) {
requiredFilterStrings.push(`${f.column.table}.${f.column.name} ${f.comparison} ?`); // e.g. 'table.id = ?'
if (requiredQueryValues.length) {
requiredQueryValues = requiredQueryValues + `, `;
}
requiredQueryValues = requiredQueryValues + `params.${f.param}`;
} else {
/* e.g.
if (eventLog.app) {
query = query + ', app = ?';
queryValues.push(eventLog.app);
}
*/
optionalFilterParts = optionalFilterParts + `
if (params.${f.param}) {
query = query + ' AND ${f.column.table}.${f.column.name} ${f.comparison} ?';
queryValues.push(params.${f.param});
}`;
}
}
filters = ` WHERE ${requiredFilterStrings.join(' AND ')}`;
}
query = `SELECT ${columns} FROM ${tables}${filters}`;
let orderByCode = '';
if (view.orderBy) {
orderByCode = `
query = query + '${buildOrderBy(view)}';
`;
}
let funcParams = 'params: {[key: string]: string}, offset: number, limit: number';
let limitOrderCode = `query = query + ' LIMIT ?, ?';
queryValues.push(offset.toString(10));
queryValues.push(limit.toString(10));
${orderByCode}`;
if (view.type === 'count') {
funcParams = 'params: {[key: string]: string}';
limitOrderCode = ``;
}
func = `
public ${funcName}(${funcParams}) {
let query: string = '${query}';
let queryValues: string[] = [${requiredQueryValues}];
// optional params?
${optionalFilterParts}
${limitOrderCode}
return super.runQuery(query, [...queryValues]);
}
`;
return func;
}
function buildFilters(view: EndpointDef): {filters: string, requiredFilterValues: string, optionalFiltersCode: string} {
let filtersObj: {filters: string, requiredFilterValues: string, optionalFiltersCode: string} = {
filters: '',
requiredFilterValues: '',
optionalFiltersCode: ''
};
let filters: string = '';
let requiredFilterStrings = [];
let requiredQueryValues = '';
let optionalFilterParts = '';
if (view.filters) {
for (let i in view.filters) {
let f: Filter = view.filters[i];
if (f.required) {
requiredFilterStrings.push(`${f.column.table}.${f.column.name} ${f.comparison} ?`); // e.g. 'table.id = ?'
if (requiredQueryValues.length) {
requiredQueryValues = requiredQueryValues + `, `;
}
requiredQueryValues = requiredQueryValues + `params.${f.param}`;
} else {
/* e.g.
if (eventLog.app) {
query = query + ', app = ?';
queryValues.push(eventLog.app);
}
*/
optionalFilterParts = optionalFilterParts + `
if (params.${f.param}) {
query = query + ' AND ${f.column.table}.${f.column.name} ${f.comparison} ?';
queryValues.push(params.${f.param});
}`;
}
}
filters = ` WHERE ${requiredFilterStrings.join(' AND ')}`;
}
filtersObj.filters = filters;
filtersObj.requiredFilterValues = requiredQueryValues;
filtersObj.optionalFiltersCode = optionalFilterParts;
return filtersObj;
}
function buildJoin(view: EndpointDef): string {
if (view.join?.length) {
let join = `${view.table}`;
for (let i in view.join) {
let j: JoinDef = view.join[i];
join = join + ` JOIN ${j.table} ON ${j.on.left.table}.${j.on.left.name} = ${j.on.right.table}.${j.on.right.name}`;
}
return join;
}
return '';
}
function buildOrderBy(view: EndpointDef): string {
let orderBy: string = '';
if (view.orderBy?.length) {
orderBy = orderBy + ' ORDER BY ';
for (let i in view.orderBy) {
let order: Order = view.orderBy[i];
if (parseInt(i) !== 0) {
orderBy = orderBy + ', ';
}
orderBy = orderBy + `${order.column.table}.${order.column.name} ${order.direction}`;
}
}
return orderBy;
}
function uppercaseFirstLetter(input: string)
{
return input.charAt(0).toUpperCase() + input.slice(1);
}
function lowercaseFirstLetter(input: string)
{
return input.charAt(0).toLowerCase() + input.slice(1);
}
function buildFunctionName(view: EndpointDef): string {
let selector: {[type: string]: (view: EndpointDef) => string} = {
'list': (view: EndpointDef) => `get${pluralize(uppercaseFirstLetter(view.component))}` + (view.type === 'count' ? 'Count' : ''),
'count': (view: EndpointDef) => `get${pluralize(uppercaseFirstLetter(view.component))}` + (view.type === 'count' ? 'Count' : ''),
'item': (view: EndpointDef) => `get${uppercaseFirstLetter(view.component)}`,
'update': (view: EndpointDef) => `update${uppercaseFirstLetter(view.component)}`,
'create': (view: EndpointDef) => `create${uppercaseFirstLetter(view.component)}`,
'delete': (view: EndpointDef) => `delete${uppercaseFirstLetter(view.component)}`,
'search': (view: EndpointDef) => `search${uppercaseFirstLetter(view.component)}`,
};
return selector[view.type](view);
}