adminforth 1.2.98 → 1.2.100
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/auth.ts +6 -5
- package/dataConnectors/baseConnector.ts +91 -21
- package/dataConnectors/clickhouse.ts +58 -37
- package/dataConnectors/mongo.ts +44 -14
- package/dataConnectors/postgres.ts +53 -36
- package/dataConnectors/sqlite.ts +44 -35
- package/dist/auth.js +6 -6
- package/dist/dataConnectors/baseConnector.js +75 -17
- package/dist/dataConnectors/clickhouse.js +59 -48
- package/dist/dataConnectors/mongo.js +28 -12
- package/dist/dataConnectors/postgres.js +62 -52
- package/dist/dataConnectors/sqlite.js +62 -45
- package/dist/index.js +52 -33
- package/dist/modules/configValidator.js +25 -16
- package/dist/modules/operationalResource.js +88 -0
- package/dist/modules/restApi.js +106 -96
- package/dist/modules/utils.js +16 -0
- package/dist/types/AdminForthConfig.js +37 -0
- package/index.ts +76 -43
- package/modules/configValidator.ts +26 -21
- package/modules/operationalResource.ts +91 -0
- package/modules/restApi.ts +91 -80
- package/modules/utils.ts +20 -0
- package/package.json +3 -2
- package/spa/src/App.vue +12 -9
- package/spa/src/components/Filters.vue +1 -1
- package/spa/src/components/ResourceForm.vue +7 -4
- package/spa/src/components/ResourceListTable.vue +124 -112
- package/spa/src/components/SkeleteLoader.vue +4 -4
- package/spa/src/components/ValueRenderer.vue +1 -1
- package/spa/src/router/index.ts +0 -8
- package/spa/src/spa_types/core.ts +1 -0
- package/spa/src/stores/filters.ts +3 -2
- package/spa/src/views/ListView.vue +16 -25
- package/spa/src/views/LoginView.vue +26 -9
- package/types/AdminForthConfig.ts +140 -38
package/auth.ts
CHANGED
|
@@ -88,12 +88,13 @@ class AdminForthAuth {
|
|
|
88
88
|
console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
const dbUser = await this.adminforth.getUserByPk(pk);
|
|
92
|
+
if (!dbUser) {
|
|
93
|
+
console.error(`User with pk ${pk} not found in database`);
|
|
94
|
+
// will logout user which was deleted
|
|
95
|
+
return null;
|
|
96
96
|
}
|
|
97
|
+
decoded.dbUser = dbUser;
|
|
97
98
|
return decoded;
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { get } from "http";
|
|
2
|
+
import { AdminForthResource, IAdminForthDataSourceConnectorBase, AdminForthSortDirections, AdminForthFilterOperators, AdminForthResourceColumn, IAdminForthSort, IAdminForthFilter } from "../types/AdminForthConfig.js";
|
|
3
|
+
import { suggestIfTypo } from "../modules/utils.js";
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
export default class AdminForthBaseConnector implements IAdminForthDataSourceConnectorBase {
|
|
@@ -17,19 +19,21 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
|
|
|
17
19
|
offset: 0,
|
|
18
20
|
sort: [],
|
|
19
21
|
filters: [{ field: this.getPrimaryKey(resource), operator: AdminForthFilterOperators.EQ, value: id }],
|
|
20
|
-
getTotals: false
|
|
21
22
|
});
|
|
22
|
-
return data.
|
|
23
|
+
return data.length > 0 ? data[0] : null;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
getDataWithOriginalTypes({ resource, limit, offset, sort, filters
|
|
26
|
+
getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
|
|
26
27
|
resource: AdminForthResource,
|
|
27
28
|
limit: number,
|
|
28
29
|
offset: number,
|
|
29
|
-
sort:
|
|
30
|
-
filters:
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
sort: IAdminForthSort[],
|
|
31
|
+
filters: IAdminForthFilter[],
|
|
32
|
+
}): Promise<any[]> {
|
|
33
|
+
throw new Error('Method not implemented.');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getCount({ resource, filters }: { resource: AdminForthResource; filters: { field: string; operator: AdminForthFilterOperators; value: any; }[]; }): Promise<number> {
|
|
33
37
|
throw new Error('Method not implemented.');
|
|
34
38
|
}
|
|
35
39
|
|
|
@@ -53,51 +57,117 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
|
|
|
53
57
|
throw new Error('Method not implemented.');
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
async checkUnique(resource: AdminForthResource, column: AdminForthResourceColumn, value: any) {
|
|
61
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 checkUnique|||', column, value);
|
|
62
|
+
const existingRecord = await this.getData({
|
|
63
|
+
resource,
|
|
64
|
+
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value }],
|
|
65
|
+
limit: 1,
|
|
66
|
+
sort: [],
|
|
67
|
+
offset: 0,
|
|
68
|
+
getTotals: false
|
|
69
|
+
});
|
|
70
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 existingRecord|||', existingRecord);
|
|
71
|
+
|
|
72
|
+
return existingRecord.data.length > 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async createRecord({ resource, record, adminUser }: {
|
|
76
|
+
resource: AdminForthResource; record: any; adminUser: any;
|
|
77
|
+
}): Promise<{ error?: string; ok: boolean; createdRecord?: any; }> {
|
|
57
78
|
// transform value using setFieldValue and call createRecordOriginalValues
|
|
58
|
-
const
|
|
79
|
+
const filledRecord = {...record};
|
|
80
|
+
const recordWithOriginalValues = {...record};
|
|
81
|
+
|
|
59
82
|
for (const col of resource.dataSourceColumns) {
|
|
60
|
-
|
|
83
|
+
if (col.fillOnCreate) {
|
|
84
|
+
if (filledRecord[col.name] === undefined) {
|
|
85
|
+
filledRecord[col.name] = col.fillOnCreate({
|
|
86
|
+
initialRecord: record,
|
|
87
|
+
adminUser
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
recordWithOriginalValues[col.name] = this.setFieldValue(col, filledRecord[col.name]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
let error: string | null = null;
|
|
96
|
+
await Promise.all(
|
|
97
|
+
resource.dataSourceColumns.map(async (col) => {
|
|
98
|
+
|
|
99
|
+
if (col.isUnique && !col.virtual && !error) {
|
|
100
|
+
|
|
101
|
+
const exists = await this.checkUnique(resource, col, recordWithOriginalValues[col.name]);
|
|
102
|
+
if (exists) {
|
|
103
|
+
error = `Record with ${col.name} ${recordWithOriginalValues[col.name]} already exists`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 error', error);
|
|
109
|
+
if (error) {
|
|
110
|
+
return { error, ok: false };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record', recordWithOriginalValues);
|
|
114
|
+
await this.createRecordOriginalValues({ resource, record: recordWithOriginalValues });
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
createdRecord: recordWithOriginalValues,
|
|
61
119
|
}
|
|
62
|
-
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record', newRecord);
|
|
63
|
-
return this.createRecordOriginalValues({ resource, record: newRecord });
|
|
64
120
|
}
|
|
65
121
|
|
|
66
122
|
updateRecord({ resource, recordId, newValues }: { resource: AdminForthResource; recordId: string; newValues: any; }): Promise<void> {
|
|
67
123
|
throw new Error('Method not implemented.');
|
|
68
124
|
}
|
|
69
125
|
|
|
70
|
-
deleteRecord({ resource, recordId }: { resource: AdminForthResource; recordId: string; }): Promise<
|
|
126
|
+
deleteRecord({ resource, recordId }: { resource: AdminForthResource; recordId: string; }): Promise<boolean> {
|
|
71
127
|
throw new Error('Method not implemented.');
|
|
72
128
|
}
|
|
73
129
|
|
|
74
130
|
|
|
75
|
-
async getData({ resource, limit, offset, sort, filters }: {
|
|
131
|
+
async getData({ resource, limit, offset, sort, filters, getTotals }: {
|
|
76
132
|
resource: AdminForthResource,
|
|
77
133
|
limit: number,
|
|
78
134
|
offset: number,
|
|
79
135
|
sort: { field: string, direction: AdminForthSortDirections }[],
|
|
80
|
-
filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
|
|
136
|
+
filters: { field: string, operator: AdminForthFilterOperators, value: any }[],
|
|
137
|
+
getTotals: boolean,
|
|
81
138
|
}): Promise<{ data: any[], total: number }> {
|
|
82
139
|
if (filters) {
|
|
83
140
|
filters.map((f) => {
|
|
141
|
+
const fieldObj = resource.dataSourceColumns.find((col) => col.name == f.field);
|
|
142
|
+
if (!fieldObj) {
|
|
143
|
+
const similar = suggestIfTypo(resource.dataSourceColumns.map((col) => col.name), f.field);
|
|
144
|
+
throw new Error(`Field '${f.field}' not found in resource '${resource.resourceId}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
|
|
145
|
+
}
|
|
84
146
|
if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
85
|
-
f.value = f.value.map((val) => this.setFieldValue(
|
|
147
|
+
f.value = f.value.map((val) => this.setFieldValue(fieldObj, val));
|
|
86
148
|
} else {
|
|
87
|
-
f.value = this.setFieldValue(
|
|
149
|
+
f.value = this.setFieldValue(fieldObj, f.value);
|
|
88
150
|
}
|
|
89
151
|
});
|
|
90
152
|
}
|
|
91
153
|
|
|
92
|
-
const
|
|
154
|
+
const promises: Promise<any>[] = [this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters })];
|
|
155
|
+
if (getTotals) {
|
|
156
|
+
promises.push(this.getCount({ resource, filters }));
|
|
157
|
+
} else {
|
|
158
|
+
promises.push(Promise.resolve(undefined));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const [data, total] = await Promise.all(promises);
|
|
162
|
+
|
|
93
163
|
// call getFieldValue for each field
|
|
94
|
-
|
|
164
|
+
data.map((record) => {
|
|
95
165
|
for (const col of resource.dataSourceColumns) {
|
|
96
166
|
record[col.name] = this.getFieldValue(col, record[col.name]);
|
|
97
167
|
}
|
|
98
168
|
});
|
|
99
169
|
|
|
100
|
-
return
|
|
170
|
+
return { data, total };
|
|
101
171
|
}
|
|
102
172
|
|
|
103
173
|
async getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource; columns: AdminForthResourceColumn[]; }): Promise<{ [key: string]: { min: any; max: any; }; }> {
|
|
@@ -172,19 +172,11 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
|
|
|
172
172
|
[AdminForthSortDirections.desc]: 'DESC',
|
|
173
173
|
};
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
sort: { field: string, direction: AdminForthSortDirections }[],
|
|
181
|
-
filters: { field: string, operator: AdminForthFilterOperators, value: any }[],
|
|
182
|
-
getTotals?: boolean,
|
|
183
|
-
}): Promise<{ data: any[], total: number }> {
|
|
184
|
-
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
185
|
-
const tableName = resource.table;
|
|
186
|
-
|
|
187
|
-
const where = filters.length ? `WHERE ${filters.map((f, i) => {
|
|
175
|
+
whereClause(
|
|
176
|
+
resource: AdminForthResource,
|
|
177
|
+
filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
|
|
178
|
+
): string {
|
|
179
|
+
return filters.length ? `WHERE ${filters.map((f, i) => {
|
|
188
180
|
const column = resource.dataSourceColumns.find((col) => col.name == f.field);
|
|
189
181
|
let placeholder = `{f${i}:${column._underlineType}}`;
|
|
190
182
|
let field = f.field;
|
|
@@ -197,9 +189,12 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
|
|
|
197
189
|
|
|
198
190
|
return `${field} ${operator} ${placeholder}`
|
|
199
191
|
}).join(' AND ')}` : '';
|
|
192
|
+
}
|
|
200
193
|
|
|
194
|
+
whereParams(
|
|
195
|
+
filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
|
|
196
|
+
): any {
|
|
201
197
|
const params = {};
|
|
202
|
-
|
|
203
198
|
filters.length ? filters.forEach((f, i) => {
|
|
204
199
|
// for arrays do set in map
|
|
205
200
|
const v = f.value;
|
|
@@ -215,6 +210,24 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
|
|
|
215
210
|
}
|
|
216
211
|
}) : [];
|
|
217
212
|
|
|
213
|
+
return params;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
|
|
217
|
+
resource: AdminForthResource,
|
|
218
|
+
limit: number,
|
|
219
|
+
offset: number,
|
|
220
|
+
sort: { field: string, direction: AdminForthSortDirections }[],
|
|
221
|
+
filters: { field: string, operator: AdminForthFilterOperators, value: any }[],
|
|
222
|
+
}): Promise<any[]> {
|
|
223
|
+
console.log('getDataWithOriginalTypes', resource, limit, offset, sort, filters);
|
|
224
|
+
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
225
|
+
const tableName = resource.table;
|
|
226
|
+
|
|
227
|
+
const where = this.whereClause(resource, filters);
|
|
228
|
+
|
|
229
|
+
const params = this.whereParams(filters);
|
|
230
|
+
|
|
218
231
|
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
|
|
219
232
|
|
|
220
233
|
|
|
@@ -233,27 +246,33 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
|
|
|
233
246
|
|
|
234
247
|
const rows = await stmt.json();
|
|
235
248
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
total = countResp[0]['COUNT()'];
|
|
245
|
-
}
|
|
249
|
+
return rows.map((row) => {
|
|
250
|
+
const newRow = {};
|
|
251
|
+
for (const [key, value] of Object.entries(row)) {
|
|
252
|
+
newRow[key] = value;
|
|
253
|
+
}
|
|
254
|
+
return newRow;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
246
257
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
async getCount({
|
|
259
|
+
resource,
|
|
260
|
+
filters,
|
|
261
|
+
}: {
|
|
262
|
+
resource: AdminForthResource;
|
|
263
|
+
filters: { field: string, operator: AdminForthFilterOperators, value: any }[];
|
|
264
|
+
}): Promise<number> {
|
|
265
|
+
const tableName = resource.table;
|
|
266
|
+
const where = this.whereClause(resource, filters);
|
|
267
|
+
const d = this.whereParams(filters);
|
|
268
|
+
|
|
269
|
+
const countQ = await this.client.query({
|
|
270
|
+
query: `SELECT COUNT(*) FROM ${tableName} ${where}`,
|
|
271
|
+
format: 'JSONEachRow',
|
|
272
|
+
query_params: d,
|
|
273
|
+
});
|
|
274
|
+
const countResp = await countQ.json()
|
|
275
|
+
return countResp[0]['COUNT()'];
|
|
257
276
|
}
|
|
258
277
|
|
|
259
278
|
async getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }> {
|
|
@@ -298,16 +317,18 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
|
|
|
298
317
|
);
|
|
299
318
|
}
|
|
300
319
|
|
|
301
|
-
async deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }) {
|
|
320
|
+
async deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }): Promise<boolean> {
|
|
302
321
|
const pkColumn = resource.dataSourceColumns.find((col) => col.primaryKey);
|
|
303
|
-
await this.client.command(
|
|
322
|
+
const res = await this.client.command(
|
|
304
323
|
{
|
|
305
324
|
query: `ALTER TABLE ${this.dbName}.${resource.table} DELETE WHERE ${
|
|
306
325
|
pkColumn.name
|
|
307
326
|
} = {recordId:${pkColumn._underlineType}}`,
|
|
308
327
|
query_params: { recordId },
|
|
309
|
-
|
|
328
|
+
}
|
|
310
329
|
);
|
|
330
|
+
// todo test what is in res
|
|
331
|
+
return res;
|
|
311
332
|
}
|
|
312
333
|
|
|
313
334
|
close() {
|
package/dataConnectors/mongo.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import { MongoClient } from 'mongodb';
|
|
3
|
-
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections,
|
|
3
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections,
|
|
4
|
+
IAdminForthDataSourceConnector, AdminForthResource } from '../types/AdminForthConfig.js';
|
|
4
5
|
import AdminForthBaseConnector from './baseConnector.js';
|
|
5
6
|
|
|
6
7
|
class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataSourceConnector {
|
|
@@ -104,28 +105,56 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS
|
|
|
104
105
|
return value ? 1 : 0;
|
|
105
106
|
}
|
|
106
107
|
return value;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
async getDataWithOriginalTypes({ resource, limit, offset, sort, filters, getTotals }) {
|
|
110
|
-
// const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
|
|
111
|
-
const tableName = resource.table;
|
|
108
|
+
}
|
|
112
109
|
|
|
113
|
-
|
|
110
|
+
async genQuery({ filters }) {
|
|
114
111
|
const query = {};
|
|
115
112
|
for (const filter of filters) {
|
|
116
113
|
query[filter.field] = this.OperatorsMap[filter.operator](filter.value);
|
|
117
114
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
return query;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }:
|
|
119
|
+
{
|
|
120
|
+
resource: AdminForthResource,
|
|
121
|
+
limit: number,
|
|
122
|
+
offset: number,
|
|
123
|
+
sort: { field: string, direction: AdminForthSortDirections }[],
|
|
124
|
+
filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
|
|
121
125
|
}
|
|
126
|
+
): Promise<any[]> {
|
|
127
|
+
|
|
128
|
+
// const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
|
|
129
|
+
const tableName = resource.table;
|
|
130
|
+
|
|
131
|
+
const collection = this.db.db().collection(tableName);
|
|
132
|
+
const query = await this.genQuery({ filters });
|
|
133
|
+
|
|
134
|
+
const sortArray: any[] = sort.map((s) => {
|
|
135
|
+
return [s.field, this.SortDirectionsMap[s.direction]];
|
|
136
|
+
});
|
|
137
|
+
|
|
122
138
|
const result = await collection.find(query)
|
|
123
|
-
.sort(
|
|
139
|
+
.sort(sortArray)
|
|
124
140
|
.skip(offset)
|
|
125
141
|
.limit(limit)
|
|
126
142
|
.toArray();
|
|
127
143
|
|
|
128
|
-
return
|
|
144
|
+
return result
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async getCount({ resource, filters }: {
|
|
148
|
+
resource: AdminForthResource,
|
|
149
|
+
filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
|
|
150
|
+
}): Promise<number> {
|
|
151
|
+
|
|
152
|
+
const collection = this.db.db().collection(resource.table);
|
|
153
|
+
const query = {};
|
|
154
|
+
for (const filter of filters) {
|
|
155
|
+
query[filter.field] = this.OperatorsMap[filter.operator](filter.value);
|
|
156
|
+
}
|
|
157
|
+
return await collection.countDocuments(query);
|
|
129
158
|
}
|
|
130
159
|
|
|
131
160
|
async getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
|
|
@@ -158,10 +187,11 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS
|
|
|
158
187
|
await collection.updateOne({ [this.getPrimaryKey(resource)]: recordId }, { $set: newValues });
|
|
159
188
|
}
|
|
160
189
|
|
|
161
|
-
async deleteRecord({ resource, recordId }) {
|
|
190
|
+
async deleteRecord({ resource, recordId }): Promise<boolean> {
|
|
162
191
|
const primaryKey = this.getPrimaryKey(resource);
|
|
163
192
|
const collection = this.db.db().collection(resource.table);
|
|
164
|
-
await collection.deleteOne({ [primaryKey]: recordId });
|
|
193
|
+
const res = await collection.deleteOne({ [primaryKey]: recordId });
|
|
194
|
+
return res.deletedCount > 0;
|
|
165
195
|
}
|
|
166
196
|
|
|
167
197
|
async close() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import pkg from 'pg';
|
|
3
|
-
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, IAdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
|
|
3
|
+
import { AdminForthDataTypes, AdminForthResource, AdminForthFilterOperators, AdminForthSortDirections, IAdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
|
|
4
4
|
import AdminForthBaseConnector from './baseConnector.js';
|
|
5
5
|
const { Client } = pkg;
|
|
6
6
|
|
|
@@ -168,24 +168,26 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
|
|
|
168
168
|
|
|
169
169
|
|
|
170
170
|
setFieldValue(field, value) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
if (field._underlineType == 'timestamp' || field._underlineType == 'int') {
|
|
176
|
-
return dayjs(value);
|
|
177
|
-
} else if (field._underlineType == 'varchar') {
|
|
178
|
-
return dayjs(value).toISOString();
|
|
179
|
-
}
|
|
180
|
-
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
181
|
-
return value ? 1 : 0;
|
|
171
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
172
|
+
if (!value) {
|
|
173
|
+
return null;
|
|
182
174
|
}
|
|
183
|
-
|
|
175
|
+
if (field._underlineType == 'timestamp' || field._underlineType == 'int') {
|
|
176
|
+
return dayjs(value);
|
|
177
|
+
} else if (field._underlineType == 'varchar') {
|
|
178
|
+
return dayjs(value).toISOString();
|
|
179
|
+
}
|
|
180
|
+
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
181
|
+
return value ? 1 : 0;
|
|
184
182
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
whereClauseAndValues(resource: AdminForthResource, filters: { field: string, operator: AdminForthFilterOperators, value: any }[]) : {
|
|
187
|
+
sql: string,
|
|
188
|
+
paramsCount: number,
|
|
189
|
+
values: any[],
|
|
190
|
+
} {
|
|
189
191
|
|
|
190
192
|
let totalCounter = 1;
|
|
191
193
|
const where = filters.length ? `WHERE ${filters.map((f, i) => {
|
|
@@ -201,9 +203,9 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
|
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
if (fieldData._underlineType == 'uuid') {
|
|
204
|
-
|
|
206
|
+
field = `cast("${field}" as text)`
|
|
205
207
|
} else {
|
|
206
|
-
|
|
208
|
+
field = `"${field}"`
|
|
207
209
|
}
|
|
208
210
|
return `${field} ${operator} ${placeholder}`;
|
|
209
211
|
}).join(' AND ')}` : '';
|
|
@@ -221,8 +223,22 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
|
|
|
221
223
|
filterValues.push(v);
|
|
222
224
|
}
|
|
223
225
|
}) : [];
|
|
226
|
+
return {
|
|
227
|
+
sql: where,
|
|
228
|
+
paramsCount: totalCounter,
|
|
229
|
+
values: filterValues,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
}
|
|
224
233
|
|
|
225
|
-
|
|
234
|
+
|
|
235
|
+
async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }): Promise<any[]> {
|
|
236
|
+
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
237
|
+
const tableName = resource.table;
|
|
238
|
+
|
|
239
|
+
const { sql: where, paramsCount, values: filterValues } = this.whereClauseAndValues(resource, filters);
|
|
240
|
+
|
|
241
|
+
const limitOffset = `LIMIT $${paramsCount} OFFSET $${paramsCount + 1}`;
|
|
226
242
|
const d = [...filterValues, limit, offset];
|
|
227
243
|
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `"${s.field}" ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
|
|
228
244
|
const selectQuery = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} ${limitOffset}`;
|
|
@@ -231,20 +247,20 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
|
|
|
231
247
|
}
|
|
232
248
|
const stmt = await this.db.query(selectQuery, d);
|
|
233
249
|
const rows = stmt.rows;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
|
|
250
|
+
return rows.map((row) => {
|
|
251
|
+
const newRow = {};
|
|
252
|
+
for (const [key, value] of Object.entries(row)) {
|
|
253
|
+
newRow[key] = value;
|
|
254
|
+
}
|
|
255
|
+
return newRow;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async getCount({ resource, filters }: { resource: AdminForthResource; filters: { field: string, operator: AdminForthFilterOperators, value: any }[]; }): Promise<number> {
|
|
260
|
+
const tableName = resource.table;
|
|
261
|
+
const { sql: where, values: filterValues } = this.whereClauseAndValues(resource, filters);
|
|
262
|
+
const stmt = await this.db.query(`SELECT COUNT(*) FROM ${tableName} ${where}`, filterValues);
|
|
263
|
+
return stmt.rows[0].count;
|
|
248
264
|
}
|
|
249
265
|
|
|
250
266
|
async getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
|
|
@@ -277,8 +293,9 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
|
|
|
277
293
|
await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
|
|
278
294
|
}
|
|
279
295
|
|
|
280
|
-
async deleteRecord({ resource, recordId }) {
|
|
281
|
-
|
|
296
|
+
async deleteRecord({ resource, recordId }): Promise<boolean> {
|
|
297
|
+
const res = await this.db.query(`DELETE FROM ${resource.table} WHERE "${this.getPrimaryKey(resource)}" = $1`, [recordId]);
|
|
298
|
+
return res.rowCount > 0;
|
|
282
299
|
}
|
|
283
300
|
|
|
284
301
|
async close() {
|