bd-orm 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build-and-test.yml +26 -0
- package/.github/workflows/publish.yml +36 -0
- package/.prettierrc.json +14 -0
- package/CHANGELOG.md +3 -0
- package/README.md +5 -0
- package/check NBR en TODO.txt +32 -0
- package/dist/_/BdORMBase.d.ts +111 -0
- package/dist/_/BdORMBase.js +262 -0
- package/dist/_/BdORMCrud.d.ts +51 -0
- package/dist/_/BdORMCrud.js +223 -0
- package/dist/_/BdORMError.d.ts +4 -0
- package/dist/_/BdORMError.js +14 -0
- package/dist/_/BdOrmConnection.d.ts +32 -0
- package/dist/_/BdOrmConnection.js +5 -0
- package/dist/_/cdsBaseOrm.d.ts +12 -0
- package/dist/_/cdsBaseOrm.js +21 -0
- package/dist/dbConnections/BdOrmDbConnectionCapHana/index.d.ts +26 -0
- package/dist/dbConnections/BdOrmDbConnectionCapHana/index.js +78 -0
- package/dist/dbConnections/BdOrmDbConnectionCapHana/utils.d.ts +14 -0
- package/dist/dbConnections/BdOrmDbConnectionCapHana/utils.js +49 -0
- package/dist/dbConnections/BdOrmDbConnectionCapSqlite/index.d.ts +22 -0
- package/dist/dbConnections/BdOrmDbConnectionCapSqlite/index.js +85 -0
- package/dist/dbConnections/BdOrmDbConnectionCapSqlite/utils.d.ts +3 -0
- package/dist/dbConnections/BdOrmDbConnectionCapSqlite/utils.js +74 -0
- package/dist/dbConnections/BdOrmDbConnectionSQLBase.d.ts +32 -0
- package/dist/dbConnections/BdOrmDbConnectionSQLBase.js +99 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +18 -0
- package/dist/test-assets/Post.d.ts +12 -0
- package/dist/test-assets/Post.js +13 -0
- package/dist/test-assets/User.d.ts +16 -0
- package/dist/test-assets/User.js +13 -0
- package/dist/test-assets/jestUtils.d.ts +5 -0
- package/dist/test-assets/jestUtils.js +30 -0
- package/jest.config.js +7 -0
- package/package.json +53 -0
- package/src/_/BdORMBase.ts +288 -0
- package/src/_/BdORMCrud.test.ts +198 -0
- package/src/_/BdORMCrud.ts +240 -0
- package/src/_/BdORMError.ts +10 -0
- package/src/_/BdORMbase.test.ts +166 -0
- package/src/_/BdOrmConnection.ts +39 -0
- package/src/_/cdsBaseOrm.ts +18 -0
- package/src/dbConnections/BdOrmDbConnectionCapHana/index.ts +95 -0
- package/src/dbConnections/BdOrmDbConnectionCapHana/utils.ts +47 -0
- package/src/dbConnections/BdOrmDbConnectionCapHana.test.ts +18 -0
- package/src/dbConnections/BdOrmDbConnectionCapSqlite/index.ts +89 -0
- package/src/dbConnections/BdOrmDbConnectionCapSqlite/utils.ts +75 -0
- package/src/dbConnections/BdOrmDbConnectionCapSqlite.test.ts +41 -0
- package/src/dbConnections/BdOrmDbConnectionSQLBase.ts +124 -0
- package/src/index.ts +8 -0
- package/src/test-assets/Post.ts +16 -0
- package/src/test-assets/User.ts +18 -0
- package/src/test-assets/db/BdOrm.cds +20 -0
- package/src/test-assets/db/csv/bdorm-user.csv +3 -0
- package/src/test-assets/db/views/user.hdbview +3 -0
- package/src/test-assets/jestUtils.ts +24 -0
- package/tsconfig.json +16 -0
- package/validate-package.js +113 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const BdORMBase_1 = __importStar(require("./BdORMBase"));
|
|
37
|
+
const BdORMError_1 = require("./BdORMError");
|
|
38
|
+
const _filerDataByTable = async (self) => {
|
|
39
|
+
const data = self.getData(true);
|
|
40
|
+
const columns = await self.constructor.getDbConnection().getTableColumns(self.getTable());
|
|
41
|
+
return Object.keys(data).reduce((acc, key) => {
|
|
42
|
+
if (columns.includes(key))
|
|
43
|
+
acc[key] = data[key];
|
|
44
|
+
return acc;
|
|
45
|
+
}, {});
|
|
46
|
+
};
|
|
47
|
+
class BdORMCrud extends BdORMBase_1.default {
|
|
48
|
+
static _DbConnection;
|
|
49
|
+
// Events
|
|
50
|
+
async beforeCreate(beforeCreateParams) {
|
|
51
|
+
/* Overschrijf deze functie indien nodig */
|
|
52
|
+
}
|
|
53
|
+
async beforeUpdate(beforeUpdateParams) {
|
|
54
|
+
/* Overschrijf deze functie indien nodig */
|
|
55
|
+
}
|
|
56
|
+
async beforeSave(beforeSaveParams) {
|
|
57
|
+
/* Overschrijf deze functie indien nodig */
|
|
58
|
+
}
|
|
59
|
+
async afterCreate(afterCreateParams) {
|
|
60
|
+
/* Overschrijf deze functie indien nodig */
|
|
61
|
+
}
|
|
62
|
+
async afterUpdate(afterUpdateParams) {
|
|
63
|
+
/* Overschrijf deze functie indien nodig */
|
|
64
|
+
}
|
|
65
|
+
async afterSave(afterSaveParams) {
|
|
66
|
+
/* Overschrijf deze functie indien nodig */
|
|
67
|
+
}
|
|
68
|
+
async beforeDelete() { }
|
|
69
|
+
async afterDelete() { }
|
|
70
|
+
_execDBConnectionMethod(method, ...args) {
|
|
71
|
+
//@ts-ignore
|
|
72
|
+
return this.constructor._DbConnection[method](...args);
|
|
73
|
+
}
|
|
74
|
+
async _create({ preventBeforeCreate = false, preventAfterCreate = false } = {}) {
|
|
75
|
+
if (!preventBeforeCreate)
|
|
76
|
+
await this.beforeCreate();
|
|
77
|
+
const data = await _filerDataByTable(this), primaryKey = this.getPrimaryKey();
|
|
78
|
+
if (primaryKey in data)
|
|
79
|
+
delete data[primaryKey];
|
|
80
|
+
const result = { ...(await this._execDBConnectionMethod('create', this.getTable(), data)) };
|
|
81
|
+
const primaryKeyValue = primaryKey in result ? result[primaryKey] : false;
|
|
82
|
+
if (primaryKeyValue)
|
|
83
|
+
delete result[primaryKey];
|
|
84
|
+
// when creating a new entry, it is not necessary to add all columns
|
|
85
|
+
// and with an insert, you get all columns back from the db
|
|
86
|
+
// so recreate the propertyDescriptors
|
|
87
|
+
(0, BdORMBase_1.definePropertyDescriptors)(this, result);
|
|
88
|
+
// important AFTER definePropertyDescriptors
|
|
89
|
+
if (primaryKeyValue)
|
|
90
|
+
this.setProperty(primaryKey, primaryKeyValue);
|
|
91
|
+
if (!preventAfterCreate)
|
|
92
|
+
await this.afterCreate();
|
|
93
|
+
this._setChangedProperties([]);
|
|
94
|
+
return {
|
|
95
|
+
[primaryKey]: primaryKeyValue,
|
|
96
|
+
...result,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async _update({ preventBeforeUpdate = false, preventAfterUpdate = false } = {}) {
|
|
100
|
+
if (this.isNew())
|
|
101
|
+
throw new BdORMError_1.BdORMError('Cannot update an new instance');
|
|
102
|
+
if (!preventBeforeUpdate)
|
|
103
|
+
await this.beforeUpdate();
|
|
104
|
+
const data = await _filerDataByTable(this);
|
|
105
|
+
await this._execDBConnectionMethod('update', this.getTable(), data, {
|
|
106
|
+
primaryKey: this.getPrimaryKey(),
|
|
107
|
+
});
|
|
108
|
+
if (!preventAfterUpdate)
|
|
109
|
+
await this.afterUpdate();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Delete an instance from the database
|
|
113
|
+
*/
|
|
114
|
+
async delete() {
|
|
115
|
+
this.constructor.isMethodAllowed('delete', { throwErrorIfNotAllowed: true });
|
|
116
|
+
if (!this.id) {
|
|
117
|
+
throw new BdORMError_1.BdORMError('Cannot delete an instance without an id');
|
|
118
|
+
}
|
|
119
|
+
await this.beforeDelete();
|
|
120
|
+
await this._execDBConnectionMethod('delete', this.getTable(), this.getPrimaryKeyValue(), {
|
|
121
|
+
primaryKey: this.getPrimaryKey(),
|
|
122
|
+
});
|
|
123
|
+
await this.afterDelete();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Duplicates the current instance and if the options asDraft is not defined or false it will be saved it to the database
|
|
127
|
+
*/
|
|
128
|
+
async duplicate({ asDraft = false, newData = {}, excludeColumns = [] } = {}) {
|
|
129
|
+
const self = this.constructor;
|
|
130
|
+
self.isMethodAllowed('duplicate', { throwErrorIfNotAllowed: true });
|
|
131
|
+
if (!asDraft && this.isNew())
|
|
132
|
+
throw new BdORMError_1.BdORMError('Cannot duplicate an new instance');
|
|
133
|
+
const data = { ...this.getData(true) };
|
|
134
|
+
const primaryKey = this.getPrimaryKey();
|
|
135
|
+
if (primaryKey in data)
|
|
136
|
+
delete data[primaryKey];
|
|
137
|
+
for (const key in data) {
|
|
138
|
+
if (excludeColumns.includes(key) || self.PROPERTIES_NOT_ALLOWED_TO_DUPLICATE.includes(key)) {
|
|
139
|
+
delete data[key];
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (key in newData)
|
|
143
|
+
data[key] = typeof newData[key] === 'function' ? newData[key](data[key]) : newData[key];
|
|
144
|
+
}
|
|
145
|
+
const newInstance = new self(data);
|
|
146
|
+
if (!asDraft)
|
|
147
|
+
await newInstance.save({ preventBeforeSave: true, preventAfterSave: true });
|
|
148
|
+
return newInstance;
|
|
149
|
+
}
|
|
150
|
+
async save({ beforeSaveParams = {}, afterSaveParams = {}, preventAfterSave = false, preventBeforeSave = false, } = {}) {
|
|
151
|
+
this.constructor.isMethodAllowed('save', { throwErrorIfNotAllowed: true });
|
|
152
|
+
await this.beforeSave(beforeSaveParams);
|
|
153
|
+
if (this.isNew()) {
|
|
154
|
+
await this._create({ preventBeforeCreate: preventBeforeSave, preventAfterCreate: preventAfterSave });
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
await this._update({ preventBeforeUpdate: preventBeforeSave, preventAfterUpdate: preventAfterSave });
|
|
158
|
+
}
|
|
159
|
+
if (!preventAfterSave) {
|
|
160
|
+
await this.afterSave(afterSaveParams);
|
|
161
|
+
}
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
// STATICS
|
|
165
|
+
static async count(where, values) {
|
|
166
|
+
const result = await this._DbConnection.list(this.VIEW, {
|
|
167
|
+
columns: 'COUNT(*) as count',
|
|
168
|
+
filters: where,
|
|
169
|
+
values,
|
|
170
|
+
});
|
|
171
|
+
return result[0].count;
|
|
172
|
+
}
|
|
173
|
+
static async create(data) {
|
|
174
|
+
const instance = new this(data);
|
|
175
|
+
await instance.save();
|
|
176
|
+
return instance;
|
|
177
|
+
}
|
|
178
|
+
static async fetch(where, values) {
|
|
179
|
+
const results = await this._DbConnection.list(this.VIEW, {
|
|
180
|
+
columns: this.COLUMNS_TO_FETCH,
|
|
181
|
+
filters: where,
|
|
182
|
+
values,
|
|
183
|
+
});
|
|
184
|
+
return results.map((result) => new this(result));
|
|
185
|
+
}
|
|
186
|
+
static async fetchById(id) {
|
|
187
|
+
return this.fetchByPrimaryKey(id);
|
|
188
|
+
}
|
|
189
|
+
static async fetchOne(where, values) {
|
|
190
|
+
let limit;
|
|
191
|
+
if (typeof where === 'string')
|
|
192
|
+
where += ' LIMIT 1';
|
|
193
|
+
else
|
|
194
|
+
limit = 1;
|
|
195
|
+
const results = await this._DbConnection.list(this.VIEW, {
|
|
196
|
+
columns: this.COLUMNS_TO_FETCH,
|
|
197
|
+
filters: where,
|
|
198
|
+
limit,
|
|
199
|
+
values,
|
|
200
|
+
});
|
|
201
|
+
return results?.length ? new this(results[0]) : undefined;
|
|
202
|
+
}
|
|
203
|
+
static async fetchByPrimaryKey(primaryKeyValue) {
|
|
204
|
+
const primaryKey = await this._DbConnection.getTablePrimaryKey(this.TABLE);
|
|
205
|
+
const result = await this._DbConnection.read(this.VIEW, primaryKeyValue, { primaryKey });
|
|
206
|
+
if (!result)
|
|
207
|
+
throw new BdORMError_1.BdORMError(`No entry found for ${this.VIEW} with ${this.PRIMARY_KEY} ${primaryKeyValue}`);
|
|
208
|
+
return new this(result);
|
|
209
|
+
}
|
|
210
|
+
static setDbConnection(dbConnection) {
|
|
211
|
+
if (this._DbConnection)
|
|
212
|
+
throw new BdORMError_1.BdORMError('DbConnection already set');
|
|
213
|
+
this._DbConnection = dbConnection;
|
|
214
|
+
}
|
|
215
|
+
static clearDbConnection() {
|
|
216
|
+
//@ts-ignore
|
|
217
|
+
this._DbConnection = undefined;
|
|
218
|
+
}
|
|
219
|
+
static getDbConnection() {
|
|
220
|
+
return this._DbConnection;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
exports.default = BdORMCrud;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BdORMError = void 0;
|
|
4
|
+
class BdORMError extends Error {
|
|
5
|
+
constructor(msg) {
|
|
6
|
+
super(`[bd-Orm Error]: ${msg}`);
|
|
7
|
+
Error.captureStackTrace(this, BdORMError);
|
|
8
|
+
this.name = 'BdORMError';
|
|
9
|
+
}
|
|
10
|
+
static staticNotImplemented(methodName) {
|
|
11
|
+
return new BdORMError(`static ${methodName} not implemented! Set in derived class`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.BdORMError = BdORMError;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export default abstract class BdOrmDbConnection {
|
|
2
|
+
/**
|
|
3
|
+
* Create a new entry in the table
|
|
4
|
+
* @param table The table to create the entry in
|
|
5
|
+
* @param data The data to create the entry with
|
|
6
|
+
* @param params Additional parameters
|
|
7
|
+
* @returns The created entry
|
|
8
|
+
*/
|
|
9
|
+
abstract create(table: string, data: Record<string, any>, params?: {
|
|
10
|
+
primaryKey: string;
|
|
11
|
+
}): Promise<Record<string, any>>;
|
|
12
|
+
abstract read(table: string, primaryKeyValue: string | number, params?: {
|
|
13
|
+
primaryKey: string;
|
|
14
|
+
}): Promise<Record<string, any> | undefined>;
|
|
15
|
+
abstract update(table: string, data: any, params?: {
|
|
16
|
+
primaryKey: string;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
abstract delete(table: string, primaryKeyValue: string | number, params?: {
|
|
19
|
+
primaryKey: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
abstract list(table: string, params?: {
|
|
22
|
+
columns?: string | string[];
|
|
23
|
+
filters?: Record<string, any> | string;
|
|
24
|
+
limit?: number;
|
|
25
|
+
offset?: number;
|
|
26
|
+
orderBy?: string;
|
|
27
|
+
values?: any[];
|
|
28
|
+
}): Promise<Record<string, any>[]>;
|
|
29
|
+
abstract query(query: string, escapedValues?: any[]): Promise<Record<string, any>[]>;
|
|
30
|
+
abstract getTablePrimaryKey(table: string): Promise<string>;
|
|
31
|
+
abstract getTableColumns(table: string): Promise<string[]>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import BaseORM from './BdORMBase';
|
|
2
|
+
import cds from '@sap/cds';
|
|
3
|
+
/**
|
|
4
|
+
* Base class for CDS ORM
|
|
5
|
+
*/
|
|
6
|
+
export default abstract class cdsBaseOrm extends BaseORM {
|
|
7
|
+
/**
|
|
8
|
+
* Get the CDS model for the current table
|
|
9
|
+
* @returns the CDS model for the current table
|
|
10
|
+
*/
|
|
11
|
+
static get CDS_MODEL(): cds.entity | undefined;
|
|
12
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const BdORMBase_1 = __importDefault(require("./BdORMBase"));
|
|
7
|
+
const cds_1 = __importDefault(require("@sap/cds"));
|
|
8
|
+
/**
|
|
9
|
+
* Base class for CDS ORM
|
|
10
|
+
*/
|
|
11
|
+
class cdsBaseOrm extends BdORMBase_1.default {
|
|
12
|
+
/**
|
|
13
|
+
* Get the CDS model for the current table
|
|
14
|
+
* @returns the CDS model for the current table
|
|
15
|
+
*/
|
|
16
|
+
static get CDS_MODEL() {
|
|
17
|
+
const namespace = this.TABLE.split('_')[0].toLowerCase();
|
|
18
|
+
return Object.values(cds_1.default.entities(namespace)).find((o) => 'name' in o && o.name.toUpperCase().replace(/\./g, '_') === this.TABLE);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.default = cdsBaseOrm;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import BdOrmDbConnectionSQLBase from '../BdOrmDbConnectionSQLBase';
|
|
2
|
+
import cds from '@sap/cds';
|
|
3
|
+
export default class BdOrmDbConnectionCapHana extends BdOrmDbConnectionSQLBase {
|
|
4
|
+
protected _db(): Promise<cds.DatabaseService>;
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
getTableColumns(table: string): Promise<string[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Get the primary key of the table
|
|
9
|
+
*/
|
|
10
|
+
getTablePrimaryKey(table: string): Promise<string>;
|
|
11
|
+
create(table: string, data: Record<string, any>): Promise<any>;
|
|
12
|
+
/**
|
|
13
|
+
* Custom overwrites for list and read > hana stores the db columns in uppercase, so we need to convert the columns to what is defined in the cds file
|
|
14
|
+
*/
|
|
15
|
+
list(table: string, options?: {
|
|
16
|
+
columns?: string | string[];
|
|
17
|
+
filters?: Record<string, any> | string;
|
|
18
|
+
limit?: number;
|
|
19
|
+
offset?: number;
|
|
20
|
+
orderBy?: string;
|
|
21
|
+
values?: any[];
|
|
22
|
+
}): Promise<any>;
|
|
23
|
+
read(table: string, primaryKeyValue: string | number, { primaryKey }?: {
|
|
24
|
+
primaryKey?: string;
|
|
25
|
+
}): Promise<any>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/**
|
|
7
|
+
* Use this connection when your project is using SAP CAP and running a SAP HANA DB
|
|
8
|
+
* This class extends the BdOrmDbConnectionSQLBase and provides methods to interact with a SQLite database
|
|
9
|
+
* Need to work with unit tests? use the BdOrmDbConnectionCapSqlite class
|
|
10
|
+
*/
|
|
11
|
+
const BdORMError_1 = require("../../_/BdORMError");
|
|
12
|
+
const BdOrmDbConnectionSQLBase_1 = __importDefault(require("../BdOrmDbConnectionSQLBase"));
|
|
13
|
+
const utils_1 = require("./utils");
|
|
14
|
+
const cds_1 = __importDefault(require("@sap/cds"));
|
|
15
|
+
const PRIMARY_KEYS = {};
|
|
16
|
+
const TABLE_COLUMNS = {};
|
|
17
|
+
const remapResultColumns = (result, columns) => {
|
|
18
|
+
return Object.entries(result).reduce((resultMapped, [key, value]) => {
|
|
19
|
+
const column = columns.find(column => column.toUpperCase() === key.toUpperCase());
|
|
20
|
+
resultMapped[column ?? key] = value;
|
|
21
|
+
return resultMapped;
|
|
22
|
+
}, {});
|
|
23
|
+
};
|
|
24
|
+
class BdOrmDbConnectionCapHana extends BdOrmDbConnectionSQLBase_1.default {
|
|
25
|
+
async _db() {
|
|
26
|
+
return cds_1.default.db ? cds_1.default.db : await cds_1.default.connect.to('db');
|
|
27
|
+
}
|
|
28
|
+
async close() {
|
|
29
|
+
const db = await this._db();
|
|
30
|
+
//@ts-ignore jest zegt cds.disconnect does not exists
|
|
31
|
+
await db.disconnect();
|
|
32
|
+
}
|
|
33
|
+
async getTableColumns(table) {
|
|
34
|
+
const cdsColumns = (0, utils_1.getTableColumns)(table);
|
|
35
|
+
if (cdsColumns)
|
|
36
|
+
return cdsColumns;
|
|
37
|
+
if (TABLE_COLUMNS[table])
|
|
38
|
+
return TABLE_COLUMNS[table];
|
|
39
|
+
const tableInfo = await this.query(`SELECT COLUMN_NAME FROM TABLE_COLUMNS WHERE TABLE_NAME = '${table}'`);
|
|
40
|
+
if (tableInfo.length === 0)
|
|
41
|
+
throw new BdORMError_1.BdORMError(`No columns found for table ${table}`);
|
|
42
|
+
TABLE_COLUMNS[table] = tableInfo.map(info => info.COLUMN_NAME);
|
|
43
|
+
return TABLE_COLUMNS[table];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the primary key of the table
|
|
47
|
+
*/
|
|
48
|
+
async getTablePrimaryKey(table) {
|
|
49
|
+
const primaryKey = (0, utils_1.getTablePrimaryKey)(table);
|
|
50
|
+
if (primaryKey)
|
|
51
|
+
return primaryKey;
|
|
52
|
+
if (PRIMARY_KEYS[table])
|
|
53
|
+
return PRIMARY_KEYS[table];
|
|
54
|
+
const tableInfo = await this.query(`SELECT COLUMN_NAME FROM SYS.INDEX_COLUMNS WHERE TABLE_NAME = '${table}' AND CONSTRAINT = 'PRIMARY KEY'`);
|
|
55
|
+
PRIMARY_KEYS[table] = tableInfo[0]?.COLUMN_NAME;
|
|
56
|
+
if (!PRIMARY_KEYS[table])
|
|
57
|
+
throw new BdORMError_1.BdORMError(`No primary key found for table ${table}`);
|
|
58
|
+
return PRIMARY_KEYS[table];
|
|
59
|
+
}
|
|
60
|
+
async create(table, data) {
|
|
61
|
+
const columns = Object.keys(data), placeholders = columns.map(() => '?').join(','), pk = await this.getTablePrimaryKey(table);
|
|
62
|
+
await this.query(`INSERT INTO ${table} (${columns.join(',')}) VALUES (${placeholders})`, columns.map(column => data[column]));
|
|
63
|
+
const result = await this.query(`SELECT * FROM ${table} ORDER BY ${pk} DESC LIMIT 1`);
|
|
64
|
+
return result[0];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Custom overwrites for list and read > hana stores the db columns in uppercase, so we need to convert the columns to what is defined in the cds file
|
|
68
|
+
*/
|
|
69
|
+
async list(table, options = {}) {
|
|
70
|
+
const results = await super.list(table, options), columns = await this.getTableColumns(table);
|
|
71
|
+
return results?.map((result) => remapResultColumns(result, columns));
|
|
72
|
+
}
|
|
73
|
+
async read(table, primaryKeyValue, { primaryKey } = {}) {
|
|
74
|
+
const result = await super.read(table, primaryKeyValue, { primaryKey }), columns = await this.getTableColumns(table);
|
|
75
|
+
return result ? remapResultColumns(result, columns) : result;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.default = BdOrmDbConnectionCapHana;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type Definitions = {
|
|
2
|
+
[table: string]: {
|
|
3
|
+
primaryKey: string;
|
|
4
|
+
columnNames: string[];
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
declare const getDefinitions: () => Definitions | undefined;
|
|
8
|
+
declare const getTableDefinition: (table: string) => {
|
|
9
|
+
primaryKey: string;
|
|
10
|
+
columnNames: string[];
|
|
11
|
+
} | undefined;
|
|
12
|
+
declare const getTablePrimaryKey: (table: string) => string | undefined;
|
|
13
|
+
declare const getTableColumns: (table: string) => string[] | undefined;
|
|
14
|
+
export { getDefinitions, getTableDefinition, getTablePrimaryKey, getTableColumns };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getTableColumns = exports.getTablePrimaryKey = exports.getTableDefinition = exports.getDefinitions = void 0;
|
|
7
|
+
const cds_1 = __importDefault(require("@sap/cds"));
|
|
8
|
+
const _definitionCache = {};
|
|
9
|
+
const getDefinitions = () => {
|
|
10
|
+
if (Object.keys(_definitionCache).length)
|
|
11
|
+
return _definitionCache;
|
|
12
|
+
const cdsDefinitions = (cds_1.default?.model?.definitions ?? cds_1.default.entities ?? {});
|
|
13
|
+
if (Object.keys(cdsDefinitions).length === 0)
|
|
14
|
+
return undefined;
|
|
15
|
+
Object.entries(cdsDefinitions).forEach(([key, { elements, name }]) => {
|
|
16
|
+
if (elements === undefined)
|
|
17
|
+
return;
|
|
18
|
+
_definitionCache[name.toLowerCase().replace(/\./g, '_')] = {
|
|
19
|
+
primaryKey: Object.entries(elements).find(([_, { key }]) => key)?.[0] ?? '',
|
|
20
|
+
columnNames: Object.entries(elements).map(([column]) => column),
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
return _definitionCache;
|
|
24
|
+
};
|
|
25
|
+
exports.getDefinitions = getDefinitions;
|
|
26
|
+
const getTableDefinition = (table) => {
|
|
27
|
+
const definitions = getDefinitions();
|
|
28
|
+
if (!definitions)
|
|
29
|
+
return undefined;
|
|
30
|
+
const tableDefinition = definitions[table.toLowerCase().replace(/\./g, '_')];
|
|
31
|
+
if (!tableDefinition)
|
|
32
|
+
return undefined;
|
|
33
|
+
return tableDefinition;
|
|
34
|
+
};
|
|
35
|
+
exports.getTableDefinition = getTableDefinition;
|
|
36
|
+
const getTablePrimaryKey = (table) => {
|
|
37
|
+
const tableDefinition = getTableDefinition(table);
|
|
38
|
+
if (!tableDefinition)
|
|
39
|
+
return undefined;
|
|
40
|
+
return tableDefinition.primaryKey;
|
|
41
|
+
};
|
|
42
|
+
exports.getTablePrimaryKey = getTablePrimaryKey;
|
|
43
|
+
const getTableColumns = (table) => {
|
|
44
|
+
const tableDefinition = getTableDefinition(table);
|
|
45
|
+
if (!tableDefinition)
|
|
46
|
+
return undefined;
|
|
47
|
+
return tableDefinition.columnNames;
|
|
48
|
+
};
|
|
49
|
+
exports.getTableColumns = getTableColumns;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import BdOrmDbConnectionSQLBase from '../BdOrmDbConnectionSQLBase';
|
|
2
|
+
import cds from '@sap/cds';
|
|
3
|
+
export default class BdOrmDbConnectionCapSqlite extends BdOrmDbConnectionSQLBase {
|
|
4
|
+
private readonly _csvFiles;
|
|
5
|
+
private _deployed;
|
|
6
|
+
private _dbPath;
|
|
7
|
+
protected _db(): Promise<cds.DatabaseService>;
|
|
8
|
+
getDb(): Promise<cds.DatabaseService>;
|
|
9
|
+
constructor(dbPath: string, options?: {
|
|
10
|
+
csvFiles: string[];
|
|
11
|
+
});
|
|
12
|
+
getTableColumns(table: string): Promise<string[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Get the primary key of the table
|
|
15
|
+
*/
|
|
16
|
+
getTablePrimaryKey(table: string): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Fills the database with data from the CSV files provided in the constructor
|
|
19
|
+
* note the table will be emptied before filling it with the data from the CSV file
|
|
20
|
+
*/
|
|
21
|
+
fillTableFromCsvFile(table: string, csvFile: string): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/**
|
|
7
|
+
* Use this connection class when writing unit tests
|
|
8
|
+
*
|
|
9
|
+
* This class extends the BdOrmDbConnection and provides methods to interact with a SQLite in-memory database
|
|
10
|
+
* for unit testing purposes. It includes functionality to create, read, update, delete, and list records
|
|
11
|
+
* in the database, as well as to execute arbitrary queries.
|
|
12
|
+
*
|
|
13
|
+
* The class maintains a record of deployed databases and primary keys for tables to optimize database
|
|
14
|
+
* operations. It uses the SAP CDS framework for database connectivity and operations.
|
|
15
|
+
*
|
|
16
|
+
* it automatically fills the database with data from the CSV files that are found in the provided database folder
|
|
17
|
+
* or from the CSV files provided in the constructor
|
|
18
|
+
*/
|
|
19
|
+
const BdORMError_1 = require("../../_/BdORMError");
|
|
20
|
+
const BdOrmDbConnectionSQLBase_1 = __importDefault(require("../BdOrmDbConnectionSQLBase"));
|
|
21
|
+
const utils_1 = require("./utils");
|
|
22
|
+
const cds_1 = __importDefault(require("@sap/cds"));
|
|
23
|
+
const PRIMARY_KEYS = {};
|
|
24
|
+
const TABLE_INFO = {};
|
|
25
|
+
class BdOrmDbConnectionCapSqlite extends BdOrmDbConnectionSQLBase_1.default {
|
|
26
|
+
_csvFiles = [];
|
|
27
|
+
_deployed = false;
|
|
28
|
+
_dbPath = '';
|
|
29
|
+
async _db() {
|
|
30
|
+
let db;
|
|
31
|
+
if (!this._deployed) {
|
|
32
|
+
//@ts-ignore jest zegt cds.deploy does not exists
|
|
33
|
+
await cds_1.default.deploy(this._dbPath).to('sqlite::memory:');
|
|
34
|
+
db = await cds_1.default.connect.to('db');
|
|
35
|
+
if (!this._csvFiles.length)
|
|
36
|
+
await (0, utils_1.addCsvFilesFromDBFolder)(db, this._dbPath);
|
|
37
|
+
else {
|
|
38
|
+
for (const csvFile of this._csvFiles) {
|
|
39
|
+
await (0, utils_1.fillTableFromCsv)(db, csvFile.split('/').pop()?.replace('.csv', '').replace('-', '_'), csvFile);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
await (0, utils_1.deployHdbViewFiles)(db, this._dbPath);
|
|
43
|
+
this._deployed = true;
|
|
44
|
+
}
|
|
45
|
+
return db ?? cds_1.default.connect.to('db');
|
|
46
|
+
}
|
|
47
|
+
async getDb() {
|
|
48
|
+
return this._db();
|
|
49
|
+
}
|
|
50
|
+
constructor(dbPath, options) {
|
|
51
|
+
super();
|
|
52
|
+
this._csvFiles = options?.csvFiles || [];
|
|
53
|
+
this._dbPath = dbPath;
|
|
54
|
+
}
|
|
55
|
+
async getTableColumns(table) {
|
|
56
|
+
if (TABLE_INFO[table])
|
|
57
|
+
return TABLE_INFO[table].map(info => info.name);
|
|
58
|
+
const tableInfo = await this.query(`PRAGMA table_info(${table})`);
|
|
59
|
+
TABLE_INFO[table] = tableInfo;
|
|
60
|
+
return tableInfo.map(info => info.name);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the primary key of the table
|
|
64
|
+
*/
|
|
65
|
+
async getTablePrimaryKey(table) {
|
|
66
|
+
if (PRIMARY_KEYS[table])
|
|
67
|
+
return PRIMARY_KEYS[table];
|
|
68
|
+
const tableInfo = await this.query(`PRAGMA table_info(${table})`);
|
|
69
|
+
TABLE_INFO[table] = tableInfo;
|
|
70
|
+
const primaryKey = tableInfo.find(info => info.pk === 1)?.name;
|
|
71
|
+
if (!primaryKey)
|
|
72
|
+
throw new BdORMError_1.BdORMError(`No primary key found for table ${table}`);
|
|
73
|
+
PRIMARY_KEYS[table] = primaryKey;
|
|
74
|
+
return primaryKey;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Fills the database with data from the CSV files provided in the constructor
|
|
78
|
+
* note the table will be emptied before filling it with the data from the CSV file
|
|
79
|
+
*/
|
|
80
|
+
async fillTableFromCsvFile(table, csvFile) {
|
|
81
|
+
const db = await this._db();
|
|
82
|
+
await (0, utils_1.fillTableFromCsv)(db, table, csvFile);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.default = BdOrmDbConnectionCapSqlite;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import cds from '@sap/cds';
|
|
2
|
+
declare const log: (message: string) => void, fillTableFromCsv: (db: cds.DatabaseService, table: string, csvFile: string) => Promise<void>, findFiles: (currentPath: string, extname: string) => Promise<string[]>, addCsvFilesFromDBFolder: (db: cds.DatabaseService, dbPath: string) => Promise<void>, deployHdbViewFiles: (db: cds.DatabaseService, dbPath: string) => Promise<void>;
|
|
3
|
+
export { addCsvFilesFromDBFolder, fillTableFromCsv, findFiles, log, deployHdbViewFiles };
|