remult 0.16.3-exp.3 → 0.16.3-exp.4
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/README.md +27 -5
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/postgres/postgres-data-provider.d.ts +1 -3
- package/esm/postgres/postgres-data-provider.js +1 -11
- package/esm/postgres/schema-builder.js +20 -20
- package/esm/remult-express.js +2 -0
- package/esm/remult-fastify.js +2 -0
- package/esm/remult-knex/index.d.ts +28 -4
- package/esm/remult-knex/index.js +33 -46
- package/esm/remult-mongo.d.ts +2 -8
- package/esm/remult-mongo.js +8 -22
- package/esm/server/expressBridge.d.ts +7 -0
- package/esm/server/expressBridge.js +12 -24
- package/esm/server/initAsyncHooks.d.ts +1 -0
- package/esm/server/initAsyncHooks.js +10 -0
- package/esm/src/backend-tests/my-test.backend-spec.js +2 -66
- package/esm/src/data-providers/sql-database.d.ts +0 -2
- package/esm/src/data-providers/sql-database.js +15 -26
- package/esm/src/data-providers/web-sql-data-provider.d.ts +0 -3
- package/esm/src/data-providers/web-sql-data-provider.js +8 -16
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.d.ts +14 -13
- package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +19 -52
- package/esm/src/filter/filter-interfaces.d.ts +3 -3
- package/esm/src/filter/filter-interfaces.js +1 -1
- package/esm/src/remult3/RepositoryImplementation.d.ts +2 -1
- package/esm/src/remult3/RepositoryImplementation.js +12 -17
- package/esm/src/shared-tests/db-tests-setup.js +2 -1
- package/esm/src/tests/entityForCustomFilter.js +7 -6
- package/esm/src/tests/frontend-database-tests-setup.spec.js +2 -1
- package/esm/src/tests/testHelper.spec.js +1 -0
- package/index.d.ts +2 -1
- package/index.js +3 -3
- package/package.json +1 -1
- package/postgres/postgres-data-provider.d.ts +1 -3
- package/postgres/postgres-data-provider.js +1 -11
- package/postgres/schema-builder.js +19 -19
- package/remult-express.js +2 -0
- package/remult-fastify.js +2 -0
- package/remult-knex/index.d.ts +28 -4
- package/remult-knex/index.js +38 -68
- package/remult-mongo.d.ts +2 -8
- package/remult-mongo.js +8 -39
- package/server/expressBridge.d.ts +7 -0
- package/server/expressBridge.js +13 -34
- package/server/initAsyncHooks.d.ts +1 -0
- package/server/initAsyncHooks.js +14 -0
- package/src/backend-tests/my-test.backend-spec.js +0 -212
- package/src/data-providers/sql-database.d.ts +0 -2
- package/src/data-providers/sql-database.js +14 -25
- package/src/data-providers/web-sql-data-provider.d.ts +0 -3
- package/src/data-providers/web-sql-data-provider.js +7 -15
- package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +14 -13
- package/src/filter/filter-consumer-bridge-to-sql-request.js +46 -95
- package/src/filter/filter-interfaces.d.ts +3 -3
- package/src/filter/filter-interfaces.js +1 -1
- package/src/remult3/RepositoryImplementation.d.ts +2 -1
- package/src/remult3/RepositoryImplementation.js +16 -31
- package/src/shared-tests/db-tests-setup.js +3 -2
- package/src/tests/entityForCustomFilter.js +7 -6
- package/src/tests/frontend-database-tests-setup.spec.js +5 -4
- package/src/tests/testHelper.spec.js +4 -3
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<img src="https://github.com/remult/remult/raw/master/docs/.vuepress/public/logo.png" width="140" height="140">
|
|
4
4
|
</a>
|
|
5
5
|
<h1>Remult</h1>
|
|
6
|
+
<p>A CRUD framework for full-stack TypeScript</p>
|
|
6
7
|
<a href="https://circleci.com/gh/remult/remult/tree/master" rel="nofollow">
|
|
7
8
|
<img alt="CircleCI" src="https://circleci.com/gh/remult/remult/tree/master.svg?style=shield">
|
|
8
9
|
</a>
|
|
@@ -24,14 +25,28 @@
|
|
|
24
25
|
<a href="https://twitter.com/intent/tweet?text=Remult%20-%20open-source%20CRUD%20framework%20for%20full-stack%20TypeScript%0A%0A%40remultjs%0A&url=https%3A%2F%2Fgithub.com%2Fremult%2Fremult">
|
|
25
26
|
<img alt="Twitter URL" src="https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Ftext%3DRemult%2520-%2520open-source%2520CRUD%2520framework%2520for%2520full-stack%2520TypeScript%250A%250A%2540remultjs%250A%26url%3Dhttps%253A%252F%252Fgithub.com%252Fremult%252Fremult">
|
|
26
27
|
</a>
|
|
27
|
-
<a href="https://twitter.com/remultjs" rel="nofollow">
|
|
28
|
-
<img alt="Twitter Follow" src="https://img.shields.io/twitter/url.svg?label=Follow%20%40remultjs&style=social&url=https%3A%2F%2Ftwitter.com%2FRemultJs">
|
|
29
|
-
</a>
|
|
30
28
|
</div>
|
|
29
|
+
|
|
30
|
+
<br/>
|
|
31
|
+
|
|
32
|
+
<div align="center">
|
|
33
|
+
<a href="#getting-started">Getting Started</a> |
|
|
34
|
+
<a href="#documentation">Documentation</a> |
|
|
35
|
+
<a href="#example-apps">Example Apps</a>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
31
38
|
<hr/>
|
|
39
|
+
<br/>
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
<a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
|
|
42
|
+
<p align="center">
|
|
43
|
+
<img src="https://github.com/remult/remult/raw/master/static/images/video-thumbnail.jpg" alt="Video thumbnail" width="320" />
|
|
44
|
+
</p>
|
|
45
|
+
</a>
|
|
46
|
+
|
|
47
|
+
<a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
|
|
48
|
+
<p align="center">Watch code demo on YouTube here</p>
|
|
49
|
+
</a>
|
|
35
50
|
|
|
36
51
|
## What is Remult?
|
|
37
52
|
|
|
@@ -39,6 +54,13 @@
|
|
|
39
54
|
as a single source of truth for your API, frontend type-safe API client and
|
|
40
55
|
backend ORM**.
|
|
41
56
|
|
|
57
|
+
* :zap: Zero-boilerplate CRUD API routes with paging, sorting, and filtering for Express / Fastify / Next.js / NestJS / Koa / others...
|
|
58
|
+
* :ok_hand: Fullstack type-safety for API queries, mutations and RPC, without code generation
|
|
59
|
+
* :sparkles: Input validation, defined once, runs both on the backend and on the frontend for best UX
|
|
60
|
+
* :lock: Fine-grained code-based API authorization
|
|
61
|
+
* :relieved: Incrementally adoptable
|
|
62
|
+
* :rocket: Production ready
|
|
63
|
+
|
|
42
64
|
### Status
|
|
43
65
|
|
|
44
66
|
Remult is **production-ready** and, in fact, used in production apps since 2018.
|
package/esm/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { InMemoryDataProvider } from './src/data-providers/in-memory-database';
|
|
|
10
10
|
export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-provider';
|
|
11
11
|
export { WebSqlDataProvider } from './src/data-providers/web-sql-data-provider';
|
|
12
12
|
export { SqlDatabase, } from './src/data-providers/sql-database';
|
|
13
|
-
export { CustomSqlFilterObject, CustomSqlFilterBuilder
|
|
13
|
+
export { CustomSqlFilterObject, CustomSqlFilterBuilder } from './src/filter/filter-consumer-bridge-to-sql-request';
|
|
14
14
|
export { JsonDataProvider, JsonEntityStorage } from './src/data-providers/json-data-provider';
|
|
15
15
|
export { Controller, BackendMethodOptions, BackendMethod, ProgressListener } from './src/server-action';
|
|
16
16
|
export { Allowed, Allow, Remult, RemultContext, ApiClient, isBackend, AllowedForInstance, EventDispatcher, EventSource, ExternalHttpProvider, Unobserve, UserInfo } from './src/context';
|
|
@@ -18,6 +18,7 @@ export { IdEntity } from './src/id-entity';
|
|
|
18
18
|
export { SortSegment, Sort } from './src/sort';
|
|
19
19
|
export { OneToMany, CompoundIdField } from './src/column';
|
|
20
20
|
export { Filter } from './src/filter/filter-interfaces';
|
|
21
|
+
export { FilterConsumerBridgeToSqlRequest } from './src/filter/filter-consumer-bridge-to-sql-request';
|
|
21
22
|
export { UrlBuilder } from './urlBuilder';
|
|
22
23
|
export { Validators } from './src/validators';
|
|
23
24
|
export { ValueConverters } from './src/valueConverters';
|
package/esm/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export { InMemoryDataProvider } from './src/data-providers/in-memory-database';
|
|
|
8
8
|
export { ArrayEntityDataProvider } from './src/data-providers/array-entity-data-provider'; //V
|
|
9
9
|
export { WebSqlDataProvider } from './src/data-providers/web-sql-data-provider'; //V
|
|
10
10
|
export { SqlDatabase, } from './src/data-providers/sql-database'; //V
|
|
11
|
-
export { CustomSqlFilterBuilder
|
|
11
|
+
export { CustomSqlFilterBuilder } from './src/filter/filter-consumer-bridge-to-sql-request';
|
|
12
12
|
export { JsonDataProvider } from './src/data-providers/json-data-provider'; //V
|
|
13
13
|
//export * from './src/data-api'; //reconsider if to make internal
|
|
14
14
|
export { Controller, BackendMethod, ProgressListener } from './src/server-action';
|
|
@@ -17,6 +17,7 @@ export { IdEntity } from './src/id-entity';
|
|
|
17
17
|
export { Sort } from './src/sort';
|
|
18
18
|
export { OneToMany, CompoundIdField } from './src/column';
|
|
19
19
|
export { Filter } from './src/filter/filter-interfaces';
|
|
20
|
+
export { FilterConsumerBridgeToSqlRequest } from './src/filter/filter-consumer-bridge-to-sql-request';
|
|
20
21
|
export { UrlBuilder } from './urlBuilder';
|
|
21
22
|
export { Validators } from './src/validators';
|
|
22
23
|
export { ValueConverters } from './src/valueConverters';
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Remult } from '../src/context';
|
|
1
|
+
import { PoolConfig, QueryResult } from 'pg';
|
|
3
2
|
import { EntityMetadata } from '../src/remult3';
|
|
4
3
|
import { SqlCommand, SqlImplementation } from '../src/sql-command';
|
|
5
4
|
import { SqlDatabase } from '../src/data-providers/sql-database';
|
|
@@ -11,7 +10,6 @@ export interface PostgresClient extends PostgresCommandSource {
|
|
|
11
10
|
}
|
|
12
11
|
export declare class PostgresDataProvider implements SqlImplementation {
|
|
13
12
|
private pool;
|
|
14
|
-
static getDb(remult?: Remult): ClientBase;
|
|
15
13
|
entityIsUsedForTheFirstTime(entity: EntityMetadata): Promise<void>;
|
|
16
14
|
getLimitSqlSyntax(limit: number, offset: number): string;
|
|
17
15
|
createCommand(): SqlCommand;
|
|
@@ -6,14 +6,6 @@ export class PostgresDataProvider {
|
|
|
6
6
|
constructor(pool) {
|
|
7
7
|
this.pool = pool;
|
|
8
8
|
}
|
|
9
|
-
static getDb(remult) {
|
|
10
|
-
const sql = SqlDatabase.getDb(remult);
|
|
11
|
-
const me = sql._getSourceSql();
|
|
12
|
-
if (!me.pool) {
|
|
13
|
-
throw "the data provider is not a PostgresDataProvider";
|
|
14
|
-
}
|
|
15
|
-
return me.pool;
|
|
16
|
-
}
|
|
17
9
|
async entityIsUsedForTheFirstTime(entity) {
|
|
18
10
|
}
|
|
19
11
|
getLimitSqlSyntax(limit, offset) {
|
|
@@ -30,9 +22,7 @@ export class PostgresDataProvider {
|
|
|
30
22
|
createCommand: () => new PostgresBridgeToSQLCommand(client),
|
|
31
23
|
entityIsUsedForTheFirstTime: this.entityIsUsedForTheFirstTime,
|
|
32
24
|
transaction: () => { throw "nested transactions not allowed"; },
|
|
33
|
-
getLimitSqlSyntax: this.getLimitSqlSyntax
|
|
34
|
-
//@ts-ignore
|
|
35
|
-
pool: client
|
|
25
|
+
getLimitSqlSyntax: this.getLimitSqlSyntax
|
|
36
26
|
});
|
|
37
27
|
await client.query('COMMIT');
|
|
38
28
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { allEntities } from '../src/context';
|
|
2
|
-
import {
|
|
2
|
+
import { getDbNameProvider } from '../src/filter/filter-consumer-bridge-to-sql-request';
|
|
3
3
|
import { isAutoIncrement } from '../src/remult3';
|
|
4
4
|
import { ValueConverters } from '../src/valueConverters';
|
|
5
5
|
export function postgresColumnSyntax(x, dbName) {
|
|
@@ -46,57 +46,57 @@ export class PostgresSchemaBuilder {
|
|
|
46
46
|
console.log("start verify structure");
|
|
47
47
|
for (const entityClass of allEntities) {
|
|
48
48
|
let entity = remult.repo(entityClass).metadata;
|
|
49
|
-
let e = await
|
|
49
|
+
let e = await getDbNameProvider(entity);
|
|
50
50
|
try {
|
|
51
51
|
if (!entity.options.sqlExpression) {
|
|
52
|
-
if ((await e
|
|
52
|
+
if ((await e.entityName).toLowerCase().indexOf('from ') < 0) {
|
|
53
53
|
await this.createIfNotExist(entity);
|
|
54
54
|
await this.verifyAllColumns(entity);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (err) {
|
|
59
|
-
console.log("failed verify structure of " + e
|
|
59
|
+
console.log("failed verify structure of " + e.entityName + " ", err);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
async createIfNotExist(entity) {
|
|
64
64
|
var c = this.pool.createCommand();
|
|
65
|
-
let e = await
|
|
66
|
-
await c.execute("select 1 from information_Schema.tables where table_name=" + c.addParameterAndReturnSqlToken((e
|
|
65
|
+
let e = await getDbNameProvider(entity);
|
|
66
|
+
await c.execute("select 1 from information_Schema.tables where table_name=" + c.addParameterAndReturnSqlToken((e.entityName).toLowerCase()) + this.additionalWhere).then(async (r) => {
|
|
67
67
|
if (r.rows.length == 0) {
|
|
68
68
|
let result = '';
|
|
69
69
|
for (const x of entity.fields) {
|
|
70
|
-
if (!isDbReadonly(x
|
|
70
|
+
if (!e.isDbReadonly(x) || isAutoIncrement(x)) {
|
|
71
71
|
if (result.length != 0)
|
|
72
72
|
result += ',';
|
|
73
73
|
result += '\r\n ';
|
|
74
74
|
if (isAutoIncrement(x))
|
|
75
|
-
result += e.
|
|
75
|
+
result += e.nameOf(x) + ' serial';
|
|
76
76
|
else {
|
|
77
|
-
result += postgresColumnSyntax(x, e.
|
|
77
|
+
result += postgresColumnSyntax(x, e.nameOf(x));
|
|
78
78
|
if (x == entity.idMetadata.field)
|
|
79
79
|
result += ' primary key';
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
let sql = 'create table ' + e
|
|
83
|
+
let sql = 'create table ' + e.entityName + ' (' + result + '\r\n)';
|
|
84
84
|
//console.log(sql);
|
|
85
85
|
await this.pool.execute(sql);
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
async addColumnIfNotExist(entity, c) {
|
|
90
|
-
let e = await
|
|
91
|
-
if (isDbReadonly(c(entity)
|
|
90
|
+
let e = await getDbNameProvider(entity);
|
|
91
|
+
if (e.isDbReadonly(c(entity)))
|
|
92
92
|
return;
|
|
93
93
|
try {
|
|
94
94
|
let cmd = this.pool.createCommand();
|
|
95
|
-
const colName = e.
|
|
95
|
+
const colName = e.nameOf(c(entity));
|
|
96
96
|
if ((await cmd.execute(`select 1
|
|
97
97
|
FROM information_schema.columns
|
|
98
|
-
WHERE table_name=${cmd.addParameterAndReturnSqlToken((e
|
|
99
|
-
let sql = `alter table ${e
|
|
98
|
+
WHERE table_name=${cmd.addParameterAndReturnSqlToken((e.entityName).toLocaleLowerCase())} and column_name=${cmd.addParameterAndReturnSqlToken((colName).toLocaleLowerCase())}` + this.additionalWhere)).rows.length == 0) {
|
|
99
|
+
let sql = `alter table ${e.entityName} add column ${postgresColumnSyntax(c(entity), colName)}`;
|
|
100
100
|
//console.log(sql);
|
|
101
101
|
await this.pool.execute(sql);
|
|
102
102
|
}
|
|
@@ -108,14 +108,14 @@ export class PostgresSchemaBuilder {
|
|
|
108
108
|
async verifyAllColumns(entity) {
|
|
109
109
|
try {
|
|
110
110
|
let cmd = this.pool.createCommand();
|
|
111
|
-
let e = await
|
|
111
|
+
let e = await getDbNameProvider(entity);
|
|
112
112
|
let cols = (await cmd.execute(`select column_name
|
|
113
113
|
FROM information_schema.columns
|
|
114
|
-
WHERE table_name=${cmd.addParameterAndReturnSqlToken((e
|
|
114
|
+
WHERE table_name=${cmd.addParameterAndReturnSqlToken((e.entityName).toLocaleLowerCase())} ` + this.additionalWhere)).rows.map(x => x.column_name);
|
|
115
115
|
for (const col of entity.fields) {
|
|
116
|
-
if (!isDbReadonly(col
|
|
117
|
-
if (!cols.includes(e.
|
|
118
|
-
let sql = `alter table ${e
|
|
116
|
+
if (!e.isDbReadonly(col))
|
|
117
|
+
if (!cols.includes(e.nameOf(col).toLocaleLowerCase())) {
|
|
118
|
+
let sql = `alter table ${e.entityName} add column ${postgresColumnSyntax(col, e.nameOf(col))}`;
|
|
119
119
|
//console.log(sql);
|
|
120
120
|
await this.pool.execute(sql);
|
|
121
121
|
}
|
package/esm/remult-express.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as express from 'express';
|
|
2
2
|
import { createRemultServer } from './server/expressBridge';
|
|
3
|
+
import { initAsyncHooks } from './server/initAsyncHooks';
|
|
3
4
|
export function remultExpress(options) {
|
|
4
5
|
let app = express.Router();
|
|
5
6
|
if (!options) {
|
|
@@ -12,6 +13,7 @@ export function remultExpress(options) {
|
|
|
12
13
|
app.use(express.json({ limit: options.bodySizeLimit }));
|
|
13
14
|
app.use(express.urlencoded({ extended: true, limit: options.bodySizeLimit }));
|
|
14
15
|
}
|
|
16
|
+
initAsyncHooks();
|
|
15
17
|
const server = createRemultServer(options);
|
|
16
18
|
server.registerRouter(app);
|
|
17
19
|
return Object.assign(app, {
|
package/esm/remult-fastify.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRemultServer } from './server/expressBridge';
|
|
2
|
+
import { initAsyncHooks } from './server/initAsyncHooks';
|
|
2
3
|
export function remultFastify(options) {
|
|
3
4
|
function fastifyHandler(handler) {
|
|
4
5
|
const response = (req, res) => {
|
|
@@ -18,6 +19,7 @@ export function remultFastify(options) {
|
|
|
18
19
|
};
|
|
19
20
|
return response;
|
|
20
21
|
}
|
|
22
|
+
initAsyncHooks();
|
|
21
23
|
const api = createRemultServer(options);
|
|
22
24
|
const pluginFunction = async (instance, op) => {
|
|
23
25
|
//@ts-ignore
|
|
@@ -1,19 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Knex } from 'knex';
|
|
2
|
+
import { Filter, FilterConsumer } from "../src/filter/filter-interfaces";
|
|
3
|
+
import { dbNameProvider } from "../src/filter/filter-consumer-bridge-to-sql-request";
|
|
2
4
|
import { Remult } from "../src/context";
|
|
3
|
-
import { EntityFilter, EntityMetadata
|
|
5
|
+
import { EntityFilter, EntityMetadata } from "../src/remult3";
|
|
4
6
|
import { DataProvider, EntityDataProvider } from '../src/data-interfaces';
|
|
5
7
|
import { FieldMetadata } from '../src/column-interfaces';
|
|
6
8
|
export declare class KnexDataProvider implements DataProvider {
|
|
7
9
|
knex: Knex;
|
|
8
10
|
constructor(knex: Knex);
|
|
9
|
-
static getDb(remult?: Remult): Knex<any, Record<string, any>[]>;
|
|
10
11
|
getEntityDataProvider(entity: EntityMetadata<any>): EntityDataProvider;
|
|
11
12
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
12
13
|
static customFilter(build: CustomKnexFilterBuilderFunction): EntityFilter<any>;
|
|
13
14
|
supportsCustomFilter?: boolean;
|
|
14
15
|
}
|
|
15
16
|
export declare type CustomKnexFilterBuilderFunction = () => Promise<(builder: Knex.QueryBuilder) => void>;
|
|
16
|
-
export declare
|
|
17
|
+
export declare class FilterConsumerBridgeToKnexRequest implements FilterConsumer {
|
|
18
|
+
private nameProvider;
|
|
19
|
+
_addWhere: boolean;
|
|
20
|
+
promises: Promise<void>[];
|
|
21
|
+
result: ((builder: Knex.QueryBuilder) => void)[];
|
|
22
|
+
resolveWhere(): Promise<((builder: Knex.QueryBuilder<any, any>) => void)[]>;
|
|
23
|
+
constructor(nameProvider: dbNameProvider);
|
|
24
|
+
custom(key: string, customItem: any): void;
|
|
25
|
+
or(orElements: Filter[]): void;
|
|
26
|
+
isNull(col: FieldMetadata): void;
|
|
27
|
+
isNotNull(col: FieldMetadata): void;
|
|
28
|
+
isIn(col: FieldMetadata, val: any[]): void;
|
|
29
|
+
isEqualTo(col: FieldMetadata, val: any): void;
|
|
30
|
+
isDifferentFrom(col: FieldMetadata, val: any): void;
|
|
31
|
+
isGreaterOrEqualTo(col: FieldMetadata, val: any): void;
|
|
32
|
+
isGreaterThan(col: FieldMetadata, val: any): void;
|
|
33
|
+
isLessOrEqualTo(col: FieldMetadata, val: any): void;
|
|
34
|
+
isLessThan(col: FieldMetadata, val: any): void;
|
|
35
|
+
containsCaseInsensitive(col: FieldMetadata, val: any): void;
|
|
36
|
+
private add;
|
|
37
|
+
databaseCustom(databaseCustom: {
|
|
38
|
+
buildKnex: CustomKnexFilterBuilderFunction;
|
|
39
|
+
}): void;
|
|
40
|
+
}
|
|
17
41
|
export declare class KnexSchemaBuilder {
|
|
18
42
|
private knex;
|
|
19
43
|
verifyStructureOfAllEntities(remult: Remult): Promise<void>;
|
package/esm/remult-knex/index.js
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
+
import knex from 'knex';
|
|
1
2
|
import { customDatabaseFilterToken, Filter } from "../src/filter/filter-interfaces";
|
|
2
|
-
import {
|
|
3
|
+
import { getDbNameProvider } from "../src/filter/filter-consumer-bridge-to-sql-request";
|
|
3
4
|
import { allEntities, Remult } from "../src/context";
|
|
4
5
|
import { isAutoIncrement } from "../src/remult3";
|
|
5
6
|
import { ValueConverters } from "../src/valueConverters";
|
|
6
7
|
import { CompoundIdField } from '../src/column';
|
|
7
8
|
import { Sort } from '../src/sort';
|
|
8
|
-
import { remult as remultContext } from '../src/remult-proxy';
|
|
9
9
|
export class KnexDataProvider {
|
|
10
10
|
constructor(knex) {
|
|
11
11
|
this.knex = knex;
|
|
12
12
|
}
|
|
13
|
-
static getDb(remult) {
|
|
14
|
-
const r = (remult || remultContext).dataProvider;
|
|
15
|
-
if (!r.knex)
|
|
16
|
-
throw "the data provider is not an KnexDataProvider";
|
|
17
|
-
return r.knex;
|
|
18
|
-
}
|
|
19
13
|
getEntityDataProvider(entity) {
|
|
20
14
|
return new KnexEntityDataProvider(entity, this.knex);
|
|
21
15
|
}
|
|
@@ -48,7 +42,7 @@ class KnexEntityDataProvider {
|
|
|
48
42
|
const br = new FilterConsumerBridgeToKnexRequest(e);
|
|
49
43
|
where.__applyToConsumer(br);
|
|
50
44
|
let r = await br.resolveWhere();
|
|
51
|
-
const result = (await this.knex(e
|
|
45
|
+
const result = (await this.knex(e.entityName)
|
|
52
46
|
.count().where(b => r.forEach(w => w(b))));
|
|
53
47
|
var row = result[0];
|
|
54
48
|
for (const key in row) {
|
|
@@ -66,11 +60,11 @@ class KnexEntityDataProvider {
|
|
|
66
60
|
if (x.isServerExpression) {
|
|
67
61
|
}
|
|
68
62
|
else {
|
|
69
|
-
cols.push(e.
|
|
63
|
+
cols.push(e.nameOf(x));
|
|
70
64
|
colKeys.push(x);
|
|
71
65
|
}
|
|
72
66
|
}
|
|
73
|
-
let query = this.knex(e
|
|
67
|
+
let query = this.knex(e.entityName).select(cols);
|
|
74
68
|
if (options?.where) {
|
|
75
69
|
const br = new FilterConsumerBridgeToKnexRequest(e);
|
|
76
70
|
options.where.__applyToConsumer(br);
|
|
@@ -82,7 +76,7 @@ class KnexEntityDataProvider {
|
|
|
82
76
|
}
|
|
83
77
|
if (options.orderBy) {
|
|
84
78
|
query = query.orderBy(options.orderBy.Segments.map(s => ({
|
|
85
|
-
column: e.
|
|
79
|
+
column: e.nameOf(s.field),
|
|
86
80
|
order: s.isDescending ? "desc" : "asc"
|
|
87
81
|
})));
|
|
88
82
|
}
|
|
@@ -108,7 +102,7 @@ class KnexEntityDataProvider {
|
|
|
108
102
|
});
|
|
109
103
|
}
|
|
110
104
|
async init() {
|
|
111
|
-
return await
|
|
105
|
+
return await getDbNameProvider(this.entity);
|
|
112
106
|
}
|
|
113
107
|
async update(id, data) {
|
|
114
108
|
const e = await this.init();
|
|
@@ -119,17 +113,17 @@ class KnexEntityDataProvider {
|
|
|
119
113
|
resultFilter = this.entity.idMetadata.getIdFilter(data.id);
|
|
120
114
|
let updateObject = {};
|
|
121
115
|
for (const x of this.entity.fields) {
|
|
122
|
-
if (isDbReadonly(x
|
|
116
|
+
if (e.isDbReadonly(x)) { }
|
|
123
117
|
else if (data[x.key] !== undefined) {
|
|
124
118
|
let v = x.valueConverter.toDb(data[x.key]);
|
|
125
119
|
if (v !== undefined) {
|
|
126
|
-
let key = await e.
|
|
120
|
+
let key = await e.nameOf(x);
|
|
127
121
|
updateObject[key] = v;
|
|
128
122
|
}
|
|
129
123
|
}
|
|
130
124
|
}
|
|
131
125
|
let where = await f.resolveWhere();
|
|
132
|
-
await this.knex(e
|
|
126
|
+
await this.knex(e.entityName).update(updateObject).where(b => where.forEach(w => w(b)));
|
|
133
127
|
return this.find({ where: Filter.fromEntityFilter(this.entity, resultFilter) }).then(y => y[0]);
|
|
134
128
|
}
|
|
135
129
|
async delete(id) {
|
|
@@ -137,7 +131,7 @@ class KnexEntityDataProvider {
|
|
|
137
131
|
let f = new FilterConsumerBridgeToKnexRequest(e);
|
|
138
132
|
Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
|
|
139
133
|
let where = await f.resolveWhere();
|
|
140
|
-
await this.knex(e
|
|
134
|
+
await this.knex(e.entityName).delete().where(b => where.forEach(w => w(b)));
|
|
141
135
|
}
|
|
142
136
|
async insert(data) {
|
|
143
137
|
const e = await this.init();
|
|
@@ -148,16 +142,16 @@ class KnexEntityDataProvider {
|
|
|
148
142
|
resultFilter = Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(data[this.entity.idMetadata.field.key]));
|
|
149
143
|
let insertObject = {};
|
|
150
144
|
for (const x of this.entity.fields) {
|
|
151
|
-
if (isDbReadonly(x
|
|
145
|
+
if (e.isDbReadonly(x)) { }
|
|
152
146
|
else {
|
|
153
147
|
let v = x.valueConverter.toDb(data[x.key]);
|
|
154
148
|
if (v != undefined) {
|
|
155
|
-
let key = await e.
|
|
149
|
+
let key = await e.nameOf(x);
|
|
156
150
|
insertObject[key] = v;
|
|
157
151
|
}
|
|
158
152
|
}
|
|
159
153
|
}
|
|
160
|
-
let insert = this.knex(e
|
|
154
|
+
let insert = this.knex(e.entityName).insert(insertObject);
|
|
161
155
|
if (isAutoIncrement(this.entity.idMetadata.field)) {
|
|
162
156
|
let result = await insert.returning(this.entity.idMetadata.field.key);
|
|
163
157
|
let newId = result[0].id;
|
|
@@ -170,7 +164,7 @@ class KnexEntityDataProvider {
|
|
|
170
164
|
});
|
|
171
165
|
}
|
|
172
166
|
}
|
|
173
|
-
class FilterConsumerBridgeToKnexRequest {
|
|
167
|
+
export class FilterConsumerBridgeToKnexRequest {
|
|
174
168
|
constructor(nameProvider) {
|
|
175
169
|
this.nameProvider = nameProvider;
|
|
176
170
|
this._addWhere = true;
|
|
@@ -214,13 +208,13 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
214
208
|
})());
|
|
215
209
|
}
|
|
216
210
|
isNull(col) {
|
|
217
|
-
this.result.push(b => b.whereNull(this.nameProvider.
|
|
211
|
+
this.result.push(b => b.whereNull(this.nameProvider.nameOf(col)));
|
|
218
212
|
}
|
|
219
213
|
isNotNull(col) {
|
|
220
|
-
this.result.push(b => b.whereNotNull(this.nameProvider.
|
|
214
|
+
this.result.push(b => b.whereNotNull(this.nameProvider.nameOf(col)));
|
|
221
215
|
}
|
|
222
216
|
isIn(col, val) {
|
|
223
|
-
this.result.push(knex => knex.whereIn(this.nameProvider.
|
|
217
|
+
this.result.push(knex => knex.whereIn(this.nameProvider.nameOf(col), val.map(x => col.valueConverter.toDb(x))));
|
|
224
218
|
}
|
|
225
219
|
isEqualTo(col, val) {
|
|
226
220
|
this.add(col, val, "=");
|
|
@@ -241,12 +235,12 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
241
235
|
this.add(col, val, "<");
|
|
242
236
|
}
|
|
243
237
|
containsCaseInsensitive(col, val) {
|
|
244
|
-
this.result.push(b => b.whereRaw('lower (' + this.nameProvider.
|
|
238
|
+
this.result.push(b => b.whereRaw('lower (' + this.nameProvider.nameOf(col) + ") like lower ('%" + val.replace(/'/g, '\'\'') + "%')"));
|
|
245
239
|
this.promises.push((async () => {
|
|
246
240
|
})());
|
|
247
241
|
}
|
|
248
242
|
add(col, val, operator) {
|
|
249
|
-
this.result.push(b => b.where(this.nameProvider.
|
|
243
|
+
this.result.push(b => b.where(this.nameProvider.nameOf(col), operator, col.valueConverter.toDb(val)));
|
|
250
244
|
}
|
|
251
245
|
databaseCustom(databaseCustom) {
|
|
252
246
|
this.promises.push((async () => {
|
|
@@ -256,13 +250,6 @@ class FilterConsumerBridgeToKnexRequest {
|
|
|
256
250
|
})());
|
|
257
251
|
}
|
|
258
252
|
}
|
|
259
|
-
export async function knexCondition(repo, condition) {
|
|
260
|
-
var b = new FilterConsumerBridgeToKnexRequest(await dbNamesOf(repo.metadata));
|
|
261
|
-
b._addWhere = false;
|
|
262
|
-
await (await (repo.translateWhereToFilter(condition))).__applyToConsumer(b);
|
|
263
|
-
let r = await b.resolveWhere();
|
|
264
|
-
return knex => r.forEach(y => y(knex));
|
|
265
|
-
}
|
|
266
253
|
export class KnexSchemaBuilder {
|
|
267
254
|
constructor(knex) {
|
|
268
255
|
this.knex = knex;
|
|
@@ -286,16 +273,16 @@ export class KnexSchemaBuilder {
|
|
|
286
273
|
}
|
|
287
274
|
}
|
|
288
275
|
async createIfNotExist(entity) {
|
|
289
|
-
const e = await
|
|
290
|
-
if (!await this.knex.schema.hasTable(e
|
|
276
|
+
const e = await getDbNameProvider(entity);
|
|
277
|
+
if (!await this.knex.schema.hasTable(e.entityName)) {
|
|
291
278
|
let cols = new Map();
|
|
292
279
|
for (const f of entity.fields) {
|
|
293
280
|
cols.set(f, {
|
|
294
|
-
name: e.
|
|
295
|
-
readonly: isDbReadonly(f
|
|
281
|
+
name: e.nameOf(f),
|
|
282
|
+
readonly: e.isDbReadonly(f)
|
|
296
283
|
});
|
|
297
284
|
}
|
|
298
|
-
await logSql(this.knex.schema.createTable(e
|
|
285
|
+
await logSql(this.knex.schema.createTable(e.entityName, b => {
|
|
299
286
|
for (const x of entity.fields) {
|
|
300
287
|
if (!cols.get(x).readonly || isAutoIncrement(x)) {
|
|
301
288
|
if (isAutoIncrement(x))
|
|
@@ -311,22 +298,22 @@ export class KnexSchemaBuilder {
|
|
|
311
298
|
}
|
|
312
299
|
}
|
|
313
300
|
async addColumnIfNotExist(entity, c) {
|
|
314
|
-
let e = await
|
|
315
|
-
if (isDbReadonly(c(entity)
|
|
301
|
+
let e = await getDbNameProvider(entity);
|
|
302
|
+
if (e.isDbReadonly(c(entity)))
|
|
316
303
|
return;
|
|
317
304
|
let col = c(entity);
|
|
318
|
-
let colName = e.
|
|
319
|
-
if (!await this.knex.schema.hasColumn(e
|
|
320
|
-
await this.knex.schema.alterTable(e
|
|
305
|
+
let colName = e.nameOf(col);
|
|
306
|
+
if (!await this.knex.schema.hasColumn(e.entityName, colName)) {
|
|
307
|
+
await this.knex.schema.alterTable(e.entityName, b => {
|
|
321
308
|
buildColumn(col, colName, b);
|
|
322
309
|
});
|
|
323
310
|
}
|
|
324
311
|
}
|
|
325
312
|
async verifyAllColumns(entity) {
|
|
326
|
-
let e = await
|
|
313
|
+
let e = await getDbNameProvider(entity);
|
|
327
314
|
try {
|
|
328
315
|
for (const col of entity.fields) {
|
|
329
|
-
if (isDbReadonly(col
|
|
316
|
+
if (!e.isDbReadonly(col)) {
|
|
330
317
|
await this.addColumnIfNotExist(entity, () => col);
|
|
331
318
|
}
|
|
332
319
|
}
|
|
@@ -390,7 +377,7 @@ function logSql(who) {
|
|
|
390
377
|
return who;
|
|
391
378
|
}
|
|
392
379
|
export async function createKnexDataProvider(config, autoCreateTables = true) {
|
|
393
|
-
let k =
|
|
380
|
+
let k = knex(config);
|
|
394
381
|
let result = new KnexDataProvider(k);
|
|
395
382
|
if (autoCreateTables)
|
|
396
383
|
await new KnexSchemaBuilder(k).verifyStructureOfAllEntities(new Remult(result));
|
package/esm/remult-mongo.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { DataProvider, EntityDataProvider,
|
|
1
|
+
import { MongoClient, Db } from 'mongodb';
|
|
2
|
+
import { DataProvider, EntityDataProvider, EntityMetadata } from '.';
|
|
3
3
|
export declare class MongoDataProvider implements DataProvider {
|
|
4
4
|
private db;
|
|
5
5
|
private client;
|
|
6
6
|
constructor(db: Db, client: MongoClient);
|
|
7
|
-
static getDb(remult?: Remult): Db;
|
|
8
7
|
getEntityDataProvider(entity: EntityMetadata<any>): EntityDataProvider;
|
|
9
8
|
transaction(action: (dataProvider: DataProvider) => Promise<void>): Promise<void>;
|
|
10
9
|
}
|
|
11
|
-
export declare function mongoCondition<entityType>(repo: Repository<entityType>, condition: EntityFilter<entityType>): Promise<{
|
|
12
|
-
$and: any[];
|
|
13
|
-
} | {
|
|
14
|
-
$and?: undefined;
|
|
15
|
-
}>;
|
package/esm/remult-mongo.js
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import { CompoundIdField, Filter } from '.';
|
|
2
|
-
import {
|
|
3
|
-
import { remult as remultContext } from './src/remult-proxy';
|
|
2
|
+
import { getDbNameProvider } from './src/filter/filter-consumer-bridge-to-sql-request';
|
|
4
3
|
export class MongoDataProvider {
|
|
5
4
|
constructor(db, client) {
|
|
6
5
|
this.db = db;
|
|
7
6
|
this.client = client;
|
|
8
7
|
}
|
|
9
|
-
static getDb(remult) {
|
|
10
|
-
const r = (remult || remultContext).dataProvider;
|
|
11
|
-
if (!r.db)
|
|
12
|
-
throw "the data provider is not a MongoDataProvider";
|
|
13
|
-
return r.db;
|
|
14
|
-
}
|
|
15
8
|
getEntityDataProvider(entity) {
|
|
16
9
|
return new MongoEntityDataProvider(this.db, entity);
|
|
17
10
|
}
|
|
@@ -40,7 +33,7 @@ class MongoEntityDataProvider {
|
|
|
40
33
|
translateFromJson(row, nameProvider) {
|
|
41
34
|
let result = {};
|
|
42
35
|
for (const col of this.entity.fields) {
|
|
43
|
-
let val = row[nameProvider.
|
|
36
|
+
let val = row[nameProvider.nameOf(col)];
|
|
44
37
|
if (isNull(val))
|
|
45
38
|
val = null;
|
|
46
39
|
result[col.key] = col.valueConverter.fromDb(val);
|
|
@@ -53,7 +46,7 @@ class MongoEntityDataProvider {
|
|
|
53
46
|
let val = col.valueConverter.toDb(row[col.key]);
|
|
54
47
|
if (val === null)
|
|
55
48
|
val = NULL;
|
|
56
|
-
result[nameProvider.
|
|
49
|
+
result[nameProvider.nameOf(col)] = val;
|
|
57
50
|
}
|
|
58
51
|
return result;
|
|
59
52
|
}
|
|
@@ -80,7 +73,7 @@ class MongoEntityDataProvider {
|
|
|
80
73
|
if (options.orderBy) {
|
|
81
74
|
op.sort = {};
|
|
82
75
|
for (const s of options.orderBy.Segments) {
|
|
83
|
-
op.sort[e.
|
|
76
|
+
op.sort[e.nameOf(s.field)] = s.isDescending ? -1 : 1;
|
|
84
77
|
}
|
|
85
78
|
}
|
|
86
79
|
return await Promise.all(await collection.find(where, op).map(x => this.translateFromJson(x, e)).toArray());
|
|
@@ -123,8 +116,8 @@ class MongoEntityDataProvider {
|
|
|
123
116
|
return await this.translateFromJson(await collection.findOne({ _id: r.insertedId }), e);
|
|
124
117
|
}
|
|
125
118
|
async collection() {
|
|
126
|
-
const e = await
|
|
127
|
-
const collection = this.db.collection(e
|
|
119
|
+
const e = await getDbNameProvider(this.entity);
|
|
120
|
+
const collection = this.db.collection(e.entityName);
|
|
128
121
|
return { e, collection };
|
|
129
122
|
}
|
|
130
123
|
}
|
|
@@ -178,7 +171,7 @@ class FilterConsumerBridgeToMongo {
|
|
|
178
171
|
}
|
|
179
172
|
isIn(col, val) {
|
|
180
173
|
this.result.push(() => ({
|
|
181
|
-
[this.nameProvider.
|
|
174
|
+
[this.nameProvider.nameOf(col)]: {
|
|
182
175
|
$in: val.map(x => col.valueConverter.toDb(x))
|
|
183
176
|
}
|
|
184
177
|
}));
|
|
@@ -210,7 +203,7 @@ class FilterConsumerBridgeToMongo {
|
|
|
210
203
|
}
|
|
211
204
|
add(col, val, operator) {
|
|
212
205
|
this.result.push(() => ({
|
|
213
|
-
[this.nameProvider.
|
|
206
|
+
[this.nameProvider.nameOf(col)]: { [operator]: isNull(val) ? val : col.valueConverter.toDb(val) }
|
|
214
207
|
}));
|
|
215
208
|
}
|
|
216
209
|
databaseCustom(databaseCustom) {
|
|
@@ -226,10 +219,3 @@ class FilterConsumerBridgeToMongo {
|
|
|
226
219
|
// })());
|
|
227
220
|
}
|
|
228
221
|
}
|
|
229
|
-
export async function mongoCondition(repo, condition) {
|
|
230
|
-
var b = new FilterConsumerBridgeToMongo(await dbNamesOf(repo.metadata));
|
|
231
|
-
b._addWhere = false;
|
|
232
|
-
await (await (repo.translateWhereToFilter(condition))).__applyToConsumer(b);
|
|
233
|
-
let r = await b.resolveWhere();
|
|
234
|
-
return r;
|
|
235
|
-
}
|
|
@@ -57,6 +57,13 @@ export interface GenericResponse {
|
|
|
57
57
|
status(statusCode: number): GenericResponse;
|
|
58
58
|
end(): any;
|
|
59
59
|
}
|
|
60
|
+
export declare class RemultAsyncLocalStorage {
|
|
61
|
+
private readonly remultObjectStorage;
|
|
62
|
+
constructor(remultObjectStorage: import('async_hooks').AsyncLocalStorage<Remult>);
|
|
63
|
+
run(remult: Remult, callback: VoidFunction): void;
|
|
64
|
+
getRemult(): Remult;
|
|
65
|
+
static instance: RemultAsyncLocalStorage;
|
|
66
|
+
}
|
|
60
67
|
export interface queuedJobInfo {
|
|
61
68
|
info: queuedJobInfoResponse;
|
|
62
69
|
userId: string;
|