initialize components, routes, and services
This commit is contained in:
2
dist/example-def.d.ts
vendored
2
dist/example-def.d.ts
vendored
@ -1,3 +1,3 @@
|
||||
import { SystemDef } from './processDef';
|
||||
import { SystemDef } from './systemGenService';
|
||||
declare let def: SystemDef;
|
||||
export default def;
|
||||
|
1
dist/example-def.js
vendored
1
dist/example-def.js
vendored
@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var def = {
|
||||
name: 'Todo',
|
||||
storage: {
|
||||
tables: [
|
||||
{
|
||||
|
4
dist/processDef.js
vendored
4
dist/processDef.js
vendored
@ -103,10 +103,10 @@ function dateString() {
|
||||
}
|
||||
function writeMigrationsToFile(migrations) {
|
||||
var migrationFileContents = "\n--up\n" + migrations.up.join('\n') + "\n--down\n" + migrations.down.join('\n') + "\n ";
|
||||
var migrationFilePath = path.join(__dirname, outDir, 'src', 'migrationJobs', dateString() + "-create-database.sql");
|
||||
var migrationFilePath = path.join(process.cwd(), outDir, 'src', 'migrationJobs', dateString() + "-create-database.sql");
|
||||
fs.writeFileSync(migrationFilePath, migrationFileContents);
|
||||
}
|
||||
ncp(path.join(__dirname, 'frame'), path.join(__dirname, outDir), function (err) {
|
||||
ncp(path.join(process.cwd(), 'frame'), path.join(process.cwd(), outDir), function (err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
app.use('/', indexRouter);
|
||||
app.use('/api/v1/users', usersRouter);
|
||||
|
||||
// SYSTEM-BUILDER-app.use
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { db } from '../../databases';
|
||||
import { {{Component}}Mapper } from './componentMapper';
|
||||
import { {{Component}}Mapper } from './{{component}}Mapper';
|
||||
|
||||
class {{Component}}Service {
|
||||
private {{component}}Mapper = new {{Component}}Mapper(db);
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { userService } from './components/users/userService';
|
||||
|
||||
// SYSTEM-BUILDER-initialize-import
|
||||
|
||||
|
||||
import { altitude } from './databases';
|
||||
|
||||
const appReady = altitude.runMigrations().then(() => {
|
||||
let servicesPromise = [
|
||||
userService.initialize(),
|
||||
|
||||
// SYSTEM-BUILDER-initialize-service
|
||||
|
||||
];
|
||||
return Promise.all(servicesPromise);
|
||||
});
|
||||
|
@ -5,7 +5,7 @@
|
||||
"path": "^0.12.7"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "rm -rf dist && tsc && cp -r frame dist/ && node dist/processDef.js"
|
||||
"start": "rm -rf dist && tsc && cp -r frame dist/ && cd dist && node index.js"
|
||||
},
|
||||
"name": "system-builder",
|
||||
"version": "1.0.0",
|
||||
|
115
src/database/database-creator.ts
Normal file
115
src/database/database-creator.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import {
|
||||
SystemDef,
|
||||
StorageDef,
|
||||
ViewDef,
|
||||
Order,
|
||||
Filter,
|
||||
TableDef,
|
||||
ColumnDef,
|
||||
BelongsToDef,
|
||||
ManyToManyDef
|
||||
} from '../systemGenService';
|
||||
|
||||
let outDir = 'test';
|
||||
|
||||
export function createDatabase(storageDef: StorageDef) {
|
||||
let tableCreationQueries: string[] = [];
|
||||
let tableDeletionQueries: string[] = [];
|
||||
for (let i in storageDef.tables) {
|
||||
tableCreationQueries.push(getTableCreationString(storageDef.tables[i]));
|
||||
tableDeletionQueries.push(getTableDeletionString(storageDef.tables[i]));
|
||||
}
|
||||
for (let i in storageDef.relations) {
|
||||
tableCreationQueries.push(getTableRelationsCreationString(storageDef.relations[i]));
|
||||
tableDeletionQueries.push(getTableRelationsDeletionString(storageDef.relations[i]))
|
||||
}
|
||||
tableCreationQueries.map((query: string) => {
|
||||
console.log(query);
|
||||
});
|
||||
|
||||
tableDeletionQueries.map((query: string) => {
|
||||
console.log(query);
|
||||
});
|
||||
|
||||
return {
|
||||
up: tableCreationQueries,
|
||||
down: tableDeletionQueries,
|
||||
};
|
||||
}
|
||||
|
||||
function getTableCreationString(tableDef: TableDef): string {
|
||||
let primaryKeyString: string = `${tableDef.name}_id VARCHAR(36) PRIMARY KEY`;
|
||||
let columnString: string = `${primaryKeyString}`;
|
||||
let defaultColumns: string = `created DATETIME NOT NULL DEFAULT NOW(), modified DATETIME NOT NULL DEFAULT NOW()`
|
||||
let indexString: string = ``;
|
||||
|
||||
// columns
|
||||
for (let i in tableDef.columns) {
|
||||
let column: ColumnDef = tableDef.columns[i];
|
||||
columnString = `${columnString}, ${createColumnString(column)}`;
|
||||
}
|
||||
|
||||
// relations
|
||||
for (let i in tableDef.relations) {
|
||||
let relation: BelongsToDef = tableDef.relations[i];
|
||||
columnString = `${columnString}, ${relation.table}_id VARCHAR(36)`;
|
||||
}
|
||||
|
||||
// default created & modified
|
||||
columnString = `${columnString}, ${defaultColumns}`;
|
||||
|
||||
return `CREATE TABLE IF NOT EXISTS ${tableDef.name} (${columnString}${indexString !== '' ? ', ' + indexString : ''});`;
|
||||
}
|
||||
|
||||
function getTableRelationsCreationString(relationsDef: ManyToManyDef): string {
|
||||
let columnString: string = `${relationsDef.left}_id VARCHAR(36) NOT NULL, ${relationsDef.right}_id VARCHAR(36) NOT NULL`;
|
||||
let indexString: string = `INDEX ${relationsDef.left}_id_index (${relationsDef.left}_id), INDEX ${relationsDef.right}_id_index (${relationsDef.right}_id)`;
|
||||
|
||||
if (relationsDef.columns && relationsDef.columns.length) {
|
||||
for (let i in relationsDef.columns) {
|
||||
let column: ColumnDef = relationsDef.columns[i];
|
||||
columnString = `${columnString}, ${createColumnString(column)}`;
|
||||
}
|
||||
}
|
||||
|
||||
return `CREATE TABLE IF NOT EXISTS ${relationsDef.left}_${relationsDef.right} (${columnString}${indexString !== '' ? ', ' + indexString : ''});`;
|
||||
}
|
||||
|
||||
function createColumnString(column: ColumnDef) {
|
||||
const typeMap: {[type: string]: string} = {
|
||||
"blob": "BLOB",
|
||||
"boolean": "BOOLEAN",
|
||||
"date": "DATE",
|
||||
"dateTIME": "DATETIME",
|
||||
"number": "INT",
|
||||
"string": "VARCHAR(255)",
|
||||
};
|
||||
|
||||
return `${column.name} ${typeMap[column.type]}${column.nullable ? '' : ' NOT NULL'}${column.default ? ' DEFAULT ' + column.default : ''}${column.autoIncrement ? ' AUTO_INCREMENT' : ''}${column.unique ? ' UNIQUE' : ''}`;
|
||||
}
|
||||
|
||||
function getTableDeletionString(tableDef: TableDef): string {
|
||||
return `DROP TABLE IF EXISTS ${tableDef.name};`
|
||||
}
|
||||
|
||||
function getTableRelationsDeletionString(relationsDef: ManyToManyDef) : string {
|
||||
return `DROP TABLE IF EXISTS ${relationsDef.left}_${relationsDef.right};`
|
||||
}
|
||||
|
||||
function dateString() {
|
||||
let date = new Date();
|
||||
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`;
|
||||
}
|
||||
|
||||
export function writeMigrationsToFile(migrations: {up: string[], down: string[]}, outDir: string) {
|
||||
let migrationFileContents = `
|
||||
--up
|
||||
${migrations.up.join('\n')}
|
||||
--down
|
||||
${migrations.down.join('\n')}
|
||||
`;
|
||||
let migrationFilePath = path.join(process.cwd(), outDir, 'src', 'migrationJobs', `${dateString()}-create-database.sql`);
|
||||
fs.writeFileSync(migrationFilePath, migrationFileContents);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { SystemDef } from './processDef';
|
||||
import { SystemDef } from './systemGenService';
|
||||
|
||||
let def: SystemDef = {
|
||||
name: 'Todo',
|
||||
storage: {
|
||||
tables: [
|
||||
{
|
||||
|
9
src/index.ts
Normal file
9
src/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
import { systemGenService } from './systemGenService';
|
||||
|
||||
import defs from './example-def';
|
||||
// import simmer from './simmer-def-example';
|
||||
|
||||
|
||||
systemGenService.runSysGen(defs);
|
@ -104,12 +104,12 @@ ${migrations.up.join('\n')}
|
||||
--down
|
||||
${migrations.down.join('\n')}
|
||||
`;
|
||||
let migrationFilePath = path.join(__dirname, outDir, 'src', 'migrationJobs', `${dateString()}-create-database.sql`);
|
||||
let migrationFilePath = path.join(process.cwd(), outDir, 'src', 'migrationJobs', `${dateString()}-create-database.sql`);
|
||||
fs.writeFileSync(migrationFilePath, migrationFileContents);
|
||||
}
|
||||
|
||||
|
||||
ncp(path.join(__dirname, 'frame'), path.join(__dirname, outDir), (err: any) => {
|
||||
ncp(path.join(process.cwd(), 'frame'), path.join(process.cwd(), outDir), (err: any) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
|
105
src/systemGenService.ts
Normal file
105
src/systemGenService.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import * as path from 'path';
|
||||
const ncp = require('ncp').ncp;
|
||||
import { createDatabase, writeMigrationsToFile } from './database/database-creator';
|
||||
import { createViews } from './views/views-creator';
|
||||
|
||||
class SystemGenService {
|
||||
|
||||
public runSysGen(defs: SystemDef) {
|
||||
ncp(path.join(process.cwd(), 'frame'), path.join(process.cwd(), defs.name), (err: any) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('success copying files');
|
||||
this.buildDatabase(defs.storage, defs.name);
|
||||
this.buildViews(defs, defs.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public buildDatabase(storage: StorageDef, outDir: string) {
|
||||
let creationMigrations = createDatabase(storage);
|
||||
writeMigrationsToFile(creationMigrations, outDir);
|
||||
}
|
||||
|
||||
public buildViews(systemDef: SystemDef, outDir: string) {
|
||||
createViews(systemDef);
|
||||
}
|
||||
}
|
||||
|
||||
const systemGenService = new SystemGenService();
|
||||
|
||||
|
||||
interface SystemDef {
|
||||
name: string;
|
||||
storage: StorageDef;
|
||||
views: ViewDef[];
|
||||
// TODO: add Views, ACLs, Behaviors, UX
|
||||
}
|
||||
|
||||
interface StorageDef {
|
||||
tables: TableDef[];
|
||||
relations: ManyToManyDef[];
|
||||
}
|
||||
|
||||
interface ViewDef {
|
||||
component: string;
|
||||
type: ('list' | 'count' | 'item' | 'distinct')[];
|
||||
columns: string[];
|
||||
orderBy?: Order[];
|
||||
filters?: Filter[];
|
||||
// if type is 'list' it will always include skip and limit for pagination
|
||||
}
|
||||
|
||||
interface Order {
|
||||
column: string;
|
||||
direction: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
param: string; // the query param used to get the value
|
||||
column: string;
|
||||
comparison: '=' | '!=' | '>' | '<' | 'contains';
|
||||
value?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
interface TableDef {
|
||||
name: string;
|
||||
columns: ColumnDef[],
|
||||
relations: BelongsToDef[],
|
||||
}
|
||||
|
||||
interface ColumnDef {
|
||||
name: string;
|
||||
type: "blob" | "boolean" | "date" | "dateTIME" | "number" | "string";
|
||||
nullable: boolean;
|
||||
unique?: boolean;
|
||||
autoIncrement?: boolean;
|
||||
default?: string;
|
||||
}
|
||||
|
||||
interface BelongsToDef {
|
||||
type: 'belongs-to';
|
||||
table: string;
|
||||
}
|
||||
|
||||
interface ManyToManyDef {
|
||||
left: string;
|
||||
relation: 'many-to-many';
|
||||
right: string;
|
||||
columns?: ColumnDef[];
|
||||
}
|
||||
|
||||
export {
|
||||
SystemDef,
|
||||
StorageDef,
|
||||
ViewDef,
|
||||
Order,
|
||||
Filter,
|
||||
TableDef,
|
||||
ColumnDef,
|
||||
BelongsToDef,
|
||||
ManyToManyDef,
|
||||
systemGenService
|
||||
}
|
92
src/views/views-creator.ts
Normal file
92
src/views/views-creator.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {
|
||||
SystemDef,
|
||||
ViewDef
|
||||
} from '../systemGenService';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const ncp = require('ncp').ncp;
|
||||
|
||||
|
||||
export function createViews(systemDef: SystemDef) {
|
||||
let viewsPromises = [];
|
||||
for (let i in systemDef.views) {
|
||||
viewsPromises.push(createComponent(systemDef.views[i], systemDef.name));
|
||||
}
|
||||
return Promise.all(viewsPromises);
|
||||
}
|
||||
|
||||
function createComponent(view: ViewDef, outDir: string) {
|
||||
let componentPromise = new Promise((componentResolve, componentReject) => {
|
||||
ncp(path.join(process.cwd(), 'frame', 'src', 'components', '{{component}}'), path.join(process.cwd(), outDir, 'src', 'components', view.component), (err: any) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
let mapperPromise = initializeComponentFile(path.join(process.cwd(), outDir, 'src', 'components', view.component, '{{component}}Mapper.ets'), path.join(process.cwd(), outDir, 'src', 'components', view.component, `${view.component}Mapper.ts`), view.component, outDir);
|
||||
let routesPromise = initializeComponentFile(path.join(process.cwd(), outDir, 'src', 'components', view.component, '{{component}}Routes.ets'), path.join(process.cwd(), outDir, 'src', 'components', view.component, `${view.component}Routes.ts`), view.component, outDir);
|
||||
addInitializeRoutesCode(view.component, outDir);
|
||||
let serviceFileLocation: string = path.join(process.cwd(), outDir, 'src', 'components', view.component, `${view.component}Service.ts`);
|
||||
let servicePromise = initializeComponentFile(path.join(process.cwd(), outDir, 'src', 'components', view.component, '{{component}}Service.ets'), serviceFileLocation, view.component, outDir);
|
||||
addInitializeServiceCode(view.component, outDir);
|
||||
Promise.all([mapperPromise, routesPromise, servicePromise]).then(() => {
|
||||
console.log(`success creating component ${view.component}`);
|
||||
componentResolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
return componentPromise;
|
||||
}
|
||||
|
||||
function initializeComponentFile(sourceFile: string, destinationFile: string, component: string, outDir: string) {
|
||||
let filePromise = new Promise((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();
|
||||
});
|
||||
});
|
||||
return filePromise
|
||||
}
|
||||
|
||||
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 uppercaseFirstLetter(input: string)
|
||||
{
|
||||
return input.charAt(0).toUpperCase() + input.slice(1);
|
||||
}
|
||||
|
||||
function lowercaseFirstLetter(input: string)
|
||||
{
|
||||
return input.charAt(0).toLowerCase() + input.slice(1);
|
||||
}
|
Reference in New Issue
Block a user