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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use this connection class when writing unit tests
|
|
3
|
+
*
|
|
4
|
+
* This class extends the BdOrmDbConnection and provides methods to interact with a SQLite in-memory database
|
|
5
|
+
* for unit testing purposes. It includes functionality to create, read, update, delete, and list records
|
|
6
|
+
* in the database, as well as to execute arbitrary queries.
|
|
7
|
+
*
|
|
8
|
+
* The class maintains a record of deployed databases and primary keys for tables to optimize database
|
|
9
|
+
* operations. It uses the SAP CDS framework for database connectivity and operations.
|
|
10
|
+
*
|
|
11
|
+
* it automatically fills the database with data from the CSV files that are found in the provided database folder
|
|
12
|
+
* or from the CSV files provided in the constructor
|
|
13
|
+
*/
|
|
14
|
+
import { BdORMError } from '../../_/BdORMError';
|
|
15
|
+
import BdOrmDbConnectionSQLBase from '../BdOrmDbConnectionSQLBase';
|
|
16
|
+
import { addCsvFilesFromDBFolder, deployHdbViewFiles, fillTableFromCsv } from './utils';
|
|
17
|
+
import cds from '@sap/cds';
|
|
18
|
+
|
|
19
|
+
type TableInfo = { pk: number; name: string; type: string };
|
|
20
|
+
|
|
21
|
+
const PRIMARY_KEYS: Record<string, string> = {};
|
|
22
|
+
const TABLE_INFO: Record<string, TableInfo[]> = {};
|
|
23
|
+
|
|
24
|
+
export default class BdOrmDbConnectionCapSqlite extends BdOrmDbConnectionSQLBase {
|
|
25
|
+
private readonly _csvFiles: string[] = [];
|
|
26
|
+
private _deployed: boolean = false;
|
|
27
|
+
private _dbPath: string = '';
|
|
28
|
+
|
|
29
|
+
protected async _db() {
|
|
30
|
+
let db;
|
|
31
|
+
if (!this._deployed) {
|
|
32
|
+
//@ts-ignore jest zegt cds.deploy does not exists
|
|
33
|
+
await cds.deploy(this._dbPath).to('sqlite::memory:');
|
|
34
|
+
db = await cds.connect.to('db');
|
|
35
|
+
if (!this._csvFiles.length) await addCsvFilesFromDBFolder(db, this._dbPath);
|
|
36
|
+
else {
|
|
37
|
+
for (const csvFile of this._csvFiles) {
|
|
38
|
+
await fillTableFromCsv(
|
|
39
|
+
db,
|
|
40
|
+
csvFile.split('/').pop()?.replace('.csv', '').replace('-', '_') as string,
|
|
41
|
+
csvFile,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
await deployHdbViewFiles(db, this._dbPath);
|
|
46
|
+
this._deployed = true;
|
|
47
|
+
}
|
|
48
|
+
return db ?? cds.connect.to('db');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public async getDb() {
|
|
52
|
+
return this._db();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
constructor(dbPath: string, options?: { csvFiles: string[] }) {
|
|
56
|
+
super();
|
|
57
|
+
this._csvFiles = options?.csvFiles || [];
|
|
58
|
+
this._dbPath = dbPath;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async getTableColumns(table: string) {
|
|
62
|
+
if (TABLE_INFO[table]) return TABLE_INFO[table].map(info => info.name);
|
|
63
|
+
const tableInfo: [TableInfo] = await this.query(`PRAGMA table_info(${table})`);
|
|
64
|
+
TABLE_INFO[table] = tableInfo;
|
|
65
|
+
return tableInfo.map(info => info.name);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get the primary key of the table
|
|
70
|
+
*/
|
|
71
|
+
public async getTablePrimaryKey(table: string) {
|
|
72
|
+
if (PRIMARY_KEYS[table]) return PRIMARY_KEYS[table];
|
|
73
|
+
const tableInfo: [TableInfo] = await this.query(`PRAGMA table_info(${table})`);
|
|
74
|
+
TABLE_INFO[table] = tableInfo;
|
|
75
|
+
const primaryKey = tableInfo.find(info => info.pk === 1)?.name;
|
|
76
|
+
if (!primaryKey) throw new BdORMError(`No primary key found for table ${table}`);
|
|
77
|
+
PRIMARY_KEYS[table] = primaryKey;
|
|
78
|
+
return primaryKey;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fills the database with data from the CSV files provided in the constructor
|
|
83
|
+
* note the table will be emptied before filling it with the data from the CSV file
|
|
84
|
+
*/
|
|
85
|
+
public async fillTableFromCsvFile(table: string, csvFile: string) {
|
|
86
|
+
const db = await this._db();
|
|
87
|
+
await fillTableFromCsv(db, table, csvFile);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BdORMError } from '../../_/BdORMError';
|
|
2
|
+
import cds from '@sap/cds';
|
|
3
|
+
import csv from 'csv-parser';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
const log = (message: string) => {
|
|
8
|
+
console.log(`[BdOrmDbConnectionCapSqlite] ${message}`);
|
|
9
|
+
},
|
|
10
|
+
fillTableFromCsv = async (db: cds.DatabaseService, table: string, csvFile: string) => {
|
|
11
|
+
try {
|
|
12
|
+
const rows: Record<string, any>[] = [];
|
|
13
|
+
await new Promise<void>((resolve, reject) => {
|
|
14
|
+
fs.createReadStream(csvFile)
|
|
15
|
+
.pipe(csv({ separator: ';' }))
|
|
16
|
+
.on('data', (data: Record<string, any>) => rows.push(data))
|
|
17
|
+
.on('end', () => resolve())
|
|
18
|
+
.on('error', error => {
|
|
19
|
+
reject(error);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
// empty db
|
|
23
|
+
await db.run(`DELETE FROM ${table}`);
|
|
24
|
+
for (const row of rows) {
|
|
25
|
+
const columns = Object.keys(row),
|
|
26
|
+
placeholders = columns.map(() => '?').join(',');
|
|
27
|
+
await db.run(
|
|
28
|
+
`INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`,
|
|
29
|
+
Object.values(row),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
log(`Added ${rows.length} rows in ${table} from CSV file: ${csvFile}`);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw new BdORMError(`Error while filling table from CSV: ${(error as Error).message}`);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
findFiles = async (currentPath: string, extname: string) => {
|
|
38
|
+
let files: string[] = [];
|
|
39
|
+
//@ts-ignore
|
|
40
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
41
|
+
//@ts-ignore
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const entryPath = path.join(currentPath, entry.name);
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
const subfiles = await findFiles(entryPath, extname);
|
|
46
|
+
files.push(...subfiles);
|
|
47
|
+
} else if (entry.isFile() && path.extname(entry.name) === extname) {
|
|
48
|
+
files.push(entryPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return files;
|
|
52
|
+
},
|
|
53
|
+
addCsvFilesFromDBFolder = async (db: cds.DatabaseService, dbPath: string) => {
|
|
54
|
+
try {
|
|
55
|
+
const csvFiles = await findFiles(dbPath, '.csv');
|
|
56
|
+
for (const csvFile of csvFiles) {
|
|
57
|
+
await fillTableFromCsv(
|
|
58
|
+
db,
|
|
59
|
+
csvFile.split('/').pop()?.replace('.csv', '').replace('-', '_') as string,
|
|
60
|
+
csvFile,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new BdORMError(`Error while adding CsvFilesFromDbFolder: ${(error as Error).message}`);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
deployHdbViewFiles = async (db: cds.DatabaseService, dbPath: string) => {
|
|
68
|
+
const hdbViewFiles = await findFiles(dbPath, '.hdbview');
|
|
69
|
+
for (const hdbViewFile of hdbViewFiles) {
|
|
70
|
+
const hdbView = fs.readFileSync(hdbViewFile, 'utf8');
|
|
71
|
+
const m = await db.run(`create ${hdbView}`);
|
|
72
|
+
log(`Deployed HDB view from file: ${hdbViewFile}`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
export { addCsvFilesFromDBFolder, fillTableFromCsv, findFiles, log, deployHdbViewFiles };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import BdOrmDbConnectionCapSqlite from './BdOrmDbConnectionCapSqlite';
|
|
2
|
+
|
|
3
|
+
describe('bd-orm baseORMCrud', () => {
|
|
4
|
+
const dbConnection = new BdOrmDbConnectionCapSqlite(__dirname + '/../test-assets/db');
|
|
5
|
+
it('check if csv import has worked and if read works', async () => {
|
|
6
|
+
const { firstname } = await dbConnection.read('BDORM_USER', 2);
|
|
7
|
+
expect(firstname).toBe('Barry');
|
|
8
|
+
});
|
|
9
|
+
it('create worked', async () => {
|
|
10
|
+
const user = await dbConnection.create('BDORM_USER', { firstname: 'John', lastname: 'Wayne' });
|
|
11
|
+
expect(user.firstname).toBe('John');
|
|
12
|
+
expect(user.lastname).toBe('Wayne');
|
|
13
|
+
expect(user.id).toBe(3);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('update worked', async () => {
|
|
17
|
+
await dbConnection.update('BDORM_USER', { id: 1, firstname: 'Boomer' });
|
|
18
|
+
const { firstname } = await dbConnection.read('BDORM_USER', 1);
|
|
19
|
+
expect(firstname).toBe('Boomer');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('delete worked', async () => {
|
|
23
|
+
const users = await dbConnection.list('BDORM_USER');
|
|
24
|
+
await dbConnection.delete('BDORM_USER', 1);
|
|
25
|
+
const deleted = await dbConnection.read('BDORM_USER', 1);
|
|
26
|
+
const usersAfterDelete = await dbConnection.list('BDORM_USER');
|
|
27
|
+
expect(deleted).toBeUndefined();
|
|
28
|
+
expect(usersAfterDelete).toHaveLength(users.length - 1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('query worked', async () => {
|
|
32
|
+
const users = await dbConnection.query('SELECT * FROM BDORM_USER WHERE id = 2');
|
|
33
|
+
expect(users[0].firstname).toBe('Barry');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('query a view worked', async () => {
|
|
37
|
+
const user = await dbConnection.create('BDORM_USER', { firstname: 'Test', lastname: 'Persoon' });
|
|
38
|
+
const users = await dbConnection.query('SELECT * FROM USER_VIEW WHERE lastname = ?', ['Persoon']);
|
|
39
|
+
expect(users[0].fullname).toBe('Test Persoon');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use this class as a base class for all SQL database connections
|
|
3
|
+
* It provides methods to interact with the database, such as create, read, update, delete, and list records.
|
|
4
|
+
* It also provides methods to execute raw SQL queries and manage database connections.
|
|
5
|
+
*
|
|
6
|
+
* This class is not meant to be used directly, but rather as a base class for other database connection classes.
|
|
7
|
+
* make sure yo u implement the _db method to return the correct database connection
|
|
8
|
+
* and the getTableColumns and getTablePrimaryKey methods to return the correct table columns and primary key
|
|
9
|
+
*/
|
|
10
|
+
import { BdORMError } from '../_/BdORMError';
|
|
11
|
+
import BdOrmDbConnection from '../_/BdOrmConnection';
|
|
12
|
+
|
|
13
|
+
abstract class BdOrmDbConnectionSQLBase extends BdOrmDbConnection {
|
|
14
|
+
protected async _db(): Promise<{ run: (query: string, values?: any[]) => Promise<any> }> {
|
|
15
|
+
throw new BdORMError(`_db not implemented for ${this.constructor.name}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public async close() {
|
|
19
|
+
throw new BdORMError(`close not implemented for ${this.constructor.name}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async create(table: string, data: Record<string, any>) {
|
|
23
|
+
const columns = Object.keys(data),
|
|
24
|
+
placeholders = columns.map(() => '?').join(',');
|
|
25
|
+
const { lastInsertRowid }: { changes: number; lastInsertRowid: number } = await this.query(
|
|
26
|
+
`INSERT INTO ${table} (${columns.join(',')}) VALUES (${placeholders})`,
|
|
27
|
+
columns.map(column => data[column]),
|
|
28
|
+
);
|
|
29
|
+
const pk = await this.getTablePrimaryKey(table);
|
|
30
|
+
const result = await this.query(`SELECT * FROM ${table} WHERE ${pk} = ${lastInsertRowid}`);
|
|
31
|
+
return result[0];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async delete(table: string, primaryKeyValue: string | number) {
|
|
35
|
+
const primaryKey = await this.getTablePrimaryKey(table);
|
|
36
|
+
await this.query(`DELETE FROM ${table} WHERE ${primaryKey} = ?`, [primaryKeyValue]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async getTableColumns(table: string): Promise<string[]> {
|
|
40
|
+
throw new BdORMError(`getTableColumns not implemented for ${this.constructor.name}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the primary key of the table
|
|
45
|
+
*/
|
|
46
|
+
public async getTablePrimaryKey(table: string): Promise<string> {
|
|
47
|
+
throw new BdORMError(`getTablePrimaryKey not implemented for ${this.constructor.name}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public async list(
|
|
51
|
+
table: string,
|
|
52
|
+
{
|
|
53
|
+
filters,
|
|
54
|
+
columns = '*',
|
|
55
|
+
orderBy,
|
|
56
|
+
limit,
|
|
57
|
+
offset,
|
|
58
|
+
values = [],
|
|
59
|
+
}: {
|
|
60
|
+
columns?: string | string[];
|
|
61
|
+
filters?: Record<string, any> | string;
|
|
62
|
+
limit?: number;
|
|
63
|
+
offset?: number;
|
|
64
|
+
orderBy?: string;
|
|
65
|
+
values?: any[];
|
|
66
|
+
} = {},
|
|
67
|
+
) {
|
|
68
|
+
let query = `SELECT ${columns} FROM ${table}`;
|
|
69
|
+
if (typeof filters === 'string') {
|
|
70
|
+
query += ` WHERE ${filters}`;
|
|
71
|
+
} else {
|
|
72
|
+
if (typeof filters === 'object' && Object.keys(filters).length > 0) {
|
|
73
|
+
values = [];
|
|
74
|
+
const whereClause = Object.keys(filters)
|
|
75
|
+
.map(key => `${key} = ?`)
|
|
76
|
+
.join(' AND ');
|
|
77
|
+
query += ` WHERE ${whereClause}`;
|
|
78
|
+
values.push(...Object.values(filters));
|
|
79
|
+
}
|
|
80
|
+
if (orderBy) query += ` ORDER BY ${orderBy}`;
|
|
81
|
+
if (limit) query += ` LIMIT ${limit}`;
|
|
82
|
+
if (offset) query += ` OFFSET ${offset}`;
|
|
83
|
+
}
|
|
84
|
+
return this.query(query, values);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public async query(query: string, escapedValues: any[] = []) {
|
|
88
|
+
const db = await this._db();
|
|
89
|
+
try {
|
|
90
|
+
return await db.run(query, escapedValues);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new BdORMError(
|
|
93
|
+
`Error executing db query: ${(error as Error)?.message}. \nQuery: ${query} \nValues: ${escapedValues}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Alias for query
|
|
99
|
+
*/
|
|
100
|
+
public async run(query: string, escapedValues: any[] = []) {
|
|
101
|
+
return this.query(query, escapedValues);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public async read(table: string, primaryKeyValue: string | number, { primaryKey }: { primaryKey?: string } = {}) {
|
|
105
|
+
const pk = primaryKey ?? (await this.getTablePrimaryKey(table));
|
|
106
|
+
const result = await this.query(`SELECT * FROM ${table} WHERE ${pk} = ?`, [primaryKeyValue]);
|
|
107
|
+
return result?.[0] || undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public async update(table: string, data: any) {
|
|
111
|
+
const setClause = Object.keys(data)
|
|
112
|
+
.map(key => `${key} = ?`)
|
|
113
|
+
.join(', '),
|
|
114
|
+
primaryKey = await this.getTablePrimaryKey(table),
|
|
115
|
+
primaryKeyValue = data[primaryKey];
|
|
116
|
+
if (!primaryKeyValue)
|
|
117
|
+
throw new BdORMError(`No primary key value found for table ${table}, data: ${JSON.stringify(data)}`);
|
|
118
|
+
await this.query(`UPDATE ${table} SET ${setClause} WHERE ${primaryKey} = ?`, [
|
|
119
|
+
...Object.values(data),
|
|
120
|
+
primaryKeyValue,
|
|
121
|
+
]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export default BdOrmDbConnectionSQLBase;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import BdOrmDbConnectionCapSqlite from './dbConnections/BdOrmDbConnectionCapSqlite';
|
|
2
|
+
import BaseORMCrud from './_/BdORMCrud';
|
|
3
|
+
import BdOrmDbConnection from './_/BdOrmConnection';
|
|
4
|
+
import BdOrmDbConnectionCapHana from './dbConnections/BdOrmDbConnectionCapHana';
|
|
5
|
+
import BdOrmDbConnectionSQLBase from './dbConnections/BdOrmDbConnectionSQLBase';
|
|
6
|
+
|
|
7
|
+
export default class BdOrm extends BaseORMCrud {}
|
|
8
|
+
export { BdOrmDbConnection, BdOrmDbConnectionCapSqlite, BdOrmDbConnectionCapHana, BdOrmDbConnectionSQLBase };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import BdOrm from '..';
|
|
2
|
+
|
|
3
|
+
interface Post {
|
|
4
|
+
id: number;
|
|
5
|
+
title: string;
|
|
6
|
+
content: string;
|
|
7
|
+
userId: number;
|
|
8
|
+
}
|
|
9
|
+
class Post extends BdOrm {
|
|
10
|
+
static readonly DB_TABLE: string = 'posts';
|
|
11
|
+
async fetchUser() {
|
|
12
|
+
//TODO; //User.fetch
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default Post;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import BdOrm from '..';
|
|
2
|
+
|
|
3
|
+
interface User {
|
|
4
|
+
id: number;
|
|
5
|
+
firstname: string;
|
|
6
|
+
lastname: string;
|
|
7
|
+
fullname?: string; // view property
|
|
8
|
+
email: string;
|
|
9
|
+
dateCreated: string;
|
|
10
|
+
}
|
|
11
|
+
class User extends BdOrm {
|
|
12
|
+
static readonly DB_TABLE = 'bdorm_user';
|
|
13
|
+
static readonly DB_VIEW = 'user_view';
|
|
14
|
+
static readonly PROPERTIES_NOT_ALLOWED_TO_CHANGE = ['dateCreated'];
|
|
15
|
+
static readonly PROPERTIES_NOT_ALLOWED_TO_DUPLICATE = ['dateCreated'];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default User;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
namespace bdOrm;
|
|
2
|
+
|
|
3
|
+
entity User {
|
|
4
|
+
key id : Integer;
|
|
5
|
+
firstname : String(50);
|
|
6
|
+
lastname : String(50);
|
|
7
|
+
email : String(50);
|
|
8
|
+
dateCreated : DateTime default $now;
|
|
9
|
+
options : String(500);
|
|
10
|
+
deleted : DateTime;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
entity Post {
|
|
14
|
+
key id : Integer;
|
|
15
|
+
userId : Integer;
|
|
16
|
+
title : String(50);
|
|
17
|
+
content : String(50);
|
|
18
|
+
dateCreated : DateTime default $now;
|
|
19
|
+
deleted : DateTime;
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { it, expect } from '@jest/globals';
|
|
2
|
+
export const expectErrorMessage = async (fn: () => Promise<unknown>, message: string) => {
|
|
3
|
+
try {
|
|
4
|
+
await fn();
|
|
5
|
+
//@ts-ignore
|
|
6
|
+
fail('Expected an error to be thrown');
|
|
7
|
+
} catch (error) {
|
|
8
|
+
//@ts-ignore (excepct = jest)
|
|
9
|
+
if (error instanceof Error) return expect(error.message).toContain(message);
|
|
10
|
+
console.log(error);
|
|
11
|
+
//@ts-ignore (fail = jest fail)
|
|
12
|
+
fail('error is not of type Error');
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const expectToBes = (tests: Record<string, { test: () => any; toBe: any }>) => {
|
|
17
|
+
Object.entries(tests).forEach(([title, { test, toBe }]) => {
|
|
18
|
+
it(`${title} returns ${toBe}`, () => {
|
|
19
|
+
const value = test();
|
|
20
|
+
expect(value).toBe(toBe);
|
|
21
|
+
expect(value).not.toBe('RANDOM VALUE 1234567890');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"allowJs": true,
|
|
12
|
+
"declaration": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "**/*.test.ts"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const exec = require('child_process').exec;
|
|
3
|
+
const core = require('@actions/core');
|
|
4
|
+
const semver = require('semver');
|
|
5
|
+
|
|
6
|
+
const successColor = '\x1b[42m';
|
|
7
|
+
const errorColor = '\x1b[41m';
|
|
8
|
+
|
|
9
|
+
const packageJson = fs.readFileSync('./package.json', 'utf8');
|
|
10
|
+
const { name: packageName, version: packageVersion } = JSON.parse(packageJson);
|
|
11
|
+
|
|
12
|
+
const logResult = (message, { failed } = {}) => console.log(failed ? errorColor : successColor, message, '\x1b[0m');
|
|
13
|
+
|
|
14
|
+
exec(`npm view ${packageName} dist-tags --json`, (err, stdout) => {
|
|
15
|
+
let isFirstDeployment = false;
|
|
16
|
+
|
|
17
|
+
if (err) {
|
|
18
|
+
/* if the is an error with 'code E404', it means that there is no package
|
|
19
|
+
* with this name found in the registry, which means it is the first deployment!
|
|
20
|
+
* is the error someting else, we exit
|
|
21
|
+
*/
|
|
22
|
+
if (!err.message.includes('code E404')) {
|
|
23
|
+
console.error(err);
|
|
24
|
+
process.exit(1); // Exit with error code
|
|
25
|
+
}
|
|
26
|
+
isFirstDeployment = true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// check whether packageVersion is a proper semantic version
|
|
30
|
+
if (!semver.valid(packageVersion)) {
|
|
31
|
+
logResult('❌ De versie in het package.json is niet valide! Bekijk https://semver.org/ voor meer info.', {
|
|
32
|
+
failed: true,
|
|
33
|
+
});
|
|
34
|
+
process.exit(1); // Exit with error code
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(stdout);
|
|
38
|
+
// parse output of stdout, but keep in mind whether it is the first deployment or not!
|
|
39
|
+
const { beta, latest, next } = JSON.parse(
|
|
40
|
+
isFirstDeployment || stdout.trim().length === 0
|
|
41
|
+
? JSON.stringify({ beta: undefined, latest: undefined, next: undefined })
|
|
42
|
+
: stdout.trim(),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
console.log(`versiecheck:`);
|
|
46
|
+
console.log(` - laatst gedeployede latest versie: ${latest ?? 'geen eerdere release'}`);
|
|
47
|
+
console.log(` - laatste beta versie: ${beta ?? 'geen eerdere release'}`);
|
|
48
|
+
console.log(` - laatste next versie: ${next ?? 'geen eerdere release'}`);
|
|
49
|
+
console.log(`versie in package.json: ${packageVersion}`);
|
|
50
|
+
|
|
51
|
+
// this function will exit the process if local version is lower than on the registry
|
|
52
|
+
const verifyVersion = (version, errorMessage) => {
|
|
53
|
+
if (!version) {
|
|
54
|
+
// no version information available, falling back to default (0.0.0)
|
|
55
|
+
version = '0.0.0';
|
|
56
|
+
}
|
|
57
|
+
if (semver.lte(packageVersion, version)) {
|
|
58
|
+
logResult(`❌ ${errorMessage}`, { failed: true });
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const prereleaseComponents = semver.prerelease(packageVersion);
|
|
64
|
+
const isPrerelease = !!prereleaseComponents;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* we'll use this tag as output of this file. We need to determine what kind of version the package.json is using now..
|
|
68
|
+
* supported release kinds;
|
|
69
|
+
* - regular release. Example: 0.7.1 => publishtag 'latest'
|
|
70
|
+
* - beta release. Example: 1.3.0-beta.1 => publishtag 'beta'
|
|
71
|
+
* - next release. Example: 1.0.0-next.0 => publishtag 'next'
|
|
72
|
+
*/
|
|
73
|
+
let publishTag;
|
|
74
|
+
|
|
75
|
+
if (!isPrerelease) {
|
|
76
|
+
verifyVersion(
|
|
77
|
+
latest,
|
|
78
|
+
`package.json versie is nog niet opgehoogd ten opzichte van laatste release op npm (${packageVersion} vs ${latest})`,
|
|
79
|
+
);
|
|
80
|
+
publishTag = 'latest';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isPrerelease) {
|
|
84
|
+
const [kind] = prereleaseComponents;
|
|
85
|
+
if (kind === 'beta') {
|
|
86
|
+
// beta version
|
|
87
|
+
verifyVersion(
|
|
88
|
+
beta,
|
|
89
|
+
`package.json versie is nog niet opgehoogd ten opzichte van laatste release op npm (${packageVersion} vs ${beta})`,
|
|
90
|
+
);
|
|
91
|
+
publishTag = 'beta';
|
|
92
|
+
} else if (kind === 'next') {
|
|
93
|
+
// next version
|
|
94
|
+
verifyVersion(
|
|
95
|
+
next,
|
|
96
|
+
`package.json versie is nog niet opgehoogd ten opzichte van laatste release op npm (${packageVersion} vs ${next})`,
|
|
97
|
+
);
|
|
98
|
+
publishTag = 'next';
|
|
99
|
+
} else {
|
|
100
|
+
console.error(
|
|
101
|
+
`een onbekend soort prerelease is gevonden; '${kind}'. We ondersteunen enkel 'next' en 'beta'`,
|
|
102
|
+
);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// if we've arrived here, we are sure the package.json version is a valid semver string and we determined our publish tag
|
|
108
|
+
// this is used for github actions
|
|
109
|
+
core.setOutput('publishtag', publishTag);
|
|
110
|
+
isFirstDeployment
|
|
111
|
+
? logResult('✔ Eerste release van dit package!')
|
|
112
|
+
: logResult('✔ package.json versie is opgehoogd ten opzichte van de vorige versie!');
|
|
113
|
+
});
|