Compare commits
10 Commits
0026d7ace3
...
dcd3c0a95b
Author | SHA1 | Date | |
---|---|---|---|
dcd3c0a95b | |||
9253a84d7e | |||
7514dc03da | |||
7b18b0842a | |||
9869125965 | |||
be789ae882 | |||
87312fbc86 | |||
8e8c587c85 | |||
17deed412a | |||
c176de3297 |
@ -0,0 +1,31 @@
|
|||||||
|
DEBUG=frame:*
|
||||||
|
|
||||||
|
OAUTH2_HOST=https://p01.bergx.io
|
||||||
|
WELL_KNOWN=https://p01.bergx.io/.well-known/openid-configuration
|
||||||
|
AUTHORIZATION_URL=https://p01.bergx.io/oauth/dialog/authorize
|
||||||
|
TOKEN_URL=https://p01.bergx.io/oauth/token
|
||||||
|
CLIENT_ID={{oauth-client-id}}
|
||||||
|
CLIENT_SECRET={{oauth-client-secret}}
|
||||||
|
|
||||||
|
# Update this to be your host name
|
||||||
|
CALLBACK_URL={{your-host-name}}/auth/callback
|
||||||
|
|
||||||
|
BX_CLIENT_ID={{bergx-client-id}}
|
||||||
|
BX_CLIENT_SECRET={{bergx-client-secret}}
|
||||||
|
|
||||||
|
|
||||||
|
# DB stuff
|
||||||
|
DB_HOST={{db-host}}
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USER={{db-user}}
|
||||||
|
DB_PASSWORD={db-user-password}}
|
||||||
|
DB_NAME={{db-name}}
|
||||||
|
|
||||||
|
# Session stuff
|
||||||
|
DOMAIN={{your-host-name}}
|
||||||
|
DB_HOST={{session-db-host}}
|
||||||
|
DB_PORT=3306
|
||||||
|
SESSION_DB_USER={{session-db-user}}
|
||||||
|
SESSION_DB_PASSWORD={{session-db-password}}
|
||||||
|
SESSION_DB_NAME={{session-db-name}}
|
||||||
|
SESSION_SECRET={{session-db-user-password
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"dist": "yarn build && cp -R ./build ./dist/ && cp -R ./node_modules ./dist/"
|
"dist": "yarn build && cp -R ./build ./dist/ && cp -R ./node_modules ./dist/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@bergx/bergx-sdk": "^1.0.1",
|
||||||
"@types/node": "^14.0.13",
|
"@types/node": "^14.0.13",
|
||||||
"cookie-parser": "~1.4.4",
|
"cookie-parser": "~1.4.4",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import BergxSDK, { TryResponse } from 'bergx-sdk';
|
import BergxSDK, { TryResponse } from '@bergx/bergx-sdk';
|
||||||
import { userService } from '../users/userService';
|
import { userService } from '../users/userService';
|
||||||
import { DBUser } from '../users/usersMapper';
|
import { DBUser } from '../users/usersMapper';
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import * as passport from 'passport';
|
import * as passport from 'passport';
|
||||||
|
import { {{component}}Service } from './{{component}}Service';
|
||||||
|
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
|
|
||||||
const debug = require('debug')('frame:{{component}}Routes');
|
const debug = require('debug')('frame:{{component}}Routes');
|
||||||
|
|
||||||
// SYSTEM-BUILDER-{{component}}-routes
|
// SYSTEM-BUILDER-{{component}}-routes
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
@ -7,7 +7,6 @@ class {{Component}}Service {
|
|||||||
public initialize() {
|
public initialize() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SYSTEM-BUILDER-{{component}}-service
|
// SYSTEM-BUILDER-{{component}}-service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
139
frame/yarn.lock
@ -2,6 +2,34 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@bergx/bergx-sdk@^1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@bergx/bergx-sdk/-/bergx-sdk-1.0.1.tgz#c7097caf3233ddfecff2453216822b27f01c31d6"
|
||||||
|
integrity sha512-7xxB2g3TgBt0Wmf5CPkCnwHFCf+b0mQmJf5lMW3yi1StAZR/DcLxsRs/6TP/npg+x64VYaUeMIiKL6AqgRpMPg==
|
||||||
|
dependencies:
|
||||||
|
"@types/debug" "^4.1.5"
|
||||||
|
"@types/jsonwebtoken" "^8.3.8"
|
||||||
|
cross-fetch "^3.0.5"
|
||||||
|
debug "^4.1.1"
|
||||||
|
jsonwebtoken "^8.5.1"
|
||||||
|
|
||||||
|
"@types/debug@^4.1.5":
|
||||||
|
version "4.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.6.tgz#0b7018723084918a865eff99249c490505df2163"
|
||||||
|
integrity sha512-7fDOJFA/x8B+sO1901BmHlf5dE1cxBU8mRXj8QOEDnn16hhGJv/IHxJtZhvsabZsIMn0eLIyeOKAeqSNJJYTpA==
|
||||||
|
|
||||||
|
"@types/jsonwebtoken@^8.3.8":
|
||||||
|
version "8.5.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz#50ccaf0aa6f5d7b9956e70fe323b76e582991913"
|
||||||
|
integrity sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/node@*":
|
||||||
|
version "16.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.2.tgz#655432817f83b51ac869c2d51dd8305fb8342e16"
|
||||||
|
integrity sha512-jJs9ErFLP403I+hMLGnqDRWT0RYKSvArxuBVh2veudHV7ifEC1WAmjJADacZ7mRbA2nWgHtn8xyECMAot0SkAw==
|
||||||
|
|
||||||
"@types/node@^14.0.13":
|
"@types/node@^14.0.13":
|
||||||
version "14.0.13"
|
version "14.0.13"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.13.tgz#ee1128e881b874c371374c1f72201893616417c9"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.13.tgz#ee1128e881b874c371374c1f72201893616417c9"
|
||||||
@ -58,6 +86,11 @@ bowser@2.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.9.0.tgz#3bed854233b419b9a7422d9ee3e85504373821c9"
|
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.9.0.tgz#3bed854233b419b9a7422d9ee3e85504373821c9"
|
||||||
integrity sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==
|
integrity sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==
|
||||||
|
|
||||||
|
buffer-equal-constant-time@1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||||
|
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||||
|
|
||||||
bytes@3.0.0:
|
bytes@3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||||
@ -119,6 +152,13 @@ cors@^2.8.5:
|
|||||||
object-assign "^4"
|
object-assign "^4"
|
||||||
vary "^1"
|
vary "^1"
|
||||||
|
|
||||||
|
cross-fetch@^3.0.5:
|
||||||
|
version "3.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
|
||||||
|
integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==
|
||||||
|
dependencies:
|
||||||
|
node-fetch "2.6.1"
|
||||||
|
|
||||||
dasherize@2.0.0:
|
dasherize@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/dasherize/-/dasherize-2.0.0.tgz#6d809c9cd0cf7bb8952d80fc84fa13d47ddb1308"
|
resolved "https://registry.yarnpkg.com/dasherize/-/dasherize-2.0.0.tgz#6d809c9cd0cf7bb8952d80fc84fa13d47ddb1308"
|
||||||
@ -138,6 +178,13 @@ debug@4.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
|
debug@^4.1.1:
|
||||||
|
version "4.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||||
|
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
depd@2.0.0, depd@~2.0.0:
|
depd@2.0.0, depd@~2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||||
@ -168,6 +215,13 @@ dotenv@^8.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||||
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
||||||
|
|
||||||
|
ecdsa-sig-formatter@1.0.11:
|
||||||
|
version "1.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||||
|
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
@ -400,6 +454,74 @@ isarray@~1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||||
|
|
||||||
|
jsonwebtoken@^8.5.1:
|
||||||
|
version "8.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||||
|
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||||
|
dependencies:
|
||||||
|
jws "^3.2.2"
|
||||||
|
lodash.includes "^4.3.0"
|
||||||
|
lodash.isboolean "^3.0.3"
|
||||||
|
lodash.isinteger "^4.0.4"
|
||||||
|
lodash.isnumber "^3.0.3"
|
||||||
|
lodash.isplainobject "^4.0.6"
|
||||||
|
lodash.isstring "^4.0.1"
|
||||||
|
lodash.once "^4.0.0"
|
||||||
|
ms "^2.1.1"
|
||||||
|
semver "^5.6.0"
|
||||||
|
|
||||||
|
jwa@^1.4.1:
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||||
|
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||||
|
dependencies:
|
||||||
|
buffer-equal-constant-time "1.0.1"
|
||||||
|
ecdsa-sig-formatter "1.0.11"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
jws@^3.2.2:
|
||||||
|
version "3.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||||
|
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||||
|
dependencies:
|
||||||
|
jwa "^1.4.1"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
lodash.includes@^4.3.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||||
|
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||||
|
|
||||||
|
lodash.isboolean@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||||
|
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||||
|
|
||||||
|
lodash.isinteger@^4.0.4:
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||||
|
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||||
|
|
||||||
|
lodash.isnumber@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||||
|
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||||
|
|
||||||
|
lodash.isplainobject@^4.0.6:
|
||||||
|
version "4.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||||
|
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||||
|
|
||||||
|
lodash.isstring@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||||
|
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||||
|
|
||||||
|
lodash.once@^4.0.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||||
|
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||||
|
|
||||||
media-typer@0.3.0:
|
media-typer@0.3.0:
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||||
@ -448,7 +570,7 @@ ms@2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||||
|
|
||||||
ms@^2.1.1:
|
ms@2.1.2, ms@^2.1.1:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
@ -473,6 +595,11 @@ nocache@2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f"
|
resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f"
|
||||||
integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==
|
integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==
|
||||||
|
|
||||||
|
node-fetch@2.6.1:
|
||||||
|
version "2.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
||||||
oauth@0.9.x:
|
oauth@0.9.x:
|
||||||
version "0.9.15"
|
version "0.9.15"
|
||||||
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
|
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
|
||||||
@ -618,11 +745,21 @@ safe-buffer@5.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||||
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
||||||
|
|
||||||
|
safe-buffer@^5.0.1:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
|
|
||||||
"safer-buffer@>= 2.1.2 < 3":
|
"safer-buffer@>= 2.1.2 < 3":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
|
semver@^5.6.0:
|
||||||
|
version "5.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
|
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||||
|
|
||||||
send@0.16.2:
|
send@0.16.2:
|
||||||
version "0.16.2"
|
version "0.16.2"
|
||||||
resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
|
resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
|
||||||
|
23
frontend-frame/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
24
frontend-frame/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# frontend-frame
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
yarn serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
5
frontend-frame/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
51
frontend-frame/package.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend-frame",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"register-service-worker": "^1.7.1",
|
||||||
|
"vue": "^2.6.11",
|
||||||
|
"vue-class-component": "^7.2.3",
|
||||||
|
"vue-property-decorator": "^9.1.2",
|
||||||
|
"vue-router": "^3.5.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||||
|
"@typescript-eslint/parser": "^4.18.0",
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-pwa": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||||
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"@vue/eslint-config-typescript": "^7.0.0",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"typescript": "~4.1.5",
|
||||||
|
"vue-template-compiler": "^2.6.11"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended",
|
||||||
|
"@vue/typescript/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead"
|
||||||
|
]
|
||||||
|
}
|
BIN
frontend-frame/public/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
frontend-frame/public/img/icons/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
frontend-frame/public/img/icons/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 22 KiB |
BIN
frontend-frame/public/img/icons/apple-touch-icon-120x120.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
frontend-frame/public/img/icons/apple-touch-icon-152x152.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
frontend-frame/public/img/icons/apple-touch-icon-180x180.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
frontend-frame/public/img/icons/apple-touch-icon-60x60.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
frontend-frame/public/img/icons/apple-touch-icon-76x76.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
frontend-frame/public/img/icons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
frontend-frame/public/img/icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 799 B |
BIN
frontend-frame/public/img/icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
frontend-frame/public/img/icons/msapplication-icon-144x144.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
frontend-frame/public/img/icons/mstile-150x150.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
3
frontend-frame/public/img/icons/safari-pinned-tab.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.00251 14.9297L0 1.07422H6.14651L8.00251 4.27503L9.84583 1.07422H16L8.00251 14.9297Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 215 B |
17
frontend-frame/public/index.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
2
frontend-frame/public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
31
frontend-frame/src/App.vue
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
|
||||||
|
<!-- SYSTEM-BUILDER-list-components-html -->
|
||||||
|
|
||||||
|
<div class="routes-container">
|
||||||
|
<router-view/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
import HelloWorld from './components/HelloWorld.vue';
|
||||||
|
// SYSTEM-BUILDER-list-components-imports
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class App extends Vue {}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
BIN
frontend-frame/src/assets/logo.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
60
frontend-frame/src/components/HelloWorld.vue
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<div class="hello">
|
||||||
|
<h1>{{ msg }}</h1>
|
||||||
|
<p>
|
||||||
|
For a guide and recipes on how to configure / customize this project,<br>
|
||||||
|
check out the
|
||||||
|
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||||
|
</p>
|
||||||
|
<h3>Installed CLI Plugins</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa" target="_blank" rel="noopener">pwa</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>Essential Links</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||||
|
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||||
|
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||||
|
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||||
|
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>Ecosystem</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||||
|
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||||
|
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class HelloWorld extends Vue {
|
||||||
|
@Prop() private msg!: string;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
h3 {
|
||||||
|
margin: 40px 0 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #42b983;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<div class="{{component}}-details">
|
||||||
|
<!-- SYSTEM-BUILDER-property-row -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { {{Component}}Config } from './{{component}}Types';
|
||||||
|
import { {{component}}Service } from './{{component}}Service';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class {{Component}}Editor extends Vue {
|
||||||
|
@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>
|
||||||
|
|
||||||
|
<style scoped lang="css">
|
||||||
|
.errorMessage {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<div class="{{component}}-editor">
|
||||||
|
|
||||||
|
<!-- SYSTEM-BUILDER-input-row -->
|
||||||
|
|
||||||
|
<div class="errorMessage" v-if="errorMessage != ''">{{errorMessage}}</div>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button class="btn" @click="close()">Cancel</button>
|
||||||
|
<button class="btn btn-secondary" :disabled="!isComplete" @click="isComplete && save{{Component}}()">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { {{component}}Service } from './{{component}}Service';
|
||||||
|
import { {{Component}}Config } from './{{component}}Types';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class {{Component}}Editor extends Vue {
|
||||||
|
@Prop() private {{component}}_id!: string;
|
||||||
|
private {{component}}: {{Component}}Config = {
|
||||||
|
// SYSTEM-BUILDER-init-props
|
||||||
|
};
|
||||||
|
private loading: boolean = false;
|
||||||
|
private errorMessage: string = '';
|
||||||
|
|
||||||
|
get isComplete() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private close() {
|
||||||
|
this.$emit('close-editor');
|
||||||
|
}
|
||||||
|
|
||||||
|
private mounted() {
|
||||||
|
this.loadDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadDetails() {
|
||||||
|
this.loading = true;
|
||||||
|
this.errorMessage = '';
|
||||||
|
{{component}}Service.get{{Component}}(this.{{component}}_id).then((res: {{Component}}Config) => {
|
||||||
|
this.loading = false;
|
||||||
|
this.errorMessage = '';
|
||||||
|
this.{{component}} = res;
|
||||||
|
}).catch(this.handleError.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private save{{Component}}() {
|
||||||
|
this.loading = true;
|
||||||
|
this.errorMessage = '';
|
||||||
|
// either create or update...
|
||||||
|
if (this.{{component}}.{{component}}_id === '') {
|
||||||
|
{{component}}Service.create{{Component}}(this.{{component}}).then(() => {
|
||||||
|
// success
|
||||||
|
this.loading = false;
|
||||||
|
this.errorMessage = '';
|
||||||
|
this.close();
|
||||||
|
}).catch(this.handleError.bind(this));
|
||||||
|
} else {
|
||||||
|
{{component}}Service.update{{Component}}(this.{{component}}.{{component}}_id, this.{{component}}).then(() => {
|
||||||
|
// success
|
||||||
|
this.loading = false;
|
||||||
|
this.errorMessage = '';
|
||||||
|
this.close();
|
||||||
|
}).catch(this.handleError.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(err: any) {
|
||||||
|
// error
|
||||||
|
this.loading = false;
|
||||||
|
this.errorMessage = err.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="css">
|
||||||
|
.errorMessage {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div class="{{component}}-editor">
|
||||||
|
<div class="filter-row">
|
||||||
|
<!-- SYSTEM-BUILDER-filter-options -->
|
||||||
|
</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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { {{component}}Service } from './{{component}}Service';
|
||||||
|
import { {{Component}}Config } from './{{component}}Types';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class {{Component}}Editor extends Vue {
|
||||||
|
private items: {{Component}}Config[] = [];
|
||||||
|
private loading: boolean = false;
|
||||||
|
private errorMessage: string = '';
|
||||||
|
private filterItems: {{Component}}Config = {
|
||||||
|
// SYSTEM-BUILDER-init-props
|
||||||
|
};
|
||||||
|
// SYSTEM-BUILDER-component-variables
|
||||||
|
|
||||||
|
// SYSTEM-BUILDER-component-functions
|
||||||
|
|
||||||
|
private mounted() {
|
||||||
|
this.get{{Components}}();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add a search function if there is an endpoint with type 'search'
|
||||||
|
|
||||||
|
private handleError(err: any) {
|
||||||
|
// error
|
||||||
|
this.loading = false;
|
||||||
|
this.errorMessage = err.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="css">
|
||||||
|
.red-text {
|
||||||
|
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>
|
@ -0,0 +1,14 @@
|
|||||||
|
import { del, get, post, put } from '@/services/fetch';
|
||||||
|
import { {{Component}}Config } from './{{component}}Types';
|
||||||
|
|
||||||
|
class {{Component}}Service {
|
||||||
|
// SYSTEM-BUILDER-service-variables
|
||||||
|
// SYSTEM-BUILDER-service-functions
|
||||||
|
}
|
||||||
|
|
||||||
|
let {{component}}Service = new {{Component}}Service();
|
||||||
|
|
||||||
|
export {
|
||||||
|
{{component}}Service,
|
||||||
|
{{Component}}Service
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
// SYSTEM-BUILDER-{{component}}-interfaces
|
||||||
|
|
||||||
|
// e.g.
|
||||||
|
interface {{Component}}Config {
|
||||||
|
{{component}}_id: string;
|
||||||
|
// SYSTEM-BUILDER-init-props
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
{{Component}}Config,
|
||||||
|
// SYSTEM-BUILDER-{{component}}-exports
|
||||||
|
};
|
11
frontend-frame/src/main.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router';
|
||||||
|
import './registerServiceWorker'
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
render: h => h(App),
|
||||||
|
}).$mount('#app')
|
32
frontend-frame/src/registerServiceWorker.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
import { register } from 'register-service-worker'
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||||
|
ready () {
|
||||||
|
console.log(
|
||||||
|
'App is being served from cache by a service worker.\n' +
|
||||||
|
'For more details, visit https://goo.gl/AFskqB'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
registered () {
|
||||||
|
console.log('Service worker has been registered.')
|
||||||
|
},
|
||||||
|
cached () {
|
||||||
|
console.log('Content has been cached for offline use.')
|
||||||
|
},
|
||||||
|
updatefound () {
|
||||||
|
console.log('New content is downloading.')
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
console.log('New content is available; please refresh.')
|
||||||
|
},
|
||||||
|
offline () {
|
||||||
|
console.log('No internet connection found. App is running in offline mode.')
|
||||||
|
},
|
||||||
|
error (error) {
|
||||||
|
console.error('Error during service worker registration:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
12
frontend-frame/src/router.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import Router from 'vue-router';
|
||||||
|
|
||||||
|
Vue.use(Router);
|
||||||
|
|
||||||
|
export default new Router({
|
||||||
|
mode: 'history',
|
||||||
|
base: process.env.BASE_URL,
|
||||||
|
routes: [
|
||||||
|
// SYSTE-BUILDER-routes
|
||||||
|
]
|
||||||
|
});
|
100
frontend-frame/src/services/fetch.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
declare var window: any;
|
||||||
|
const defaultBasePath = '';
|
||||||
|
|
||||||
|
export function fetchAsync(method: 'GET' | 'POST' | 'DELETE' | 'PUT', url: string, body?: any) {
|
||||||
|
const headers = { 'Content-Type': 'application/json; charset=utf-8' };
|
||||||
|
// if (access_token) headers['Authorization'] = `Token ${access_token}`;
|
||||||
|
return window.fetch(`${defaultBasePath}${url}`, {
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
body: body && JSON.stringify(body)
|
||||||
|
}).then((response: any) => {
|
||||||
|
if (response.status === 401) {
|
||||||
|
window.location.href = '/auth/login';
|
||||||
|
// throw new Error('401');
|
||||||
|
}
|
||||||
|
if (response.status === 403 && url === '/api/v1/organization/') {
|
||||||
|
// this indicates a user that is not supposed to be in bx-console
|
||||||
|
window.location.href = '/auth/logout';
|
||||||
|
}
|
||||||
|
const result = response.json();
|
||||||
|
if (!response.ok) throw result;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get<T>(url: string): Promise<T> {
|
||||||
|
return fetchAsync('GET', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function post<T>(url: string, body?: any): Promise<T> {
|
||||||
|
return fetchAsync('POST', url, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postWithFile<T>(url: string, body?: any): Promise<T> {
|
||||||
|
return fetchWithFile('POST', url, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putWithFile<T>(url: string, body?: any): Promise<T> {
|
||||||
|
return fetchWithFile('PUT', url, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchWithFile<T>(method: string, url: string, body?: any): Promise<T> {
|
||||||
|
return window.fetch(`${defaultBasePath}${url}`, {
|
||||||
|
method: method,
|
||||||
|
body: body
|
||||||
|
}).then((response: any) => {
|
||||||
|
if (response.status === 401) {
|
||||||
|
window.location.href = '/auth/login';
|
||||||
|
// throw new Error('401');
|
||||||
|
}
|
||||||
|
const result = response.json();
|
||||||
|
if (!response.ok) throw result;
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function del(url: string) {
|
||||||
|
return fetchAsync('DELETE', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function put(url: string, body?: any) {
|
||||||
|
return fetchAsync('PUT', url, body);
|
||||||
|
}
|
||||||
|
export function toQueryString(obj: any) {
|
||||||
|
const parts = [];
|
||||||
|
for (var i in obj) {
|
||||||
|
if (obj.hasOwnProperty(i)) {
|
||||||
|
parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parts.join("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeObject<T>(form: any) {
|
||||||
|
let obj: {[key: string]: any} = {};
|
||||||
|
if (typeof form == 'object' && form.nodeName == "FORM") {
|
||||||
|
for (let i = 0; i < form.elements.length; i++) {
|
||||||
|
const field = form.elements[i];
|
||||||
|
if (field.name
|
||||||
|
&& field.type != 'file'
|
||||||
|
&& field.type != 'reset'
|
||||||
|
&& field.type != 'submit'
|
||||||
|
&& field.type != 'button') {
|
||||||
|
if (field.type == 'select-multiple') {
|
||||||
|
obj[field.name] = '';
|
||||||
|
let tempvalue = '';
|
||||||
|
for (let j = 0; j < form.elements[i].options.length; j++) {
|
||||||
|
if (field.options[j].selected)
|
||||||
|
tempvalue += field.options[j].value + ';';
|
||||||
|
}
|
||||||
|
if (tempvalue.charAt(tempvalue.length - 1) === ';') obj[field.name] = tempvalue.substring(0, tempvalue.length - 1);
|
||||||
|
|
||||||
|
} else if ((field.type != 'checkbox' && field.type != 'radio') || field.checked) {
|
||||||
|
obj[field.name] = field.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj as T;
|
||||||
|
}
|
13
frontend-frame/src/shims-tsx.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Vue, { VNode } from 'vue'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace JSX {
|
||||||
|
// tslint:disable no-empty-interface
|
||||||
|
interface Element extends VNode {}
|
||||||
|
// tslint:disable no-empty-interface
|
||||||
|
interface ElementClass extends Vue {}
|
||||||
|
interface IntrinsicElements {
|
||||||
|
[elem: string]: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
frontend-frame/src/shims-vue.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
declare module '*.vue' {
|
||||||
|
import Vue from 'vue'
|
||||||
|
export default Vue
|
||||||
|
}
|
40
frontend-frame/tsconfig.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "esnext",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"importHelpers": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"types": [
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue",
|
||||||
|
"tests/**/*.ts",
|
||||||
|
"tests/**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
10
frontend-frame/vue.config.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module.exports = {
|
||||||
|
devServer: {
|
||||||
|
proxy: {
|
||||||
|
'/api/': {
|
||||||
|
target: 'http://localhost:3000',
|
||||||
|
changeOrigin: true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9203
frontend-frame/yarn.lock
Normal file
@ -7,7 +7,7 @@
|
|||||||
"pluralize": "^8.0.0"
|
"pluralize": "^8.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "rm -rf dist && tsc && cp -r frame dist/ && cd dist && node index.js"
|
"start": "rm -rf dist && tsc && cp -r frame dist/ && cp -r frontend-frame dist/ && cd dist && node index.js"
|
||||||
},
|
},
|
||||||
"name": "system-builder",
|
"name": "system-builder",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {
|
import {
|
||||||
SystemDef,
|
|
||||||
StorageDef,
|
StorageDef,
|
||||||
EndpointDef,
|
|
||||||
Order,
|
|
||||||
Filter,
|
|
||||||
TableDef,
|
TableDef,
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
BelongsToDef,
|
BelongsToDef,
|
||||||
ManyToManyDef
|
ManyToManyDef
|
||||||
} from '../systemGenService';
|
} from '../systemGenService';
|
||||||
|
|
||||||
let outDir = 'test';
|
|
||||||
|
|
||||||
export function createDatabase(storageDef: StorageDef) {
|
export function createDatabase(storageDef: StorageDef) {
|
||||||
let tableCreationQueries: string[] = [];
|
let tableCreationQueries: string[] = [];
|
||||||
let tableDeletionQueries: string[] = [];
|
let tableDeletionQueries: string[] = [];
|
||||||
|
@ -123,7 +123,8 @@ let def: SystemDef = {
|
|||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
param: 'list',
|
param: 'list_id',
|
||||||
|
type: 'string',
|
||||||
column: {
|
column: {
|
||||||
name: 'list_id',
|
name: 'list_id',
|
||||||
table: 'task'
|
table: 'task'
|
||||||
@ -133,6 +134,7 @@ let def: SystemDef = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
param: 'completed',
|
param: 'completed',
|
||||||
|
type: 'boolean',
|
||||||
column: {
|
column: {
|
||||||
name: 'completed',
|
name: 'completed',
|
||||||
table: 'task'
|
table: 'task'
|
||||||
@ -142,6 +144,80 @@ let def: SystemDef = {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'task',
|
||||||
|
table: 'task',
|
||||||
|
type: 'search',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'completed',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'completed_date',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
orderBy: [
|
||||||
|
{
|
||||||
|
column: {
|
||||||
|
name: 'modified',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
direction: 'desc'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
param: 'list_id',
|
||||||
|
type: 'string',
|
||||||
|
column: {
|
||||||
|
name: 'list_id',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
comparison: '=',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
param: 'completed',
|
||||||
|
type: 'boolean',
|
||||||
|
column: {
|
||||||
|
name: 'completed',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
comparison: '=',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
param: 'name',
|
||||||
|
type: 'string',
|
||||||
|
column: {
|
||||||
|
name: 'name',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
comparison: 'LIKE',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
param: 'description',
|
||||||
|
type: 'string',
|
||||||
|
column: {
|
||||||
|
name: 'description',
|
||||||
|
table: 'task'
|
||||||
|
},
|
||||||
|
comparison: 'LIKE',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'task',
|
component: 'task',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
@ -175,7 +251,8 @@ let def: SystemDef = {
|
|||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
param: 'list',
|
param: 'list_id',
|
||||||
|
type: 'string',
|
||||||
column: {
|
column: {
|
||||||
name: 'list_id',
|
name: 'list_id',
|
||||||
table: 'task'
|
table: 'task'
|
||||||
@ -185,6 +262,7 @@ let def: SystemDef = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
param: 'completed',
|
param: 'completed',
|
||||||
|
type: 'boolean',
|
||||||
column: {
|
column: {
|
||||||
name: 'completed',
|
name: 'completed',
|
||||||
table: 'task'
|
table: 'task'
|
||||||
@ -203,23 +281,27 @@ let def: SystemDef = {
|
|||||||
name: 'name',
|
name: 'name',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'name',
|
param: 'name',
|
||||||
|
type: 'string',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'list_id',
|
name: 'list_id',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'list',
|
param: 'list',
|
||||||
|
type: 'string',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'description'
|
param: 'description',
|
||||||
|
type: 'string'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'completed',
|
name: 'completed',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'completed'
|
param: 'completed',
|
||||||
|
type: 'boolean',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -232,21 +314,25 @@ let def: SystemDef = {
|
|||||||
name: 'name',
|
name: 'name',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'name',
|
param: 'name',
|
||||||
|
type: 'string'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'description'
|
param: 'description',
|
||||||
|
type: 'string'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'completed',
|
name: 'completed',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'completed'
|
param: 'completed',
|
||||||
|
type: 'boolean'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'completed_date',
|
name: 'completed_date',
|
||||||
table: 'task',
|
table: 'task',
|
||||||
param: 'completedDate'
|
param: 'completedDate',
|
||||||
|
type: 'string'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
@ -256,7 +342,8 @@ let def: SystemDef = {
|
|||||||
table: 'task'
|
table: 'task'
|
||||||
},
|
},
|
||||||
comparison: '=',
|
comparison: '=',
|
||||||
param: 'task',
|
param: 'task_id',
|
||||||
|
type: 'string',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -273,7 +360,8 @@ let def: SystemDef = {
|
|||||||
table: 'task'
|
table: 'task'
|
||||||
},
|
},
|
||||||
comparison: '=',
|
comparison: '=',
|
||||||
param: 'task',
|
param: 'task_id',
|
||||||
|
type: 'string',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -311,7 +399,8 @@ let def: SystemDef = {
|
|||||||
table: 'task'
|
table: 'task'
|
||||||
},
|
},
|
||||||
comparison: '=',
|
comparison: '=',
|
||||||
param: 'task',
|
param: 'task_id',
|
||||||
|
type: 'string',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -357,7 +446,8 @@ let def: SystemDef = {
|
|||||||
],
|
],
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
param: 'user',
|
param: 'user_id',
|
||||||
|
type: 'string',
|
||||||
column: {
|
column: {
|
||||||
name: 'user_id',
|
name: 'user_id',
|
||||||
table: 'list_user'
|
table: 'list_user'
|
||||||
|
29
src/frontend-services/app-view-builder.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
export function buildAppLinks(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
|
||||||
|
let listEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
|
||||||
|
return epd.type === 'list';
|
||||||
|
});
|
||||||
|
if (listEndpoints.length) {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
const separator: string = `<!-- SYSTEM-BUILDER-list-components-html -->`;
|
||||||
|
let fileLocation = path.join(process.cwd(), `frontend-${systemDef.name}`, 'src', 'App.vue');
|
||||||
|
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||||
|
let parts = initServiceFile.split(separator);
|
||||||
|
parts[0] = parts[0] + createLinksMarkup(component, systemDef);
|
||||||
|
let newServiceFile = parts.join(separator);
|
||||||
|
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLinksMarkup(component: ComponentDef, systemDef: SystemDef): string {
|
||||||
|
let out = `<router-link to="/${component.component}">${component.component} List</router-link><br/>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
61
src/frontend-services/details-view-builder.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
73
src/frontend-services/editor-view-builder.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
166
src/frontend-services/fe-service-creator.ts
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService";
|
||||||
|
import path from "path";
|
||||||
|
import {
|
||||||
|
componentDestination,
|
||||||
|
initializeComponentFile,
|
||||||
|
removeTemplateFiles,
|
||||||
|
removeTemplateFolder,
|
||||||
|
uppercaseFirstLetter
|
||||||
|
} from "../helpers";
|
||||||
|
import fs from "fs";
|
||||||
|
import {buildServiceFunctionName, getFuncParams} from "../views/service-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";
|
||||||
|
import {buildRoute} from "./route-view-builder";
|
||||||
|
import {buildAppLinks} from "./app-view-builder";
|
||||||
|
const ncp = require('ncp').ncp;
|
||||||
|
|
||||||
|
export function createFrontend(systemDef: SystemDef): Promise<void> {
|
||||||
|
let fePromises = [];
|
||||||
|
for (let i in systemDef.components) {
|
||||||
|
fePromises.push(createComponent(systemDef.components[i], systemDef).then(() => {
|
||||||
|
// build app router logic
|
||||||
|
return buildRoute(systemDef.components[i], componentDestination(systemDef.name, systemDef.components[i].component), systemDef);
|
||||||
|
}).then(() => {
|
||||||
|
// add a vue view and add it to App.vue
|
||||||
|
return buildAppLinks(systemDef.components[i], componentDestination(systemDef.name, systemDef.components[i].component), systemDef);
|
||||||
|
}).then(() => {
|
||||||
|
return removeTemplateFiles(componentDestination(systemDef.name, systemDef.components[i].component));
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
// TODO: after all is done clean up the template files
|
||||||
|
return Promise.all(fePromises).then((res: void[]) => {
|
||||||
|
return removeTemplateFolder(componentDestination(systemDef.name, '{{component}}')).catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function componentSource() {
|
||||||
|
return path.join(process.cwd(), 'frontend-frame', 'src', 'components', '{{component}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function createComponent(component: ComponentDef, systemDef: SystemDef) {
|
||||||
|
return new Promise<void>((componentResolve, componentReject) => {
|
||||||
|
ncp(componentSource(), componentDestination(systemDef.name, component.component), (err: any) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
componentReject();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
let componentDest: string = componentDestination(systemDef.name, component.component);
|
||||||
|
let servicePromise = initializeComponentFile(path.join(componentDest, '{{component}}Service.ets'), path.join(componentDest, `${component.component}Service.ts`), component.component, systemDef.name);
|
||||||
|
for (let i in component.endpoints) {
|
||||||
|
servicePromise = servicePromise.then(() => {
|
||||||
|
return insertFEServiceCode(component.endpoints[i], componentDest);
|
||||||
|
});
|
||||||
|
// 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) {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
const separator: string = `// SYSTEM-BUILDER-service-functions`;
|
||||||
|
let fileLocation = path.join(outDir, `${view.component}Service.ts`)
|
||||||
|
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||||
|
let parts = initServiceFile.split(separator);
|
||||||
|
parts[0] = parts[0] + createFEServiceFunc(view);
|
||||||
|
let newServiceFile = parts.join(separator);
|
||||||
|
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFEServiceFunc(view: EndpointDef): string {
|
||||||
|
let func: string = '';
|
||||||
|
let isListOrSearch: boolean = view.type === 'list' || view.type === 'search';
|
||||||
|
let funcName: string = buildServiceFunctionName(view);
|
||||||
|
let funcParams: string[] = getFuncParams(view, true);
|
||||||
|
let columnParams: string[] = view.columns?.filter(column => column.param).map(column => { return '\n ' + column.param + ': ' + column.param; });
|
||||||
|
let filterParams: string[] = view.filters?.filter(filter => filter.param).map(filter => { return '\n ' + filter.param + ': ' + filter.param; }) || [];
|
||||||
|
|
||||||
|
if (isListOrSearch) {
|
||||||
|
funcParams.push('offset: number');
|
||||||
|
funcParams.push('limit: number');
|
||||||
|
filterParams.push('\n offset: offset');
|
||||||
|
filterParams.push('\n limit: limit');
|
||||||
|
}
|
||||||
|
|
||||||
|
let isGetOrDelete = view.type === 'item' || view.type === 'delete';
|
||||||
|
let method = METHOD[view.type];
|
||||||
|
if (method === 'delete') {
|
||||||
|
method = 'del';
|
||||||
|
}
|
||||||
|
let addTypeDeclaration = method === 'get' || method === 'post';
|
||||||
|
let useList = view.type === 'list' || view.type === 'search';
|
||||||
|
let url = view.component + '/' + URL[view.type](view).replace(':' + view.component + '_id', '\' + ' + view.component + '_id');
|
||||||
|
let isUpdateOrCreate = view.type === 'update' || view.type === 'create';
|
||||||
|
|
||||||
|
func = `
|
||||||
|
public ${funcName}(${view.type === 'update' ? view.component + '_id: string, ': ''}${isUpdateOrCreate ? 'params: ' + uppercaseFirstLetter(view.component) + 'Config' : funcParams.join(', ')}) {`;
|
||||||
|
|
||||||
|
if (!isGetOrDelete && !isUpdateOrCreate) {
|
||||||
|
func = func + `
|
||||||
|
let params = {${columnParams}${columnParams.length ? ',' : ''}${filterParams}
|
||||||
|
};`;
|
||||||
|
}
|
||||||
|
func = func + `
|
||||||
|
return ${method}${addTypeDeclaration ? '<' + uppercaseFirstLetter(view.component) + 'Config' + (useList ? '[]' : '') + '>': ''}('/api/v1/${url}${isGetOrDelete ? '' : (view.type === 'update' ? ', params' : '\', params')});
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
return func;
|
||||||
|
}
|
228
src/frontend-services/list-view-builder.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add create component to sidebar
|
||||||
|
// TODO: add details component to sidebar
|
||||||
|
// TODO: if delete endpoint exists add delete button next to each row
|
||||||
|
|
||||||
|
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>
|
||||||
|
<label>${f.param}: </label>
|
||||||
|
<input type="${inputType[f.type] || 'text'}" placeholder="${f.param}" v-model="filterItems.${f.param}"/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
// default to a string filter
|
||||||
|
out = out + `<div>
|
||||||
|
<input type="text" placeholder="${f.param}" v-model="filterItems.${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 = params.map((param: string) => {
|
||||||
|
return 'this.filterItems.' + param;
|
||||||
|
});
|
||||||
|
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: ${uppercaseFirstLetter(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(view.component)}(itemKey).then(() => {
|
||||||
|
this.loading = false;
|
||||||
|
this.errorMessage = '';
|
||||||
|
return this.get${uppercaseFirstLetter(pluralize(view.component))}();
|
||||||
|
}).catch(this.handleError.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
33
src/frontend-services/route-view-builder.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
export function buildRoute(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
|
||||||
|
let listEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
|
||||||
|
return epd.type === 'list';
|
||||||
|
});
|
||||||
|
if (listEndpoints.length) {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
const separator: string = `// SYSTE-BUILDER-routes`;
|
||||||
|
let fileLocation = path.join(process.cwd(), `frontend-${systemDef.name}`, 'src', 'router.ts');
|
||||||
|
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||||
|
let parts = initServiceFile.split(separator);
|
||||||
|
parts[0] = parts[0] + createRouteCode(component, systemDef);
|
||||||
|
let newServiceFile = parts.join(separator);
|
||||||
|
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRouteCode(component: ComponentDef, systemDef: SystemDef): string {
|
||||||
|
let out = `{
|
||||||
|
path: '/${component.component}',
|
||||||
|
name: '${component.component}',
|
||||||
|
component: () => import(/* webpackChunkName: "${component.component}" */ './components/${component.component}/${component.component}List.vue')
|
||||||
|
},
|
||||||
|
`;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
25
src/frontend-services/types-view-builder.ts
Normal 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();
|
||||||
|
});
|
||||||
|
}
|
209
src/helpers.ts
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import {
|
||||||
|
BelongsToDef,
|
||||||
|
ColumnDef,
|
||||||
|
ColumnRef,
|
||||||
|
EndpointDef,
|
||||||
|
Filter,
|
||||||
|
ManyToManyDef,
|
||||||
|
SystemDef,
|
||||||
|
TableDef
|
||||||
|
} from "./systemGenService";
|
||||||
|
import path from "path";
|
||||||
|
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) {
|
||||||
|
return input.charAt(0).toUpperCase() + input.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lowercaseFirstLetter(input: string) {
|
||||||
|
return input.charAt(0).toLowerCase() + input.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
let uppercaseFirstLetterComponentName = uppercaseFirstLetter(component);
|
||||||
|
let lowercaseFirstLetterComponentName = lowercaseFirstLetter(component);
|
||||||
|
let lcPluralComponentName = lowercaseFirstLetter(pluralize(component));
|
||||||
|
let ucPluralComponentName = uppercaseFirstLetter(pluralize(component));
|
||||||
|
let newFileContents = fileContents.split('{{Component}}').join(uppercaseFirstLetterComponentName);
|
||||||
|
newFileContents = newFileContents.split('{{component}}').join(lowercaseFirstLetterComponentName);
|
||||||
|
newFileContents = newFileContents.split('{{components}}').join(lcPluralComponentName);
|
||||||
|
newFileContents = newFileContents.split('{{Components}}').join(ucPluralComponentName);
|
||||||
|
fs.writeFileSync(destinationFile, newFileContents, 'utf8');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTemplateFolder(destinationFolder: string) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
fs.rmdir(destinationFolder, { recursive: true }, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTemplateFiles(destinationFolder: string) {
|
||||||
|
let regex = /\{\{component\}\}.*$/
|
||||||
|
fs.readdirSync(destinationFolder)
|
||||||
|
.filter(f => regex.test(f))
|
||||||
|
.map(f => fs.unlinkSync(path.join(destinationFolder, f)));
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertServiceCode(view: EndpointDef, outDir: string): Promise<void> {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
const separator: string = `// SYSTEM-BUILDER-${view.component}-service`;
|
||||||
|
let fileLocation = path.join(process.cwd(), outDir, 'src', 'components', view.component, `${view.component}Service.ts`);
|
||||||
|
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||||
|
let parts = initServiceFile.split(separator);
|
||||||
|
parts[0] = parts[0] + createServiceFunc(view);
|
||||||
|
let newServiceFile = parts.join(separator);
|
||||||
|
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findColumn(col: ColumnRef, systemDef: SystemDef): ColumnDef | undefined {
|
||||||
|
let colDef: ColumnDef | undefined;
|
||||||
|
let table: TableDef | undefined = systemDef.storage.tables.find((td: TableDef) => {
|
||||||
|
return td.name === col.table;
|
||||||
|
});
|
||||||
|
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]},
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// TODO: get relations to add as properties
|
||||||
|
let storageTable: TableDef | undefined = systemDef.storage.tables.find((table: TableDef) => {
|
||||||
|
return table.name === view.component;
|
||||||
|
});
|
||||||
|
if (storageTable) {
|
||||||
|
storageTable.relations.forEach((rel: BelongsToDef) => {
|
||||||
|
out = out + `${rel.table}_id: '',
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
systemDef.storage.relations.forEach((rel:ManyToManyDef) => {
|
||||||
|
if (rel.right === view.component) {
|
||||||
|
out = out + `${rel.left}_id: '',
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
if (rel.left === view.component) {
|
||||||
|
out = out + `${rel.right}_id: '',
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDetailsTypeValues(view: EndpointDef, systemDef: SystemDef) {
|
||||||
|
let out = ``;
|
||||||
|
|
||||||
|
let storageTable = systemDef.storage.tables.find((table: TableDef) => {
|
||||||
|
return table.name === view.component;
|
||||||
|
});
|
||||||
|
let relations: string[] = [];
|
||||||
|
if (storageTable) {
|
||||||
|
storageTable.relations.map((rel: BelongsToDef) => {
|
||||||
|
relations.push(rel.table);
|
||||||
|
});
|
||||||
|
systemDef.storage.relations.forEach((rel: ManyToManyDef) => {
|
||||||
|
// @ts-ignore
|
||||||
|
if (rel.left === storageTable.name) {
|
||||||
|
relations.push(rel.right);
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
if (rel.right === storageTable.name) {
|
||||||
|
relations.push(rel.left);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
view.columns.forEach((column: ColumnRef) => {
|
||||||
|
let colDef: ColumnDef | undefined = findColumn(column, systemDef);
|
||||||
|
if (colDef) {
|
||||||
|
out = out + `${colDef.name}: ${TYPE_VALUE[colDef.type]};
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
relations.forEach((rel: string) => {
|
||||||
|
out = out + `${rel}_id: string
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function componentDestination(systemDefName: string, componentName: string) {
|
||||||
|
return path.join(process.cwd(), `frontend-${systemDefName}`, 'src', 'components', componentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
componentDestination,
|
||||||
|
createDetailsInitValues,
|
||||||
|
createDetailsTypeValues,
|
||||||
|
findColumn,
|
||||||
|
initializeComponentFile,
|
||||||
|
insertServiceCode,
|
||||||
|
lowercaseFirstLetter,
|
||||||
|
makeSet,
|
||||||
|
removeTemplateFiles,
|
||||||
|
removeTemplateFolder,
|
||||||
|
uppercaseFirstLetter,
|
||||||
|
}
|
@ -2,12 +2,15 @@ import * as path from 'path';
|
|||||||
const ncp = require('ncp').ncp;
|
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 {removeTemplateFolder} from "./helpers";
|
||||||
|
|
||||||
class SystemGenService {
|
class SystemGenService {
|
||||||
|
|
||||||
public runSysGen(defs: SystemDef) {
|
public runSysGen(defs: SystemDef) {
|
||||||
ncp(path.join(process.cwd(), 'frame'), path.join(process.cwd(), defs.name), (err: any) => {
|
ncp(path.join(process.cwd(), 'frame'), path.join(process.cwd(), defs.name), (err: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
console.log("error in ncp");
|
||||||
console.log(err);
|
console.log(err);
|
||||||
} else {
|
} else {
|
||||||
console.log('success copying files');
|
console.log('success copying files');
|
||||||
@ -15,6 +18,16 @@ class SystemGenService {
|
|||||||
this.buildViews(defs, defs.name);
|
this.buildViews(defs, defs.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ncp(path.join(process.cwd(), 'frontend-frame'), path.join(process.cwd(), `frontend-${defs.name}`), (err: any) => {
|
||||||
|
if (err) {
|
||||||
|
console.log("error in frontend ncp");
|
||||||
|
console.log(err);
|
||||||
|
} else {
|
||||||
|
console.log("success copying frontend files");
|
||||||
|
this.buildFrontend(defs);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public buildDatabase(storage: StorageDef, outDir: string) {
|
public buildDatabase(storage: StorageDef, outDir: string) {
|
||||||
@ -25,6 +38,15 @@ class SystemGenService {
|
|||||||
public buildViews(systemDef: SystemDef, outDir: string) {
|
public buildViews(systemDef: SystemDef, outDir: string) {
|
||||||
createViews(systemDef);
|
createViews(systemDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public buildFrontend(systemDef: SystemDef) {
|
||||||
|
createFrontend(systemDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public cleanup() {
|
||||||
|
// TODO: remove 'component/{{component}}' folder
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const systemGenService = new SystemGenService();
|
const systemGenService = new SystemGenService();
|
||||||
@ -34,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 {
|
||||||
@ -50,7 +72,7 @@ interface ComponentDef {
|
|||||||
interface EndpointDef {
|
interface EndpointDef {
|
||||||
component: string;
|
component: string;
|
||||||
table: string;
|
table: string;
|
||||||
type: ('list' | 'count' | 'item' | 'distinct' | 'update' | 'create' | 'delete' | 'search');
|
type: ('list' | 'count' | 'item' | 'update' | 'create' | 'delete' | 'search');
|
||||||
columns: ColumnRef[];
|
columns: ColumnRef[];
|
||||||
values?: ValueDef[];
|
values?: ValueDef[];
|
||||||
join?: JoinDef[];
|
join?: JoinDef[];
|
||||||
@ -67,8 +89,9 @@ interface Order {
|
|||||||
interface Filter {
|
interface Filter {
|
||||||
param: string; // the query param used to get the value
|
param: string; // the query param used to get the value
|
||||||
column: ColumnRef;
|
column: ColumnRef;
|
||||||
comparison: '=' | '!=' | '>' | '<' | 'contains';
|
comparison: '=' | '!=' | '>' | '<' | 'contains' | 'LIKE';
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TableDef {
|
interface TableDef {
|
||||||
@ -91,6 +114,7 @@ interface ColumnRef {
|
|||||||
table: string;
|
table: string;
|
||||||
param?: string;
|
param?: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ValueDef {
|
interface ValueDef {
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
function uppercaseFirstLetter(input: string)
|
|
||||||
{
|
|
||||||
return input.charAt(0).toUpperCase() + input.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function lowercaseFirstLetter(input: string)
|
|
||||||
{
|
|
||||||
return input.charAt(0).toLowerCase() + input.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
uppercaseFirstLetter,
|
|
||||||
lowercaseFirstLetter
|
|
||||||
}
|
|
@ -1,11 +1,13 @@
|
|||||||
import {ColumnRef, EndpointDef, Filter, JoinDef, Order} from "../systemGenService";
|
import {ColumnRef, EndpointDef, Filter, JoinDef, Order} from "../systemGenService";
|
||||||
import pluralize from "pluralize";
|
import pluralize from "pluralize";
|
||||||
import {uppercaseFirstLetter, lowercaseFirstLetter} from "./helpers";
|
import {uppercaseFirstLetter, lowercaseFirstLetter} from "../helpers";
|
||||||
|
|
||||||
function createMapperFunc(view: EndpointDef): string {
|
function createMapperFunc(view: EndpointDef): string {
|
||||||
let func: string = '';
|
let func: string = '';
|
||||||
if (view.type === 'list' || view.type === 'count') {
|
if (view.type === 'list' || view.type === 'count') {
|
||||||
func = buildGetList(view);
|
func = buildGetList(view);
|
||||||
|
} else if (view.type === 'search') {
|
||||||
|
func = buildGetList(view, true);
|
||||||
} else if (view.type === 'item') {
|
} else if (view.type === 'item') {
|
||||||
func = buildGetItem(view);
|
func = buildGetItem(view);
|
||||||
} else if (view.type === 'update') {
|
} else if (view.type === 'update') {
|
||||||
@ -20,12 +22,9 @@ function createMapperFunc(view: EndpointDef): string {
|
|||||||
|
|
||||||
function buildDeleteItem(view: EndpointDef): string {
|
function buildDeleteItem(view: EndpointDef): string {
|
||||||
let func: string = '';
|
let func: string = '';
|
||||||
let funcName = buildFunctionName(view);
|
let funcName = buildMapperFunctionName(view);
|
||||||
let tables: string = `${view.table}`;
|
let tables: string = `${view.table}`;
|
||||||
let filtersObj = buildFilters(view);
|
let {filters, requiredQueryValues, optionalFilterParts} = constructFilters(view);
|
||||||
let filters: string = filtersObj.filters;
|
|
||||||
let requiredFilterValues: string = filtersObj.requiredFilterValues;
|
|
||||||
let optionalFilterParts = filtersObj.optionalFiltersCode;
|
|
||||||
let query: string = `DELETE FROM ${tables}${filters}`;
|
let query: string = `DELETE FROM ${tables}${filters}`;
|
||||||
let funcParams = 'params: {[key: string]: string}';
|
let funcParams = 'params: {[key: string]: string}';
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ function buildDeleteItem(view: EndpointDef): string {
|
|||||||
public ${funcName}(${funcParams}) {
|
public ${funcName}(${funcParams}) {
|
||||||
|
|
||||||
let query: string = '${query}';
|
let query: string = '${query}';
|
||||||
let queryValues: string[] = [${requiredFilterValues}];
|
let queryValues: (string | number)[] = [${requiredQueryValues}];
|
||||||
|
|
||||||
// optional params?
|
// optional params?
|
||||||
${optionalFilterParts}
|
${optionalFilterParts}
|
||||||
@ -46,13 +45,10 @@ function buildDeleteItem(view: EndpointDef): string {
|
|||||||
|
|
||||||
function buildGetItem(view: EndpointDef): string {
|
function buildGetItem(view: EndpointDef): string {
|
||||||
let func: string = '';
|
let func: string = '';
|
||||||
let funcName = buildFunctionName(view);
|
let funcName = buildMapperFunctionName(view);
|
||||||
let columns: string = `${view.component}.${view.component}_id, ${view.columns.map(c => `${c.table}.${c.name}`).join(', ')}`;
|
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 tables: string = view.join?.length ? buildJoin(view) : `${view.table}`;
|
||||||
let filtersObj = buildFilters(view);
|
let {filters, requiredQueryValues, optionalFilterParts} = constructFilters(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 query: string = `SELECT ${columns} FROM ${tables}${filters} LIMIT 1`;
|
||||||
let funcParams = 'params: {[key: string]: string}';
|
let funcParams = 'params: {[key: string]: string}';
|
||||||
|
|
||||||
@ -60,7 +56,7 @@ function buildGetItem(view: EndpointDef): string {
|
|||||||
public ${funcName}(${funcParams}) {
|
public ${funcName}(${funcParams}) {
|
||||||
|
|
||||||
let query: string = '${query}';
|
let query: string = '${query}';
|
||||||
let queryValues: string[] = [${requiredFilterValues}];
|
let queryValues: (string | number)[] = [${requiredQueryValues}];
|
||||||
|
|
||||||
// optional params?
|
// optional params?
|
||||||
${optionalFilterParts}
|
${optionalFilterParts}
|
||||||
@ -73,23 +69,60 @@ function buildGetItem(view: EndpointDef): string {
|
|||||||
|
|
||||||
function buildCreateFunc(view: EndpointDef): string {
|
function buildCreateFunc(view: EndpointDef): string {
|
||||||
let func: string = '';
|
let func: string = '';
|
||||||
let query: string = `INSERT INTO ${view.table} SET `;
|
let inQuery: string = `INSERT INTO ${view.table} SET `;
|
||||||
func = getCreateOrUpdateFunc(query, view);
|
let {funcName, query, requiredQueryValues, optionalValuesCode} = getCreateOrUpdateFunc(inQuery, view);
|
||||||
|
|
||||||
|
func = `
|
||||||
|
public ${funcName}(params: {[key: string]: string}) {
|
||||||
|
|
||||||
|
let query: string = '${query}';
|
||||||
|
let queryValues: (string | number)[] = [${requiredQueryValues}];
|
||||||
|
|
||||||
|
// optional params?
|
||||||
|
${optionalValuesCode}
|
||||||
|
return super.runQuery(query, [...queryValues]);
|
||||||
|
}
|
||||||
|
`;
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildUpdateFunc(view: EndpointDef): string {
|
function buildUpdateFunc(view: EndpointDef): string {
|
||||||
let func: string = '';
|
let func: string = '';
|
||||||
let query: string = `UPDATE ${view.table} SET `;
|
let inQuery: string = `UPDATE ${view.table} SET `;
|
||||||
func = getCreateOrUpdateFunc(query, view);
|
let {funcName, query, requiredQueryValues, optionalValuesCode} = getCreateOrUpdateFunc(inQuery, view);
|
||||||
|
let postQuery = ` WHERE `;
|
||||||
|
for (let i in view.filters) {
|
||||||
|
// @ts-ignore
|
||||||
|
let filter: Filter = view.filters[i];
|
||||||
|
if (!postQuery.endsWith('WHERE ')) {
|
||||||
|
postQuery = postQuery + ` AND `;
|
||||||
|
}
|
||||||
|
postQuery = postQuery + `${filter.column.table}.${filter.column.name} = ?`;
|
||||||
|
if (requiredQueryValues.length) {
|
||||||
|
requiredQueryValues = requiredQueryValues + `, `;
|
||||||
|
}
|
||||||
|
requiredQueryValues = requiredQueryValues + `params.${filter.param}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
func = `
|
||||||
|
public ${funcName}(params: {[key: string]: string}) {
|
||||||
|
|
||||||
|
let query: string = '${query}';
|
||||||
|
let queryValues: (string | number)[] = [${requiredQueryValues}];
|
||||||
|
|
||||||
|
// optional params?
|
||||||
|
${optionalValuesCode}
|
||||||
|
|
||||||
|
query = query + '${postQuery}';
|
||||||
|
return super.runQuery(query, [...queryValues]);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCreateOrUpdateFunc(query: string, view: EndpointDef): string {
|
function getCreateOrUpdateFunc(query: string, view: EndpointDef) {
|
||||||
let func: string = '';
|
let funcName = buildMapperFunctionName(view);
|
||||||
let funcName = buildFunctionName(view);
|
|
||||||
let requiredQueryValues: string = '';
|
let requiredQueryValues: string = '';
|
||||||
let optionalValuesCode: string = '';
|
let optionalValuesCode: string = '';
|
||||||
for (let i in view.columns) {
|
for (let i in view.columns) {
|
||||||
@ -112,33 +145,11 @@ function getCreateOrUpdateFunc(query: string, view: EndpointDef): string {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {funcName, query, requiredQueryValues, optionalValuesCode};
|
||||||
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 {
|
function constructFilters(view: EndpointDef, isSearch=false) {
|
||||||
// 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 = '';
|
let filters: string = '';
|
||||||
funcName = buildFunctionName(view);
|
|
||||||
|
|
||||||
let requiredFilterStrings = [];
|
let requiredFilterStrings = [];
|
||||||
let requiredQueryValues = '';
|
let requiredQueryValues = '';
|
||||||
let optionalFilterParts = '';
|
let optionalFilterParts = '';
|
||||||
@ -160,13 +171,27 @@ function buildGetList(view: EndpointDef): string {
|
|||||||
*/
|
*/
|
||||||
optionalFilterParts = optionalFilterParts + `
|
optionalFilterParts = optionalFilterParts + `
|
||||||
if (params.${f.param}) {
|
if (params.${f.param}) {
|
||||||
query = query + ' AND ${f.column.table}.${f.column.name} ${f.comparison} ?';
|
query = query + ' ${isSearch ? 'OR' : 'AND'} ${f.column.table}.${f.column.name} ${f.comparison} ?';
|
||||||
queryValues.push(params.${f.param});
|
queryValues.push(${isSearch ? '\'%\' + params.' + f.param + ' + \'%\'' : 'params.' + f.param});
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filters = ` WHERE ${requiredFilterStrings.join(' AND ')}`;
|
filters = ` WHERE ${requiredFilterStrings.join(' AND ')}`;
|
||||||
}
|
}
|
||||||
|
return {filters, requiredQueryValues, optionalFilterParts};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildGetList(view: EndpointDef, isSearch=false): 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(*) as 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}`;
|
||||||
|
funcName = buildMapperFunctionName(view);
|
||||||
|
|
||||||
|
let {filters, requiredQueryValues, optionalFilterParts} = constructFilters(view, isSearch);
|
||||||
|
|
||||||
query = `SELECT ${columns} FROM ${tables}${filters}`;
|
query = `SELECT ${columns} FROM ${tables}${filters}`;
|
||||||
let orderByCode = '';
|
let orderByCode = '';
|
||||||
@ -176,10 +201,10 @@ function buildGetList(view: EndpointDef): string {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
let funcParams = 'params: {[key: string]: string}, offset: number, limit: number';
|
let funcParams = 'params: {[key: string]: string}, offset: number, limit: number';
|
||||||
let limitOrderCode = `query = query + ' LIMIT ?, ?';
|
let limitOrderCode = `${orderByCode}
|
||||||
queryValues.push(offset.toString(10));
|
query = query + ' LIMIT ?, ?';
|
||||||
queryValues.push(limit.toString(10));
|
queryValues.push(offset);
|
||||||
${orderByCode}`;
|
queryValues.push(limit);`;
|
||||||
if (view.type === 'count') {
|
if (view.type === 'count') {
|
||||||
funcParams = 'params: {[key: string]: string}';
|
funcParams = 'params: {[key: string]: string}';
|
||||||
limitOrderCode = ``;
|
limitOrderCode = ``;
|
||||||
@ -189,7 +214,7 @@ function buildGetList(view: EndpointDef): string {
|
|||||||
public ${funcName}(${funcParams}) {
|
public ${funcName}(${funcParams}) {
|
||||||
|
|
||||||
let query: string = '${query}';
|
let query: string = '${query}';
|
||||||
let queryValues: string[] = [${requiredQueryValues}];
|
let queryValues: (string | number)[] = [${requiredQueryValues}];
|
||||||
|
|
||||||
// optional params?
|
// optional params?
|
||||||
${optionalFilterParts}
|
${optionalFilterParts}
|
||||||
@ -200,49 +225,6 @@ function buildGetList(view: EndpointDef): string {
|
|||||||
return func;
|
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 {
|
function buildJoin(view: EndpointDef): string {
|
||||||
if (view.join?.length) {
|
if (view.join?.length) {
|
||||||
let join = `${view.table}`;
|
let join = `${view.table}`;
|
||||||
@ -270,7 +252,7 @@ function buildOrderBy(view: EndpointDef): string {
|
|||||||
return orderBy;
|
return orderBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFunctionName(view: EndpointDef): string {
|
function buildMapperFunctionName(view: EndpointDef): string {
|
||||||
let selector: {[type: string]: (view: EndpointDef) => string} = {
|
let selector: {[type: string]: (view: EndpointDef) => string} = {
|
||||||
'list': (view: EndpointDef) => `get${pluralize(uppercaseFirstLetter(view.component))}` + (view.type === 'count' ? 'Count' : ''),
|
'list': (view: EndpointDef) => `get${pluralize(uppercaseFirstLetter(view.component))}` + (view.type === 'count' ? 'Count' : ''),
|
||||||
'count': (view: EndpointDef) => `get${pluralize(uppercaseFirstLetter(view.component))}` + (view.type === 'count' ? 'Count' : ''),
|
'count': (view: EndpointDef) => `get${pluralize(uppercaseFirstLetter(view.component))}` + (view.type === 'count' ? 'Count' : ''),
|
||||||
@ -278,11 +260,12 @@ function buildFunctionName(view: EndpointDef): string {
|
|||||||
'update': (view: EndpointDef) => `update${uppercaseFirstLetter(view.component)}`,
|
'update': (view: EndpointDef) => `update${uppercaseFirstLetter(view.component)}`,
|
||||||
'create': (view: EndpointDef) => `create${uppercaseFirstLetter(view.component)}`,
|
'create': (view: EndpointDef) => `create${uppercaseFirstLetter(view.component)}`,
|
||||||
'delete': (view: EndpointDef) => `delete${uppercaseFirstLetter(view.component)}`,
|
'delete': (view: EndpointDef) => `delete${uppercaseFirstLetter(view.component)}`,
|
||||||
'search': (view: EndpointDef) => `search${uppercaseFirstLetter(view.component)}`,
|
'search': (view: EndpointDef) => `search${pluralize(uppercaseFirstLetter(view.component))}`,
|
||||||
};
|
};
|
||||||
return selector[view.type](view);
|
return selector[view.type](view);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createMapperFunc
|
createMapperFunc,
|
||||||
|
buildMapperFunctionName
|
||||||
}
|
}
|
76
src/views/routes-creator.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import {EndpointDef} from "../systemGenService";
|
||||||
|
import {lowercaseFirstLetter, uppercaseFirstLetter} from "../helpers";
|
||||||
|
import {buildServiceFunctionName, getFuncParams} from "./service-creator";
|
||||||
|
import pluralize from "pluralize";
|
||||||
|
|
||||||
|
const METHOD = {
|
||||||
|
'count': 'post',
|
||||||
|
'create': 'post',
|
||||||
|
'delete': 'delete',
|
||||||
|
'item': 'get',
|
||||||
|
'list': 'post',
|
||||||
|
'search': 'post',
|
||||||
|
'update': 'put',
|
||||||
|
}
|
||||||
|
|
||||||
|
const URL = {
|
||||||
|
'count': (view: EndpointDef) => {
|
||||||
|
return `count${uppercaseFirstLetter(pluralize(view.component))}`;
|
||||||
|
},
|
||||||
|
'create': (view: EndpointDef) => {
|
||||||
|
return `${view.component}`;
|
||||||
|
},
|
||||||
|
'delete': (view: EndpointDef) => {
|
||||||
|
// TODO: needs the params added to select the right one (id)
|
||||||
|
return `${view.component}/:${view.component}_id`;
|
||||||
|
},
|
||||||
|
'item': (view: EndpointDef) => {
|
||||||
|
// TODO: needs the params added to select the right one (id)
|
||||||
|
return `${view.component}/:${view.component}_id`;
|
||||||
|
},
|
||||||
|
'list': (view: EndpointDef) => {
|
||||||
|
return `${pluralize(view.component)}`;
|
||||||
|
},
|
||||||
|
'search': (view: EndpointDef) => {
|
||||||
|
return `search${uppercaseFirstLetter(pluralize(view.component))}`;
|
||||||
|
},
|
||||||
|
'update': (view: EndpointDef) => {
|
||||||
|
// TODO: needs the params added to select the right one (id)
|
||||||
|
return `${view.component}/:${view.component}_id`;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRoutesFunc(view: EndpointDef): string {
|
||||||
|
let params = getFuncParams(view);
|
||||||
|
let isListOrSearch: boolean = view.type === 'list' || view.type === 'search';
|
||||||
|
if (isListOrSearch) {
|
||||||
|
params.push('offset');
|
||||||
|
params.push('limit');
|
||||||
|
}
|
||||||
|
// TODO: get requests don't have req.body so the params need to come from the url
|
||||||
|
let func: string = `
|
||||||
|
router.${METHOD[view.type]}('/${URL[view.type](view)}', (req, res, next) => {
|
||||||
|
${serviceInstance(view)}.${buildServiceFunctionName(view)}(${(params.length ? 'req.body.' : '') + params.join(', req.body.')}).then((data) => {
|
||||||
|
res.status(200);
|
||||||
|
res.json(data);
|
||||||
|
}).catch((err) => {
|
||||||
|
res.status(500);
|
||||||
|
res.json({
|
||||||
|
status: 'error',
|
||||||
|
message: err,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
function serviceInstance(view: EndpointDef): string {
|
||||||
|
return `${lowercaseFirstLetter(view.component)}Service`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createRoutesFunc,
|
||||||
|
METHOD,
|
||||||
|
URL
|
||||||
|
}
|
128
src/views/service-creator.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import {BelongsToDef, ColumnDef, EndpointDef, Filter, ManyToManyDef, SystemDef, TableDef} from "../systemGenService";
|
||||||
|
import {lowercaseFirstLetter} from "../helpers";
|
||||||
|
import {buildMapperFunctionName} from "./mapper-creator";
|
||||||
|
|
||||||
|
|
||||||
|
function createServiceFunc(view: EndpointDef) {
|
||||||
|
// TODO: add support for doing things other than talking to the mapper e.g. API calls and cache checks
|
||||||
|
return buildServiceFunction(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildServiceFunction(view: EndpointDef): string {
|
||||||
|
let func: string = '';
|
||||||
|
let isListOrSearch: boolean = view.type === 'list' || view.type === 'search';
|
||||||
|
let funcName: string = buildServiceFunctionName(view);
|
||||||
|
let funcParams: string[] = getFuncParams(view);
|
||||||
|
let columnParams: string[] = view.columns?.filter(column => column.param).map(column => { return '\n ' + column.param + ': ' + column.param; });
|
||||||
|
let filterParams: string[] = view.filters?.filter(filter => filter.param).map(filter => { return '\n ' + filter.param + ': ' + filter.param; }) || [];
|
||||||
|
|
||||||
|
if (isListOrSearch) {
|
||||||
|
funcParams.push('offset');
|
||||||
|
funcParams.push('limit');
|
||||||
|
}
|
||||||
|
|
||||||
|
func = `
|
||||||
|
public ${funcName}(${funcParams.join(', ')}) {
|
||||||
|
let params = {${columnParams}${columnParams.length ? ',' : ''}${filterParams}
|
||||||
|
};
|
||||||
|
return this.${mapperInstance(view)}.${buildMapperFunctionName(view)}(params${isListOrSearch ? ', offset, limit' : ''});
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFuncParams(view: EndpointDef, addTypes=false, systemDef?: SystemDef): string[] {
|
||||||
|
let filters = view.filters || [];
|
||||||
|
let columnList: ParamDef[] = view.columns?.filter((column) => {return column.param;}).map((column) => {
|
||||||
|
return {
|
||||||
|
param: column.param,
|
||||||
|
required: column.required,
|
||||||
|
type: column.type
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let filterList: ParamDef[] = filters.filter((column) => {return column.param;}).map((filter) => {
|
||||||
|
return {
|
||||||
|
param: filter.param,
|
||||||
|
required: filter.required,
|
||||||
|
type: filter.type
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let relationsList: ParamDef[] = [];
|
||||||
|
if (systemDef) {
|
||||||
|
let storageTable: TableDef | undefined = systemDef.storage.tables.find((table: TableDef) => {
|
||||||
|
return view.component === table.name;
|
||||||
|
});
|
||||||
|
if (storageTable) {
|
||||||
|
relationsList = storageTable.relations.map((rel: BelongsToDef) => {
|
||||||
|
return {
|
||||||
|
param: rel.table + '_id',
|
||||||
|
required: false,
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
relationsList = [...relationsList, ...systemDef.storage.relations.filter((rel: ManyToManyDef) => {
|
||||||
|
if (rel.left === view.component || rel.right === view.component) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}).map((rel: ManyToManyDef) => {
|
||||||
|
if (rel.left === view.component) {
|
||||||
|
return {
|
||||||
|
param: rel.right + '_id',
|
||||||
|
required: false,
|
||||||
|
type: 'string'
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
param: rel.left + '_id',
|
||||||
|
required: false,
|
||||||
|
type: 'string'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcParams should be sorted by 'required' so the optional params can come last and be left off when calling
|
||||||
|
let combinedList = relationsList.concat(columnList).concat(filterList).sort((a, b) => {
|
||||||
|
if (a.required && b.required) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (a.required) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b.required) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return combinedList.map((param) => {
|
||||||
|
if (param.type && addTypes) {
|
||||||
|
return `${param.param}: ${param.type}`;
|
||||||
|
}
|
||||||
|
return param.param || ''; // both lists have already been filtered so the value should never return as an empty string
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildServiceFunctionName(view: EndpointDef): string {
|
||||||
|
return buildMapperFunctionName(view); // the functions should have the same name... I'm pretty sure...
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapperInstance(view: EndpointDef): string {
|
||||||
|
return `${lowercaseFirstLetter(view.component)}Mapper`;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParamDef {
|
||||||
|
param?: string;
|
||||||
|
required?: boolean;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createServiceFunc,
|
||||||
|
getFuncParams,
|
||||||
|
buildServiceFunctionName
|
||||||
|
}
|
@ -6,7 +6,8 @@ import {
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {createMapperFunc} from "./mapper-creator";
|
import {createMapperFunc} from "./mapper-creator";
|
||||||
import {uppercaseFirstLetter, lowercaseFirstLetter} from "./helpers";
|
import {initializeComponentFile, insertServiceCode} from "../helpers";
|
||||||
|
import {createRoutesFunc} from "./routes-creator";
|
||||||
|
|
||||||
const ncp = require('ncp').ncp;
|
const ncp = require('ncp').ncp;
|
||||||
|
|
||||||
@ -24,6 +25,8 @@ function createComponent(component: ComponentDef, systemDef: SystemDef) {
|
|||||||
ncp(path.join(process.cwd(), 'frame', 'src', 'components', '{{component}}'), path.join(process.cwd(), systemDef.name, 'src', 'components', component.component), (err: any) => {
|
ncp(path.join(process.cwd(), 'frame', 'src', 'components', '{{component}}'), path.join(process.cwd(), systemDef.name, 'src', 'components', component.component), (err: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
componentReject();
|
||||||
|
return;
|
||||||
} else {
|
} 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);
|
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) {
|
for (let i in component.endpoints) {
|
||||||
@ -32,9 +35,19 @@ function createComponent(component: ComponentDef, systemDef: SystemDef) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
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);
|
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);
|
||||||
|
for (let i in component.endpoints) {
|
||||||
|
routesPromise = routesPromise.then(() => {
|
||||||
|
return insertRoutesCode(component.endpoints[i], systemDef.name);
|
||||||
|
})
|
||||||
|
}
|
||||||
addInitializeRoutesCode(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 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);
|
let servicePromise = initializeComponentFile(path.join(process.cwd(), systemDef.name, 'src', 'components', component.component, '{{component}}Service.ets'), serviceFileLocation, component.component, systemDef.name);
|
||||||
|
for (let i in component.endpoints) {
|
||||||
|
servicePromise = servicePromise.then(() => {
|
||||||
|
return insertServiceCode(component.endpoints[i], systemDef.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
addInitializeServiceCode(component.component, systemDef.name);
|
addInitializeServiceCode(component.component, systemDef.name);
|
||||||
Promise.all([mapperPromise, routesPromise, servicePromise]).then(() => {
|
Promise.all([mapperPromise, routesPromise, servicePromise]).then(() => {
|
||||||
console.log(`success creating component ${component.component}`);
|
console.log(`success creating component ${component.component}`);
|
||||||
@ -45,20 +58,6 @@ function createComponent(component: ComponentDef, systemDef: SystemDef) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
function addInitializeServiceCode(component: string, outDir: string) {
|
||||||
let fileLocation = path.join(process.cwd(), outDir, 'src', 'initializeServices.ts');
|
let fileLocation = path.join(process.cwd(), outDir, 'src', 'initializeServices.ts');
|
||||||
let initServicesFile: string = fs.readFileSync(fileLocation, 'utf8');
|
let initServicesFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||||
@ -98,4 +97,18 @@ function insertMapperCode(view: EndpointDef, outDir: string): Promise<void> {
|
|||||||
fs.writeFileSync(fileLocation, newMapperFile, 'utf8');
|
fs.writeFileSync(fileLocation, newMapperFile, 'utf8');
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insertRoutesCode(view: EndpointDef, outDir: string): Promise<void> {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
const separator: string = `// SYSTEM-BUILDER-${view.component}-routes`;
|
||||||
|
let fileLocation = path.join(process.cwd(), outDir, 'src', 'components', view.component, `${view.component}Routes.ts`);
|
||||||
|
let initRoutesFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||||
|
let parts = initRoutesFile.split(separator);
|
||||||
|
parts[0] = parts[0] + createRoutesFunc(view);
|
||||||
|
let newRoutesFile = parts.join(separator);
|
||||||
|
fs.writeFileSync(fileLocation, newRoutesFile, 'utf8');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,8 @@
|
|||||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
// "types": [], /* Type declaration files to be included in compilation. */
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"esModuleInterop": true,
|
||||||
|
/* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
|
||||||
/* Source Map Options */
|
/* Source Map Options */
|
||||||
@ -58,6 +59,12 @@
|
|||||||
/* Experimental Options */
|
/* Experimental Options */
|
||||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"es5",
|
||||||
|
"scripthost",
|
||||||
|
"es2015.promise"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "**/__tests__/*"]
|
"exclude": ["node_modules", "**/__tests__/*"]
|
||||||
|