395 lines
14 KiB
TypeScript
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);
|
|
} |