working frontend build

This commit is contained in:
2021-07-07 17:12:04 -05:00
parent be789ae882
commit 9869125965
11 changed files with 621 additions and 41 deletions

View File

@ -1,10 +1,5 @@
<template> <template>
<div class="{{component}}-editor"> <div class="{{component}}-details">
<div class="property-row">
<label>Grant Name: </label>
<div>grant.name</div>
</div>
<!-- SYSTEM-BUILDER-property-row --> <!-- SYSTEM-BUILDER-property-row -->
</div> </div>
@ -13,10 +8,36 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'; import { Component, Prop, Vue } from 'vue-property-decorator';
import { {{Component}}Config } from './{{component}}Types.ts'; import { {{Component}}Config } from './{{component}}Types.ts';
import { {{component}}Service } from './{{component}}Service.ts';
@Component @Component
export default class {{Component}}Editor extends Vue { export default class {{Component}}Editor extends Vue {
@Prop() private {{component}}!: {{Component}}Config; @Prop() private id!: string;
private {{component}}!: {{Component}}Config = {
// SYSTEM-BUILDER-init-props
};
private loading: boolean = false;
private errorMessage: string = '';
private mounted() {
this.loadDetails();
}
private loadDetails() {
this.loading = true;
this.errorMessage = '';
{{component}}Service.get{{Component}}(this.id).then((res: {{Component}}Config) => {
this.loading = false;
this.errorMessage = '';
this.{{component}} = res;
}).catch(this.handleError.bind(this));
}
private handleError(err: any) {
// error
this.loading = false;
this.errorMessage = err.message;
}
} }
</script> </script>

View File

@ -1,9 +1,5 @@
<template> <template>
<div class="{{component}}-editor"> <div class="{{component}}-editor">
<div class="input-row">
<label>Grant Name: </label>
<input class="form-control" type="text" ref="name" placeholder="Grant Name" v-model="grant.name" />
</div>
<!-- SYSTEM-BUILDER-input-row --> <!-- SYSTEM-BUILDER-input-row -->
@ -22,7 +18,10 @@ import { {{Component}}Config } from './{{component}}Types.ts';
@Component @Component
export default class {{Component}}Editor extends Vue { export default class {{Component}}Editor extends Vue {
@Prop() private {{component}}!: {{Component}}Config; @Prop() private id!: string;
private {{component}}!: {{Component}}Config = {
// SYSTEM-BUILDER-init-props
};
private loading: boolean = false; private loading: boolean = false;
private errorMessage: string = ''; private errorMessage: string = '';
@ -30,11 +29,29 @@ export default class {{Component}}Editor extends Vue {
return true; return true;
} }
private close() {
this.$emit('close-editor');
}
private mounted() {
this.loadDetails();
}
private loadDetails() {
this.loading = true;
this.errorMessage = '';
{{component}}Service.get{{Component}}(this.id).then((res: {{Component}}Config) => {
this.loading = false;
this.errorMessage = '';
this.{{component}} = res;
}).catch(this.handleError.bind(this));
}
private save{{Component}}() { private save{{Component}}() {
this.loading = true; this.loading = true;
this.errorMessage = ''; this.errorMessage = '';
// either create or update... // either create or update...
if (this.{{component}}.id === '') { // TODO: use the primary key instead of 'id' here if (this.{{component}}.{{component}}_id === '') {
{{component}}Service.create{{Component}}(this.{{component}}).then(() => { {{component}}Service.create{{Component}}(this.{{component}}).then(() => {
// success // success
this.loading = false; this.loading = false;

View File

@ -1,12 +1,15 @@
<template> <template>
<div class="{{component}}-editor"> <div class="{{component}}-editor">
<div>TODO: search</div> <div class="filter-row">
<!-- SYSTEM-BUILDER-filter-options -->
<div v-for="item in items" :key="item.id" class="input-row">
<div class="">{{ item.id }}</div>
</div> </div>
<div class="errorMessage" v-if="errorMessage != ''">{{errorMessage}}</div> <div v-for="item in items" :key="item.{{component}}_id" class="input-row">
<div class="">{{ item.{{component}}_id }}</div>
<!-- SYSTEM-BUILDER-item-props -->
</div>
<div class="red-text" v-if="errorMessage !== ''">{{errorMessage}}</div>
</div> </div>
</template> </template>
@ -17,22 +20,18 @@ import { {{Component}}Config } from './{{component}}Types.ts';
@Component @Component
export default class {{Component}}Editor extends Vue { export default class {{Component}}Editor extends Vue {
private items!: {{Component}}Config[]; private items: {{Component}}Config[] = [];
private loading: boolean = false; private loading: boolean = false;
private errorMessage: string = ''; private errorMessage: string = '';
private filterItems: {{Component}}Config = {
// SYSTEM-BUILDER-init-props
};
// SYSTEM-BUILDER-component-variables // SYSTEM-BUILDER-component-variables
// SYSTEM-BUILDER-component-functions // SYSTEM-BUILDER-component-functions
private get{{Component}}s() { private mounted() {
this.loading = true; get{{Components}}();
this.errorMessage = '';
{{component}}Service.get{{Component}}s().then((listItems: {{Component}}Config[]) => {
// success
this.loading = false;
this.errorMessage = '';
this.items = listItems;
}).catch(this.handleError.bind(this));
} }
// TODO: add a search function if there is an endpoint with type 'search' // TODO: add a search function if there is an endpoint with type 'search'
@ -46,7 +45,17 @@ export default class {{Component}}Editor extends Vue {
</script> </script>
<style scoped lang="css"> <style scoped lang="css">
.errorMessage { .red-text {
color: red; color: red;
} }
.input-row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.filter-row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
</style> </style>

View File

@ -2,9 +2,12 @@
// SYSTEM-BUILDER-{{component}}-interfaces // SYSTEM-BUILDER-{{component}}-interfaces
// e.g. // e.g.
// interface {{Component}}Config { interface {{Component}}Config {
// } {{component}}_id: string;
// SYSTEM-BUILDER-init-props
}
export { export {
{{Component}}Config,
// SYSTEM-BUILDER-{{component}}-exports // SYSTEM-BUILDER-{{component}}-exports
}; };

View File

@ -0,0 +1,61 @@
import {ColumnDef, ColumnRef, ComponentDef, EndpointDef, SystemDef, TableDef} from "../systemGenService";
import path from "path";
import fs from "fs";
import {createDetailsInitValues} from "../helpers";
export function buildDetailsView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
let detailsEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
return epd.type === 'item';
});
if (detailsEndpoints.length) {
return insertFEDetailsMarkup(detailsEndpoints[0], outDir).then(() => {
return insertFEDetailsCode(detailsEndpoints[0], outDir, systemDef);
});
} else {
return Promise.resolve();
}
}
function insertFEDetailsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-init-props`;
let fileLocation = path.join(outDir, `${view.component}Details.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createDetailsInitValues(view, systemDef);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function insertFEDetailsMarkup(view: EndpointDef, outDir: string) {
return new Promise<void>((resolve) => {
const separator: string = `<!-- SYSTEM-BUILDER-property-row -->`;
let fileLocation = path.join(outDir, `${view.component}Details.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createDetailsMarkup(view);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function createDetailsMarkup(view: EndpointDef): string {
let out = ``;
view.columns.forEach((column: ColumnRef) => {
out = out + `<div class="property-row">
<label>${column.name}: </label>
<div>{{ ${view.component}.${column.name} }}</div>
</div>
`;
});
return out;
}

View File

@ -0,0 +1,73 @@
import {ColumnDef, ColumnRef, ComponentDef, EndpointDef, SystemDef, TableDef} from "../systemGenService";
import path from "path";
import fs from "fs";
import {createDetailsInitValues, uppercaseFirstLetter} from "../helpers";
export function buildEditorView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
let detailsEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
return epd.type === 'item';
});
if (detailsEndpoints.length) {
return insertFEDetailsMarkup(detailsEndpoints[0], outDir).then(() => {
return insertFEDetailsCode(detailsEndpoints[0], outDir, systemDef);
});
} else {
return Promise.resolve();
}
}
function insertFEDetailsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-init-props`;
let fileLocation = path.join(outDir, `${view.component}Editor.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createDetailsInitValues(view, systemDef);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function insertFEDetailsMarkup(view: EndpointDef, outDir: string) {
return new Promise<void>((resolve) => {
const separator: string = `<!-- SYSTEM-BUILDER-input-row -->`;
let fileLocation = path.join(outDir, `${view.component}Editor.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createDetailsMarkup(view);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function createDetailsMarkup(view: EndpointDef): string {
let out = ``;
let inputType: {[key: string]: string} = {
"blob": "text",
"boolean": "checkbox",
"date": "date",
"dateTIME": "datetime-local",
"number": "number",
"string": "text",
};
view.columns.forEach((column: ColumnRef) => {
let inputTypeValue = 'text';
if (column.type) {
inputTypeValue = inputType[column.type];
}
out = out + `<div class="input-row">
<label>${uppercaseFirstLetter(view.component)} ${uppercaseFirstLetter(column.name)}: </label>
<input class="form-control" type="${inputTypeValue}" placeholder="${column.name}" v-model="${view.component}.${column.name}" />
</div>
`;
});
return out;
}

View File

@ -1,18 +1,27 @@
import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService"; import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService";
import path from "path"; import path from "path";
import {initializeComponentFile} from "../helpers"; import {initializeComponentFile, removeTemplateFiles} from "../helpers";
import fs from "fs"; import fs from "fs";
import {buildServiceFunctionName, getFuncParams} from "../views/service-creator"; import {buildServiceFunctionName, getFuncParams} from "../views/service-creator";
import {METHOD, URL} from "../views/routes-creator"; import {METHOD, URL} from "../views/routes-creator";
import {buildDetailsView} from "./details-view-builder";
import {buildListView} from "./list-view-builder";
import {buildEditorView} from "./editor-view-builder";
import {buildTypesView} from "./types-view-builder";
const ncp = require('ncp').ncp; const ncp = require('ncp').ncp;
export function createFrontend(systemDef: SystemDef) { export function createFrontend(systemDef: SystemDef): Promise<void[]> {
let fePromises = []; let fePromises = [];
for (let i in systemDef.components) { for (let i in systemDef.components) {
fePromises.push(createComponent(systemDef.components[i], systemDef)); fePromises.push(createComponent(systemDef.components[i], systemDef));
// TODO: add a vue view and add it to App.vue // TODO: add a vue view and add it to App.vue
// TODO: build app router logic
} }
return Promise.all(fePromises); // TODO: after all is done clean up the template files
return Promise.all(fePromises).then((res: void[]) => {
removeTemplateFiles(componentSource())
return res;
});
} }
function componentSource() { function componentSource() {
@ -38,12 +47,57 @@ function createComponent(component: ComponentDef, systemDef: SystemDef) {
return insertFEServiceCode(component.endpoints[i], componentDest); return insertFEServiceCode(component.endpoints[i], componentDest);
}); });
// TODO: use templates for Details, Editor, List and Types view based on the endpoints provided // TODO: use templates for Details, Editor, List and Types view based on the endpoints provided
// servicePromise = servicePromise.then(() => {
//
// return initializeComponentFile(path.join(componentDest, '{{component}}Service.ets'), path.join(componentDest, `${component.component}Service.ts`), component.component, systemDef.name);
// });
} }
if (componentContains('list', component)) {
servicePromise = servicePromise.then(() => {
return initializeComponentFile(path.join(componentDest, '{{component}}List.vue'), path.join(componentDest, `${component.component}List.vue`), component.component, systemDef.name);
}).then(() => {
// TODO: build list contents based on the list endpoint
return buildListView(component, componentDest, systemDef);
});
}
if (componentContains('item', component)) {
servicePromise = servicePromise.then(() => {
return initializeComponentFile(path.join(componentDest, '{{component}}Details.vue'), path.join(componentDest, `${component.component}Details.vue`), component.component, systemDef.name);
}).then(() => {
// build list contents based on the item endpoint
return buildDetailsView(component, componentDest, systemDef);
});
}
if (componentContains('update', component) || componentContains('create', component)) {
servicePromise = servicePromise.then(() => {
return initializeComponentFile(path.join(componentDest, '{{component}}Editor.vue'), path.join(componentDest, `${component.component}Editor.vue`), component.component, systemDef.name);
}).then(() => {
// build list contents based on the item endpoint
return buildEditorView(component, componentDest, systemDef);
});
}
if (component.endpoints.length) {
// add a types file to power the whole component
servicePromise = servicePromise.then(() => {
return initializeComponentFile(path.join(componentDest, '{{component}}Types.ets'), path.join(componentDest, `${component.component}Types.ts`), component.component, systemDef.name);
}).then(() => {
return buildTypesView(component, componentDest, systemDef);
});
}
servicePromise.then(() => {
componentResolve();
});
} }
}); });
}); });
} }
function componentContains(type: string, component: ComponentDef) {
return component.endpoints.some((epd: EndpointDef) => {
return epd.type === type;
});
}
function insertFEServiceCode(view: EndpointDef, outDir: string) { function insertFEServiceCode(view: EndpointDef, outDir: string) {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-service-functions`; const separator: string = `// SYSTEM-BUILDER-service-functions`;

View File

@ -0,0 +1,219 @@
import {ColumnDef, ColumnRef, ComponentDef, EndpointDef, Filter, SystemDef, TableDef} from "../systemGenService";
import path from "path";
import fs from "fs";
import {createDetailsInitValues, findColumn, makeSet, uppercaseFirstLetter} from "../helpers";
import pluralize from "pluralize";
import {getFuncParams} from "../views/service-creator";
const INIT_VALUE = {
"blob": "''",
"boolean": "false",
"date": "''",
"dateTIME": "''",
"number": "0",
"string": "''"
};
export function buildListView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
let hasDeleteEndpoint = false;
let hasItemEndpoint = false;
let hasUpdateEndpoint = false;
let hasCreateEndpoint = false;
let listEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
if (epd.type === 'delete') {
hasDeleteEndpoint = true;
}
if (epd.type === 'create') {
hasCreateEndpoint = true;
}
if (epd.type === 'update') {
hasUpdateEndpoint = true;
}
if (epd.type === 'item') {
hasItemEndpoint = true;
}
return epd.type === 'list' || epd.type === 'search';
});
let filterParams: Filter[] = [];
let endpointPromise = Promise.resolve();
for (let i in listEndpoints) {
if (i === '0') {
insertFEPropsCode(listEndpoints[i], outDir, systemDef);
}
if (listEndpoints[i].filters) {
filterParams = [...filterParams, ...listEndpoints[i].filters as Filter[]];
}
endpointPromise = endpointPromise.then(() => {
return insertFEListCode(listEndpoints[i], outDir, systemDef, hasDeleteEndpoint);
});
}
if (listEndpoints.length) {
endpointPromise = endpointPromise.then(() => {
return insertFEListMarkup(listEndpoints[0], outDir, hasDeleteEndpoint)
});
}
if (hasDeleteEndpoint && listEndpoints.length) {
endpointPromise = endpointPromise.then(() => {
return insertFEDeleteCode(listEndpoints[0], outDir);
});
}
// combine all duplicate filterParams
// @ts-ignore
let filterSet: Filter[] = makeSet<Filter>(filterParams, 'param');
// add filterParams to the markup as filter options
if (listEndpoints.length) {
endpointPromise = endpointPromise.then(() => {
return insertFEFiltersMarkup(listEndpoints[0], filterSet, outDir);
});
}
return endpointPromise;
}
function insertFEListCode(view: EndpointDef, outDir: string, systemDef: SystemDef, hasDeleteEndpoint: boolean) {
return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-component-functions`;
let fileLocation = path.join(outDir, `${view.component}List.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createListRequestFunc(view, systemDef, hasDeleteEndpoint);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function insertFEDeleteCode(view: EndpointDef, outDir: string) {
return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-component-functions`;
let fileLocation = path.join(outDir, `${view.component}List.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createFEDeleteCode(view);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function insertFEListMarkup(view: EndpointDef, outDir: string, hasDeleteEndpoint: boolean) {
return new Promise<void>((resolve) => {
const separator: string = `<!-- SYSTEM-BUILDER-item-props -->`;
let fileLocation = path.join(outDir, `${view.component}List.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createListMarkup(view, hasDeleteEndpoint);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function insertFEFiltersMarkup(view: EndpointDef, filters: Filter[], outDir: string) {
return new Promise<void>((resolve) => {
const separator: string = `<!-- SYSTEM-BUILDER-filter-options -->`;
let fileLocation = path.join(outDir, `${view.component}List.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createFilterMarkup(view, filters);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function insertFEPropsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-init-props`;
let fileLocation = path.join(outDir, `${view.component}List.vue`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createDetailsInitValues(view, systemDef);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}
function createFilterMarkup(view: EndpointDef, filters: Filter[]): string {
let out = ``;
let inputType: {[key: string]: string} = {
"blob": "text",
"boolean": "checkbox",
"date": "date",
"dateTIME": "datetime-local",
"number": "number",
"string": "text",
};
filters.forEach((f: Filter) => {
if (f.type) {
// base the filter markup on type
out = out + `<div>
<input type="${inputType[f.type] || 'text'}" placeholder="${f.param}" v-model="item.${f.param}"/>
</div>
`;
} else {
// default to a string filter
out = out + `<div>
<input type="text" placeholder="${f.param}" v-model="item.${f.param}"/>
</div>
`;
}
});
out = out + `<div>
<button @click="search${uppercaseFirstLetter(pluralize(view.component))}()">Search</button>
</div>
`;
return out;
}
function createListMarkup(view: EndpointDef, hasDeleteEndpoint: boolean): string {
let out = ``;
view.columns.forEach((column: ColumnRef) => {
out = out + `<div class="">{{ item.${column.name} }}</div>
`;
});
if (hasDeleteEndpoint) {
out = out + `<div class="red-text" @click="deleteItem(item.${view.component}_id)">delete</div>
`;
}
return out;
}
function createListRequestFunc(view: EndpointDef, systemDef: SystemDef, hasDeleteEndpoint: boolean) {
let isSearch: boolean = view.type === 'search';
let params: string[] = getFuncParams(view);
params.push('0'); // offset
params.push('1000'); // limit
let out = `private ${isSearch ? 'search' : 'get'}${uppercaseFirstLetter(pluralize(view.component))}() {
this.loading = true;
this.errorMessage = '';
${view.component}Service.${isSearch ? 'search' : 'get'}${uppercaseFirstLetter(pluralize(view.component))}(${params.join(', ')}).then((listItems: ${view.component}Config[]) => {
// success
this.loading = false;
this.errorMessage = '';
this.items = listItems;
}).catch(this.handleError.bind(this));
}
`;
return out;
}
function createFEDeleteCode(view: EndpointDef) {
let out = `private deleteItem(itemKey: string) {
this.loading = true;
this.errorMessage = '';
${view.component}Service.delete${uppercaseFirstLetter(pluralize(view.component))}(itemKey).then(() => {
this.loading = false;
this.errorMessage = '';
return this.get${uppercaseFirstLetter(pluralize(view.component))}();
}).catch(this.handleError.bind(this));
}
`;
return out;
}

View File

@ -0,0 +1,25 @@
import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService";
import path from "path";
import fs from "fs";
import {createDetailsTypeValues} from "../helpers";
export function buildTypesView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
if (component.endpoints.length) {
return insertFEDetailsCode(component.endpoints[0], outDir, systemDef);
} else {
return Promise.resolve();
}
}
function insertFEDetailsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-init-props`;
let fileLocation = path.join(outDir, `${view.component}Types.ts`)
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
let parts = initServiceFile.split(separator);
parts[0] = parts[0] + createDetailsTypeValues(view, systemDef);
let newServiceFile = parts.join(separator);
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
resolve();
});
}

View File

@ -1,7 +1,26 @@
import fs from "fs"; import fs from "fs";
import {EndpointDef} from "./systemGenService"; import {ColumnDef, ColumnRef, EndpointDef, Filter, SystemDef, TableDef} from "./systemGenService";
import path from "path"; import path from "path";
import {createServiceFunc} from "./views/service-creator"; import {createServiceFunc} from "./views/service-creator";
import pluralize from "pluralize";
const INIT_VALUE = {
"blob": "''",
"boolean": "false",
"date": "''",
"dateTIME": "''",
"number": "0",
"string": "''"
};
const TYPE_VALUE = {
"blob": "string",
"boolean": "boolean",
"date": "string",
"dateTIME": "string",
"number": "number",
"string": "string",
};
function uppercaseFirstLetter(input: string) { function uppercaseFirstLetter(input: string) {
return input.charAt(0).toUpperCase() + input.slice(1); return input.charAt(0).toUpperCase() + input.slice(1);
@ -15,16 +34,32 @@ function initializeComponentFile(sourceFile: string, destinationFile: string, co
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
fs.rename(sourceFile, destinationFile, (err: any) => { fs.rename(sourceFile, destinationFile, (err: any) => {
let fileContents = fs.readFileSync(destinationFile, 'utf8'); let fileContents = fs.readFileSync(destinationFile, 'utf8');
var uppercaseFirstLetterComponentName = uppercaseFirstLetter(component); let uppercaseFirstLetterComponentName = uppercaseFirstLetter(component);
var lowercaseFirstLetterComponentName = lowercaseFirstLetter(component); let lowercaseFirstLetterComponentName = lowercaseFirstLetter(component);
let lcPluralComponentName = lowercaseFirstLetter(pluralize(component));
let ucPluralComponentName = uppercaseFirstLetter(pluralize(component));
let newFileContents = fileContents.split('{{Component}}').join(uppercaseFirstLetterComponentName); let newFileContents = fileContents.split('{{Component}}').join(uppercaseFirstLetterComponentName);
newFileContents = newFileContents.split('{{component}}').join(lowercaseFirstLetterComponentName); newFileContents = newFileContents.split('{{component}}').join(lowercaseFirstLetterComponentName);
newFileContents = newFileContents.split('{{components}}').join(lcPluralComponentName);
newFileContents = newFileContents.split('{{Components}}').join(ucPluralComponentName);
fs.writeFileSync(destinationFile, newFileContents, 'utf8'); fs.writeFileSync(destinationFile, newFileContents, 'utf8');
resolve(); resolve();
}); });
}) })
} }
function removeTemplateFiles(destinationFolder: string) {
return new Promise<void>((resolve, reject) => {
fs.rmdir(destinationFolder, (err) => {
if (err) {
reject();
} else {
resolve();
}
});
});
}
function insertServiceCode(view: EndpointDef, outDir: string): Promise<void> { function insertServiceCode(view: EndpointDef, outDir: string): Promise<void> {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
const separator: string = `// SYSTEM-BUILDER-${view.component}-service`; const separator: string = `// SYSTEM-BUILDER-${view.component}-service`;
@ -38,9 +73,70 @@ function insertServiceCode(view: EndpointDef, outDir: string): Promise<void> {
}); });
} }
export { function findColumn(col: ColumnRef, systemDef: SystemDef): ColumnDef | undefined {
uppercaseFirstLetter, let colDef: ColumnDef | undefined;
lowercaseFirstLetter, let table: TableDef | undefined = systemDef.storage.tables.find((td: TableDef) => {
initializeComponentFile, return td.name === col.table;
insertServiceCode });
if (table) {
colDef = table.columns.find((cd: ColumnDef) => {
return cd.name === col.name;
});
}
return colDef;
}
function makeSet<T extends {[id: string]: string}>(list: T[], id: string): T[] {
let typeObject: {[key: string]: T} = {};
for (let i in list) {
if (!typeObject[list[i][id]]) {
typeObject[list[i][id]] = list[i];
}
}
let typeSet: T[] = [];
for (let key in typeObject) {
typeSet.push(typeObject[key]);
}
return typeSet;
}
function createDetailsInitValues(view: EndpointDef, systemDef: SystemDef) {
let out = `${view.component}_id: '',
`;
view.columns.forEach((column: ColumnRef) => {
let colDef: ColumnDef | undefined = findColumn(column, systemDef);
if (colDef) {
out = out + `${colDef.name}: ${INIT_VALUE[colDef.type]},
`;
}
});
return out;
}
function createDetailsTypeValues(view: EndpointDef, systemDef: SystemDef) {
let out = ``;
view.columns.forEach((column: ColumnRef) => {
let colDef: ColumnDef | undefined = findColumn(column, systemDef);
if (colDef) {
out = out + `${colDef.name}: ${TYPE_VALUE[colDef.type]};
`;
}
});
return out;
}
export {
createDetailsInitValues,
createDetailsTypeValues,
findColumn,
initializeComponentFile,
insertServiceCode,
lowercaseFirstLetter,
makeSet,
removeTemplateFiles,
uppercaseFirstLetter,
} }

View File

@ -3,6 +3,7 @@ const ncp = require('ncp').ncp;
import { createDatabase, writeMigrationsToFile } from './database/database-creator'; import { createDatabase, writeMigrationsToFile } from './database/database-creator';
import { createViews } from './views/views-creator'; import { createViews } from './views/views-creator';
import {createFrontend} from "./frontend-services/fe-service-creator"; import {createFrontend} from "./frontend-services/fe-service-creator";
import {removeTemplateFiles} from "./helpers";
class SystemGenService { class SystemGenService {
@ -44,6 +45,7 @@ class SystemGenService {
public cleanup() { public cleanup() {
// TODO: remove 'component/{{component}}' folder // TODO: remove 'component/{{component}}' folder
} }
} }
@ -54,7 +56,7 @@ interface SystemDef {
name: string; name: string;
storage: StorageDef; storage: StorageDef;
components: ComponentDef[]; components: ComponentDef[];
// TODO: add Views, ACLs, Behaviors, UX // TODO: add ACLs, Behaviors, UX
} }
interface StorageDef { interface StorageDef {