UNPKG

@egi/smart-db

Version:

Unified Smart DB Access

1 lines 10.1 kB
import fs from"fs";import _ from"lodash";import process from"process";import{SmartDbBetterSqlite3}from"../drivers/smart-db-better-sqlite3";import{SmartDbOracle}from"../drivers/smart-db-oracle";import{OracleCatModel}from"../models/oracle-cat-model";import{SmartDbCoreTableModel}from"../models/smart-db-core-table-model";import{SqliteMasterModel}from"../models/sqlite-master-model";import{IN,NE,NOT_LIKE}from"../smart-db-globals";function getType(e){let t;return t=e?e.match(/varchar/i)||e.match(/text/i)?"string":e.match(/integer\(1\)/i)?"boolean":e.match(/date/i)?"Date":e.match(/integer/i)||e.match(/number/i)?"number":e.match(/long/i)||e.match(/raw/i)?"any":"SqlValueType":"SqlValueType",t}function getRelations(e){return new Promise((async(t,r)=>{e instanceof SmartDbOracle?e.getAll(OracleCatModel,{type:IN(["TABLE","VIEW"]),name:NOT_LIKE("BIN$%")}).then((a=>{a?t(a.map((e=>e.name.toLowerCase()))):r(e.getLastError())})).catch((e=>{r(e)})):e instanceof SmartDbBetterSqlite3?e.getAll(SqliteMasterModel,{type:IN(["table","view"]),name:NE("sqlite_sequence")}).then((a=>{a?t(a.map((e=>e.name))):r(e.getLastError())})).catch((e=>{r(e)})):r("unimplemented database type")}))}async function extractDbApi(e){console.log(`extract interface from '${e.db.getDbConnector()}' to '${e.modelDirectory}'`);const t=["break","case","catch","class","const","continue","debugger","default","delete","do","else","enum","export","extends","false","finally","for","function","If","import","in","instanceOf","interface","new","null","return","super","switch","this","throw","true","try","typeOf","var","void","while","with"];fs.existsSync(e.modelDirectory)||fs.mkdirSync(e.modelDirectory,{recursive:!0}),getRelations(e.db).then((async r=>{let a=[];if((e.skipCoreTables||e.justCoreTables)&&await e.db.exists(SmartDbCoreTableModel)){const t=await e.db.getAll(SmartDbCoreTableModel);if(!1===t)throw e.db.getLastError();t.forEach((e=>{a.push(e.name)}))}const n=[];for(const s of r){let r=!0;if(e.justCoreTables?r=a.includes(s):e.skipCoreTables&&(r=!a.includes(s)),r){const r=s[0].toUpperCase()+_.camelCase(s.substring(1))+"Model",a=[];console.log("working on",s),await e.db.getTableInfo(s).then((o=>{const i=o.fields;let l=!1,c="",m="",p="",d="",b="";const u=s.toLowerCase().replace(/[^[a-z0-9]/g,"-")+"-model";if(n.push({className:r,fileName:u}),c+="/* eslint-disable @typescript-eslint/member-ordering */\n",c+="// noinspection JSUnusedGlobalSymbols\n",i){e.abstractModelModule==e.smartDbInterfaces?e.separateApi?(p+=`import {GenericModelData} from "${e.abstractModelModule}";\n`,b+=`import {AbstractModel, ModelAttributeMap} from "${e.abstractModelModule}";\n`,b+=`import {${r}Data} from "./${u}-api";\n`):b+=`import {AbstractModel, GenericModelData, ModelAttributeMap} from "${e.abstractModelModule}";\n`:e.separateApi?(p+=`import {GenericModelData} from "${e.smartDbInterfaces}";\n`,b+=`import {ModelAttributeMap} from "${e.smartDbInterfaces}";\n`,b+=`import {AbstractModel} from "${e.abstractModelModule}";\n`,b+=`import {${r}Data} from "./${u}-api";\n`):(b+=`import {GenericModelData, ModelAttributeMap} from "${e.smartDbInterfaces}";\n`,b+=`import {AbstractModel} from "${e.abstractModelModule}";\n`),m+=`export interface ${r}Data extends GenericModelData {\n`;let n,o="",f="",h=!1;const g=[];i.forEach((e=>{const t=e.name.match(/([a-z0-9]{1,4})_/i);e.isPk&&(o=e.name,t&&(f=t[1])),t?g.includes(t[1])||g.push(t[1]):h=!0})),f||h||1!=g.length||(f=g[0]),f&&(n=new RegExp(`^${f}_(.*)$`));const y={};i.forEach((t=>{const r=getType(t.type);let o;l||"SqlValueType"!=r||(p+=`import {SqlValueType} from "${e.smartDbInterfaces}";\n`,b+=`import {SqlValueType} from "${e.smartDbInterfaces}";\n`,l=!0);const i=n&&t.name.match(n);o=i?_.camelCase(i[1]):_.camelCase(t.name);let c=!0;if(o!=t.name&&(a.push({name:o,attribute:o,type:r,typeScriptStyle:c,alias:t.name}),c=!1),o!=t.name.toLowerCase()&&(a.push({name:t.name.toLowerCase(),attribute:o,type:r,physical:void 0!==t.cid&&t.name==t.name.toLowerCase(),typeScriptStyle:c}),c=!1),o!=t.name.toUpperCase()&&a.push({name:t.name.toUpperCase(),attribute:o,type:r,physical:void 0!==t.cid&&t.name==t.name.toUpperCase(),typeScriptStyle:c}),t.cid){const e=a.find((e=>e.physical&&e.name.toLowerCase()==t.name.toLowerCase()));e||a.push({name:t.name,attribute:o,type:r,physical:!0,typeScriptStyle:c})}y[o]=r,"user"==s&&"name"==o&&(m+=" // @ts-ignore"),m+=` ${o}?: ${r};\n`})),m+="}\n",d+=`export class ${r} extends AbstractModel<${r}, ${r}Data> {\n`,_.forEach(y,((e,t)=>{d+=` private _${t}?: ${e};\n`})),"user"==s&&(d+=" private _hash?: string;\n",d+=" private _privileges?: string[];\n"),d+="\n",d+=" static readonly attributeMap: ModelAttributeMap = {\n",a.forEach(((e,t)=>{t>0&&(d+=",\n"),d+=` ${e.name}: {\n`,e.physical&&(d+=" physical: true,\n"),e.alias&&(d+=` alias: "${e.alias}",\n`),e.virtual&&(d+=" virtual: true,\n"),e.typeScriptStyle&&(d+=" typeScriptStyle: true,\n"),d+=` type: "${e.type}",\n`,d+=` attribute: "_${e.attribute}"\n`,d+=" }"})),d+="\n",d+=" };\n",d+="\n",d+=" static getClassName(): string {\n",d+=` return "${r}";\n`,d+=" }\n",d+="\n",d+=" static getTableName(): string {\n",d+=` return "${s}";\n`,d+=" }\n",d+="\n",d+=" static getPrimaryKey(): string {\n",d+=` return "${o}";\n`,d+=" }\n",d+="\n",d+=` static from(other: ${r} | ${r}Data): ${r} {\n`,d+=` let value: ${r} = null;\n`,d+=" if (other) {\n",d+=` value = new ${r}();\n`,d+=` if (other instanceof ${r}) {\n`,d+=" Object.assign(value, other);\n",d+=" } else {\n",d+=" value.assign(other);\n",d+=" }\n",d+=" }\n",d+=" return value;\n",d+=" }\n",d+="\n",d+=` constructor(data?: ${r} | ${r}Data) {\n`,d+=" super();\n",d+=" if (data) {\n",d+=" this.assign(data);\n",d+=" }\n",d+=" }\n",d+="\n",d+=` public clone(): ${r} {\n`,d+=` return ${r}.from(this);\n`,d+=" }\n",d+="\n",d+=" public getClassName(): string {\n",d+=` return "${r}";\n`,d+=" }\n",d+="\n",d+=" public getTableName(): string {\n",d+=` return "${s}";\n`,d+=" }\n",d+="\n",d+=" public getPrimaryKey(): string {\n",d+=` return "${o}";\n`,d+=" }\n",d+="\n",d+=" public getAttributeMap(): ModelAttributeMap {\n",d+=` return ${r}.attributeMap;\n`,d+=" }\n",a.forEach((e=>{let r;d+="\n",r=e.name.length<3||e.name.match(/_/)?"FunctionNamingConventionJS":"",""!==r&&(d+=` // noinspection ${r}\n`),d+=` get ${e.name}(): ${e.type} {\n`,d+=` return this._${e.attribute};\n`,d+=" }\n\n",""!==r&&(d+=` // noinspection ${r}\n`);let a=e.attribute;t.includes(a)&&(a="value"),d+=` set ${e.name}(${a}: ${e.type}) {\n`,d+=` this._${e.attribute} = ${a};\n`,d+=" }\n"})),"user"==s&&(d+="\n",d+=" get hash(): string {\n",d+=" return this._hash;\n",d+=" }\n\n",d+=" set hash(hash: string) {\n",d+=" this._hash = hash;\n",d+=" }\n",d+="\n",d+=" get privileges(): string[] {\n",d+=" return this._privileges;\n",d+=" }\n\n",d+=" set privileges(p: string[]) {\n",d+=" this._privileges = p;\n",d+=" }\n"),d+="}\n",e.separateApi?(m=c+"\n"+p+"\n"+m,fs.writeFileSync(`${e.modelDirectory}/${u+"-api.ts"}`,m),d=c+"\n"+b+"\n"+d):d=c+"\n"+b+"\n"+m+"\n"+d,fs.writeFileSync(`${e.modelDirectory}/${u+".ts"}`,d)}else console.log("error: table has no fields")})).catch((e=>{console.error(e),process.abort()}))}}let s=`import {AbstractModel} from "${e.abstractModelModule}";\n`;_.forEach(n,(e=>{s+=`import {${e.className}} from "./${e.fileName}";\n`})),s+="\n",s+="interface SmartDbDictionaryEntry {\n",s+=" cls: "+n.map((e=>"typeof "+e.className)).join(" |\n "),s+=";\n",s+="}\n\n",s+="export class SmartDbDictionary {\n",s+=" static models: { [relation: string]: SmartDbDictionaryEntry; } = {\n",_.forEach(n,(e=>{s+=` ${e.className}: {\n`,s+=` cls: ${e.className}\n`,s+=" },\n"})),s=s.substring(0,s.length-2)+"\n",s+=" };\n",s+="}\n",fs.writeFileSync(`${e.modelDirectory}/smart-db-dictionary.ts`,s)})).catch((e=>{console.error("extraction error",e)}))}(async()=>{let e=!1;const t=Array.from(process.argv);let r,a,n,s=!1;for(t.shift(),t.shift();t[0]&&"--"==t[0].substring(0,2);)"--separate-api"==t[0]?(e=!0,t.shift()):"--database"==t[0]?(r=t[1],t.shift(),t.shift()):"--username"==t[0]?(a=t[1],t.shift(),t.shift()):"--password"==t[0]?(n=t[1],t.shift(),t.shift()):"--create"==t[0]?(s=!0,t.shift()):(console.error(`unknown option '${t[0]}'`),process.exit(-1));if(r||(r=process.env.ORACLE_SID),fs.existsSync(r)||(a||(a=process.env.ORA_USER),n||(n=process.env.ORA_PASS)),r&&t.length<=1){let o;a&&n?o=new SmartDbOracle(r,{user:a,password:n,noDbLogging:!0}):s||fs.existsSync(r)?o=new SmartDbBetterSqlite3(r,{}):(console.error(`ERROR: missing sqlite3 database file ${r}`),process.exit(-1)),1==t.length?o.onReady.subscribe((async r=>{if(r){o.getLogger().setDbLogging(!1);const r={skipCoreTables:!0,modelDirectory:t[0],smartDbInterfaces:"@egi/smart-db",abstractModelModule:"@egi/smart-db",separateApi:e,db:o};await extractDbApi(r)}})):fs.existsSync("src/helpers/extract-db-api.ts")?o.onReady.subscribe((t=>{if(t){const t={justCoreTables:!0,modelDirectory:"src/models/generated",smartDbInterfaces:"../smart-db-interfaces",abstractModelModule:"./abstract-model",separateApi:e,db:o};t.db.initDb({module:"smart-db-core",sqlFilesDirectory:"assets/sqlite3"}).then((async()=>{await extractDbApi(t)}))}else null!==t&&console.error("unable to connect to database")})):console.log("command must be run at the root directory of the smart-db project")}else console.log("USAGE: extract-db-api --database {db-connector|path-to-db} [{options}] {target-models-directory}\n"),console.log("Options are: [--username {username}]"),console.log(" [--password {password}]"),console.log(" [--create"),console.log(" [--separate-api]")})();