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/dataConnectors/sqlite.ts
CHANGED
|
@@ -47,6 +47,9 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
|
|
|
47
47
|
} else if (baseType == 'boolean') {
|
|
48
48
|
field.type = AdminForthDataTypes.BOOLEAN;
|
|
49
49
|
field._underlineType = 'boolean';
|
|
50
|
+
} else if (baseType == 'datetime') {
|
|
51
|
+
field.type = AdminForthDataTypes.DATETIME;
|
|
52
|
+
field._underlineType = 'datetime';
|
|
50
53
|
} else {
|
|
51
54
|
field.type = 'unknown'
|
|
52
55
|
}
|
|
@@ -68,6 +71,8 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
|
|
|
68
71
|
return dayjs.unix(+value).toISOString();
|
|
69
72
|
} else if (field._underlineType == 'varchar') {
|
|
70
73
|
return dayjs(value).toISOString();
|
|
74
|
+
} else if (field._underlineType == 'datetime') {
|
|
75
|
+
return dayjs(value).toISOString();
|
|
71
76
|
} else {
|
|
72
77
|
throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps). Issue in field "${field.name}"`);
|
|
73
78
|
}
|
|
@@ -102,6 +107,8 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
|
|
|
102
107
|
} else if (field._underlineType == 'varchar') {
|
|
103
108
|
// value is iso string now, convert to unix timestamp
|
|
104
109
|
return dayjs(value).toISOString();
|
|
110
|
+
} else {
|
|
111
|
+
return value;
|
|
105
112
|
}
|
|
106
113
|
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
107
114
|
return value ? 1 : 0;
|
|
@@ -135,12 +142,8 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
|
|
|
135
142
|
[AdminForthSortDirections.desc]: 'DESC',
|
|
136
143
|
};
|
|
137
144
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
141
|
-
const tableName = resource.table;
|
|
142
|
-
|
|
143
|
-
const where = filters.length ? `WHERE ${filters.map((f, i) => {
|
|
145
|
+
whereClause(filters) {
|
|
146
|
+
return filters.length ? `WHERE ${filters.map((f, i) => {
|
|
144
147
|
let placeholder = '?';
|
|
145
148
|
let field = f.field;
|
|
146
149
|
let operator = this.OperatorsMap[f.operator];
|
|
@@ -154,25 +157,31 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
|
|
|
154
157
|
|
|
155
158
|
return `${field} ${operator} ${placeholder}`
|
|
156
159
|
}).join(' AND ')}` : '';
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
filters.length ? filters.forEach((f) => {
|
|
161
|
-
// for arrays do set in map
|
|
162
|
-
const v = f.value;
|
|
163
|
-
|
|
160
|
+
}
|
|
161
|
+
whereParams(filters) {
|
|
162
|
+
return filters.reduce((acc, f) => {
|
|
164
163
|
if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
|
|
165
|
-
|
|
164
|
+
acc.push(`%${f.value}%`);
|
|
166
165
|
} else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
167
|
-
|
|
166
|
+
acc.push(...f.value);
|
|
168
167
|
} else {
|
|
169
|
-
|
|
168
|
+
acc.push(f.value);
|
|
170
169
|
}
|
|
171
|
-
|
|
170
|
+
return acc;
|
|
171
|
+
}, []);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }): Promise<any[]> {
|
|
175
|
+
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
176
|
+
const tableName = resource.table;
|
|
177
|
+
|
|
178
|
+
const where = this.whereClause(filters);
|
|
179
|
+
|
|
180
|
+
const filterValues = this.whereParams(filters);
|
|
172
181
|
|
|
173
182
|
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
|
|
174
183
|
|
|
175
|
-
|
|
184
|
+
console.log('🪲 SQLITE Query', `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT ? OFFSET ?`, 'params:', [...filterValues, limit, offset]);
|
|
176
185
|
const q = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT ? OFFSET ?`;
|
|
177
186
|
const stmt = this.db.prepare(q);
|
|
178
187
|
const d = [...filterValues, limit, offset];
|
|
@@ -182,22 +191,21 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
|
|
|
182
191
|
}
|
|
183
192
|
const rows = await stmt.all(d);
|
|
184
193
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
194
|
+
return rows.map((row) => {
|
|
195
|
+
const newRow = {};
|
|
196
|
+
for (const [key, value] of Object.entries(row)) {
|
|
197
|
+
newRow[key] = value;
|
|
198
|
+
}
|
|
199
|
+
return newRow;
|
|
200
|
+
})
|
|
201
|
+
}
|
|
190
202
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return newRow;
|
|
198
|
-
}),
|
|
199
|
-
total,
|
|
200
|
-
};
|
|
203
|
+
async getCount({ resource, filters }) {
|
|
204
|
+
const tableName = resource.table;
|
|
205
|
+
const where = this.whereClause(filters);
|
|
206
|
+
const filterValues = this.whereParams(filters);
|
|
207
|
+
const totalStmt = this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`);
|
|
208
|
+
return totalStmt.get([...filterValues])['COUNT(*)'];
|
|
201
209
|
}
|
|
202
210
|
|
|
203
211
|
async getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }> {
|
|
@@ -232,9 +240,10 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
|
|
|
232
240
|
await q.run(values);
|
|
233
241
|
}
|
|
234
242
|
|
|
235
|
-
async deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }) {
|
|
243
|
+
async deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }): Promise<boolean> {
|
|
236
244
|
const q = this.db.prepare(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`);
|
|
237
|
-
await q.run(recordId);
|
|
245
|
+
const res = await q.run(recordId);
|
|
246
|
+
return res.changes > 0;
|
|
238
247
|
}
|
|
239
248
|
|
|
240
249
|
close() {
|
package/dist/auth.js
CHANGED
|
@@ -80,13 +80,13 @@ class AdminForthAuth {
|
|
|
80
80
|
console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
|
|
81
81
|
return null;
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
decoded.dbUser = dbUser;
|
|
83
|
+
const dbUser = yield this.adminforth.getUserByPk(pk);
|
|
84
|
+
if (!dbUser) {
|
|
85
|
+
console.error(`User with pk ${pk} not found in database`);
|
|
86
|
+
// will logout user which was deleted
|
|
87
|
+
return null;
|
|
89
88
|
}
|
|
89
|
+
decoded.dbUser = dbUser;
|
|
90
90
|
return decoded;
|
|
91
91
|
});
|
|
92
92
|
}
|
|
@@ -8,6 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { AdminForthFilterOperators } from "../types/AdminForthConfig.js";
|
|
11
|
+
import { suggestIfTypo } from "../modules/utils.js";
|
|
11
12
|
export default class AdminForthBaseConnector {
|
|
12
13
|
getPrimaryKey(resource) {
|
|
13
14
|
for (const col of resource.dataSourceColumns) {
|
|
@@ -24,12 +25,14 @@ export default class AdminForthBaseConnector {
|
|
|
24
25
|
offset: 0,
|
|
25
26
|
sort: [],
|
|
26
27
|
filters: [{ field: this.getPrimaryKey(resource), operator: AdminForthFilterOperators.EQ, value: id }],
|
|
27
|
-
getTotals: false
|
|
28
28
|
});
|
|
29
|
-
return data.
|
|
29
|
+
return data.length > 0 ? data[0] : null;
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
-
getDataWithOriginalTypes({ resource, limit, offset, sort, filters
|
|
32
|
+
getDataWithOriginalTypes({ resource, limit, offset, sort, filters }) {
|
|
33
|
+
throw new Error('Method not implemented.');
|
|
34
|
+
}
|
|
35
|
+
getCount({ resource, filters }) {
|
|
33
36
|
throw new Error('Method not implemented.');
|
|
34
37
|
}
|
|
35
38
|
discoverFields(resource) {
|
|
@@ -47,14 +50,57 @@ export default class AdminForthBaseConnector {
|
|
|
47
50
|
createRecordOriginalValues({ resource, record }) {
|
|
48
51
|
throw new Error('Method not implemented.');
|
|
49
52
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
checkUnique(resource, column, value) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 checkUnique|||', column, value);
|
|
56
|
+
const existingRecord = yield this.getData({
|
|
57
|
+
resource,
|
|
58
|
+
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value }],
|
|
59
|
+
limit: 1,
|
|
60
|
+
sort: [],
|
|
61
|
+
offset: 0,
|
|
62
|
+
getTotals: false
|
|
63
|
+
});
|
|
64
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 existingRecord|||', existingRecord);
|
|
65
|
+
return existingRecord.data.length > 0;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
createRecord(_a) {
|
|
69
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, record, adminUser }) {
|
|
70
|
+
// transform value using setFieldValue and call createRecordOriginalValues
|
|
71
|
+
const filledRecord = Object.assign({}, record);
|
|
72
|
+
const recordWithOriginalValues = Object.assign({}, record);
|
|
73
|
+
for (const col of resource.dataSourceColumns) {
|
|
74
|
+
if (col.fillOnCreate) {
|
|
75
|
+
if (filledRecord[col.name] === undefined) {
|
|
76
|
+
filledRecord[col.name] = col.fillOnCreate({
|
|
77
|
+
initialRecord: record,
|
|
78
|
+
adminUser
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
recordWithOriginalValues[col.name] = this.setFieldValue(col, filledRecord[col.name]);
|
|
83
|
+
}
|
|
84
|
+
let error = null;
|
|
85
|
+
yield Promise.all(resource.dataSourceColumns.map((col) => __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
if (col.isUnique && !col.virtual && !error) {
|
|
87
|
+
const exists = yield this.checkUnique(resource, col, recordWithOriginalValues[col.name]);
|
|
88
|
+
if (exists) {
|
|
89
|
+
error = `Record with ${col.name} ${recordWithOriginalValues[col.name]} already exists`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
})));
|
|
93
|
+
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 error', error);
|
|
94
|
+
if (error) {
|
|
95
|
+
return { error, ok: false };
|
|
96
|
+
}
|
|
97
|
+
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record', recordWithOriginalValues);
|
|
98
|
+
yield this.createRecordOriginalValues({ resource, record: recordWithOriginalValues });
|
|
99
|
+
return {
|
|
100
|
+
ok: true,
|
|
101
|
+
createdRecord: recordWithOriginalValues,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
58
104
|
}
|
|
59
105
|
updateRecord({ resource, recordId, newValues }) {
|
|
60
106
|
throw new Error('Method not implemented.');
|
|
@@ -63,25 +109,37 @@ export default class AdminForthBaseConnector {
|
|
|
63
109
|
throw new Error('Method not implemented.');
|
|
64
110
|
}
|
|
65
111
|
getData(_a) {
|
|
66
|
-
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
|
|
112
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters, getTotals }) {
|
|
67
113
|
if (filters) {
|
|
68
114
|
filters.map((f) => {
|
|
115
|
+
const fieldObj = resource.dataSourceColumns.find((col) => col.name == f.field);
|
|
116
|
+
if (!fieldObj) {
|
|
117
|
+
const similar = suggestIfTypo(resource.dataSourceColumns.map((col) => col.name), f.field);
|
|
118
|
+
throw new Error(`Field '${f.field}' not found in resource '${resource.resourceId}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
|
|
119
|
+
}
|
|
69
120
|
if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
70
|
-
f.value = f.value.map((val) => this.setFieldValue(
|
|
121
|
+
f.value = f.value.map((val) => this.setFieldValue(fieldObj, val));
|
|
71
122
|
}
|
|
72
123
|
else {
|
|
73
|
-
f.value = this.setFieldValue(
|
|
124
|
+
f.value = this.setFieldValue(fieldObj, f.value);
|
|
74
125
|
}
|
|
75
126
|
});
|
|
76
127
|
}
|
|
77
|
-
const
|
|
128
|
+
const promises = [this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters })];
|
|
129
|
+
if (getTotals) {
|
|
130
|
+
promises.push(this.getCount({ resource, filters }));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
promises.push(Promise.resolve(undefined));
|
|
134
|
+
}
|
|
135
|
+
const [data, total] = yield Promise.all(promises);
|
|
78
136
|
// call getFieldValue for each field
|
|
79
|
-
|
|
137
|
+
data.map((record) => {
|
|
80
138
|
for (const col of resource.dataSourceColumns) {
|
|
81
139
|
record[col.name] = this.getFieldValue(col, record[col.name]);
|
|
82
140
|
}
|
|
83
141
|
});
|
|
84
|
-
return
|
|
142
|
+
return { data, total };
|
|
85
143
|
});
|
|
86
144
|
}
|
|
87
145
|
getMinMaxForColumns(_a) {
|
|
@@ -184,36 +184,44 @@ class ClickhouseConnector extends AdminForthBaseConnector {
|
|
|
184
184
|
}
|
|
185
185
|
return value;
|
|
186
186
|
}
|
|
187
|
+
whereClause(resource, filters) {
|
|
188
|
+
return filters.length ? `WHERE ${filters.map((f, i) => {
|
|
189
|
+
const column = resource.dataSourceColumns.find((col) => col.name == f.field);
|
|
190
|
+
let placeholder = `{f${i}:${column._underlineType}}`;
|
|
191
|
+
let field = f.field;
|
|
192
|
+
let operator = this.OperatorsMap[f.operator];
|
|
193
|
+
if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
194
|
+
placeholder = `(${f.value.map((_, j) => `{p${i}_${j}:${column._underlineType}}`).join(', ')})`;
|
|
195
|
+
}
|
|
196
|
+
return `${field} ${operator} ${placeholder}`;
|
|
197
|
+
}).join(' AND ')}` : '';
|
|
198
|
+
}
|
|
199
|
+
whereParams(filters) {
|
|
200
|
+
const params = {};
|
|
201
|
+
filters.length ? filters.forEach((f, i) => {
|
|
202
|
+
// for arrays do set in map
|
|
203
|
+
const v = f.value;
|
|
204
|
+
if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
|
|
205
|
+
params[`f${i}`] = `%${v}%`;
|
|
206
|
+
}
|
|
207
|
+
else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
208
|
+
v.forEach((_, j) => {
|
|
209
|
+
params[`p${i}_${j}`] = v[j];
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
params[`f${i}`] = v;
|
|
214
|
+
}
|
|
215
|
+
}) : [];
|
|
216
|
+
return params;
|
|
217
|
+
}
|
|
187
218
|
getDataWithOriginalTypes(_a) {
|
|
188
|
-
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters
|
|
219
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
|
|
220
|
+
console.log('getDataWithOriginalTypes', resource, limit, offset, sort, filters);
|
|
189
221
|
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
190
222
|
const tableName = resource.table;
|
|
191
|
-
const where =
|
|
192
|
-
|
|
193
|
-
let placeholder = `{f${i}:${column._underlineType}}`;
|
|
194
|
-
let field = f.field;
|
|
195
|
-
let operator = this.OperatorsMap[f.operator];
|
|
196
|
-
if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
197
|
-
placeholder = `(${f.value.map((_, j) => `{p${i}_${j}:${column._underlineType}}`).join(', ')})`;
|
|
198
|
-
}
|
|
199
|
-
return `${field} ${operator} ${placeholder}`;
|
|
200
|
-
}).join(' AND ')}` : '';
|
|
201
|
-
const params = {};
|
|
202
|
-
filters.length ? filters.forEach((f, i) => {
|
|
203
|
-
// for arrays do set in map
|
|
204
|
-
const v = f.value;
|
|
205
|
-
if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
|
|
206
|
-
params[`f${i}`] = `%${v}%`;
|
|
207
|
-
}
|
|
208
|
-
else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
209
|
-
v.forEach((_, j) => {
|
|
210
|
-
params[`p${i}_${j}`] = v[j];
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
params[`f${i}`] = v;
|
|
215
|
-
}
|
|
216
|
-
}) : [];
|
|
223
|
+
const where = this.whereClause(resource, filters);
|
|
224
|
+
const params = this.whereParams(filters);
|
|
217
225
|
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
|
|
218
226
|
const q = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT {limit:Int} OFFSET {offset:Int}`;
|
|
219
227
|
const d = Object.assign(Object.assign({}, params), { limit,
|
|
@@ -224,26 +232,27 @@ class ClickhouseConnector extends AdminForthBaseConnector {
|
|
|
224
232
|
query_params: d,
|
|
225
233
|
});
|
|
226
234
|
const rows = yield stmt.json();
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
235
|
+
return rows.map((row) => {
|
|
236
|
+
const newRow = {};
|
|
237
|
+
for (const [key, value] of Object.entries(row)) {
|
|
238
|
+
newRow[key] = value;
|
|
239
|
+
}
|
|
240
|
+
return newRow;
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
getCount(_a) {
|
|
245
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, filters, }) {
|
|
246
|
+
const tableName = resource.table;
|
|
247
|
+
const where = this.whereClause(resource, filters);
|
|
248
|
+
const d = this.whereParams(filters);
|
|
249
|
+
const countQ = yield this.client.query({
|
|
250
|
+
query: `SELECT COUNT(*) FROM ${tableName} ${where}`,
|
|
251
|
+
format: 'JSONEachRow',
|
|
252
|
+
query_params: d,
|
|
253
|
+
});
|
|
254
|
+
const countResp = yield countQ.json();
|
|
255
|
+
return countResp[0]['COUNT()'];
|
|
247
256
|
});
|
|
248
257
|
}
|
|
249
258
|
getMinMaxForColumnsWithOriginalTypes(_a) {
|
|
@@ -291,10 +300,12 @@ class ClickhouseConnector extends AdminForthBaseConnector {
|
|
|
291
300
|
deleteRecord(_a) {
|
|
292
301
|
return __awaiter(this, arguments, void 0, function* ({ resource, recordId }) {
|
|
293
302
|
const pkColumn = resource.dataSourceColumns.find((col) => col.primaryKey);
|
|
294
|
-
yield this.client.command({
|
|
303
|
+
const res = yield this.client.command({
|
|
295
304
|
query: `ALTER TABLE ${this.dbName}.${resource.table} DELETE WHERE ${pkColumn.name} = {recordId:${pkColumn._underlineType}}`,
|
|
296
305
|
query_params: { recordId },
|
|
297
306
|
});
|
|
307
|
+
// todo test what is in res
|
|
308
|
+
return res;
|
|
298
309
|
});
|
|
299
310
|
}
|
|
300
311
|
close() {
|
|
@@ -108,25 +108,40 @@ class MongoConnector extends AdminForthBaseConnector {
|
|
|
108
108
|
}
|
|
109
109
|
return value;
|
|
110
110
|
}
|
|
111
|
-
|
|
112
|
-
return __awaiter(this, arguments, void 0, function* ({
|
|
113
|
-
// const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
|
|
114
|
-
const tableName = resource.table;
|
|
115
|
-
const collection = this.db.db().collection(tableName);
|
|
111
|
+
genQuery(_a) {
|
|
112
|
+
return __awaiter(this, arguments, void 0, function* ({ filters }) {
|
|
116
113
|
const query = {};
|
|
117
114
|
for (const filter of filters) {
|
|
118
115
|
query[filter.field] = this.OperatorsMap[filter.operator](filter.value);
|
|
119
116
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
return query;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
getDataWithOriginalTypes(_a) {
|
|
121
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
|
|
122
|
+
// const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
|
|
123
|
+
const tableName = resource.table;
|
|
124
|
+
const collection = this.db.db().collection(tableName);
|
|
125
|
+
const query = yield this.genQuery({ filters });
|
|
126
|
+
const sortArray = sort.map((s) => {
|
|
127
|
+
return [s.field, this.SortDirectionsMap[s.direction]];
|
|
128
|
+
});
|
|
124
129
|
const result = yield collection.find(query)
|
|
125
|
-
.sort(
|
|
130
|
+
.sort(sortArray)
|
|
126
131
|
.skip(offset)
|
|
127
132
|
.limit(limit)
|
|
128
133
|
.toArray();
|
|
129
|
-
return
|
|
134
|
+
return result;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
getCount(_a) {
|
|
138
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, filters }) {
|
|
139
|
+
const collection = this.db.db().collection(resource.table);
|
|
140
|
+
const query = {};
|
|
141
|
+
for (const filter of filters) {
|
|
142
|
+
query[filter.field] = this.OperatorsMap[filter.operator](filter.value);
|
|
143
|
+
}
|
|
144
|
+
return yield collection.countDocuments(query);
|
|
130
145
|
});
|
|
131
146
|
}
|
|
132
147
|
getMinMaxForColumnsWithOriginalTypes(_a) {
|
|
@@ -166,7 +181,8 @@ class MongoConnector extends AdminForthBaseConnector {
|
|
|
166
181
|
return __awaiter(this, arguments, void 0, function* ({ resource, recordId }) {
|
|
167
182
|
const primaryKey = this.getPrimaryKey(resource);
|
|
168
183
|
const collection = this.db.db().collection(resource.table);
|
|
169
|
-
yield collection.deleteOne({ [primaryKey]: recordId });
|
|
184
|
+
const res = yield collection.deleteOne({ [primaryKey]: recordId });
|
|
185
|
+
return res.deletedCount > 0;
|
|
170
186
|
});
|
|
171
187
|
}
|
|
172
188
|
close() {
|
|
@@ -184,46 +184,54 @@ class PostgresConnector extends AdminForthBaseConnector {
|
|
|
184
184
|
}
|
|
185
185
|
return value;
|
|
186
186
|
}
|
|
187
|
+
whereClauseAndValues(resource, filters) {
|
|
188
|
+
let totalCounter = 1;
|
|
189
|
+
const where = filters.length ? `WHERE ${filters.map((f, i) => {
|
|
190
|
+
let placeholder = '$' + (totalCounter);
|
|
191
|
+
const fieldData = resource.dataSourceColumns.find((col) => col.name == f.field);
|
|
192
|
+
let field = f.field;
|
|
193
|
+
let operator = this.OperatorsMap[f.operator];
|
|
194
|
+
if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
195
|
+
placeholder = `(${f.value.map((_, i) => `$${totalCounter + i}`).join(', ')})`;
|
|
196
|
+
totalCounter += f.value.length;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
totalCounter += 1;
|
|
200
|
+
}
|
|
201
|
+
if (fieldData._underlineType == 'uuid') {
|
|
202
|
+
field = `cast("${field}" as text)`;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
field = `"${field}"`;
|
|
206
|
+
}
|
|
207
|
+
return `${field} ${operator} ${placeholder}`;
|
|
208
|
+
}).join(' AND ')}` : '';
|
|
209
|
+
const filterValues = [];
|
|
210
|
+
filters.length ? filters.forEach((f) => {
|
|
211
|
+
// for arrays do set in map
|
|
212
|
+
let v = f.value;
|
|
213
|
+
if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
|
|
214
|
+
filterValues.push(`%${v}%`);
|
|
215
|
+
}
|
|
216
|
+
else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
217
|
+
filterValues.push(...v);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
filterValues.push(v);
|
|
221
|
+
}
|
|
222
|
+
}) : [];
|
|
223
|
+
return {
|
|
224
|
+
sql: where,
|
|
225
|
+
paramsCount: totalCounter,
|
|
226
|
+
values: filterValues,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
187
229
|
getDataWithOriginalTypes(_a) {
|
|
188
|
-
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters
|
|
230
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
|
|
189
231
|
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
190
232
|
const tableName = resource.table;
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
let placeholder = '$' + (totalCounter);
|
|
194
|
-
const fieldData = resource.dataSourceColumns.find((col) => col.name == f.field);
|
|
195
|
-
let field = f.field;
|
|
196
|
-
let operator = this.OperatorsMap[f.operator];
|
|
197
|
-
if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
198
|
-
placeholder = `(${f.value.map((_, i) => `$${totalCounter + i}`).join(', ')})`;
|
|
199
|
-
totalCounter += f.value.length;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
totalCounter += 1;
|
|
203
|
-
}
|
|
204
|
-
if (fieldData._underlineType == 'uuid') {
|
|
205
|
-
field = `cast("${field}" as text)`;
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
field = `"${field}"`;
|
|
209
|
-
}
|
|
210
|
-
return `${field} ${operator} ${placeholder}`;
|
|
211
|
-
}).join(' AND ')}` : '';
|
|
212
|
-
const filterValues = [];
|
|
213
|
-
filters.length ? filters.forEach((f) => {
|
|
214
|
-
// for arrays do set in map
|
|
215
|
-
let v = f.value;
|
|
216
|
-
if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
|
|
217
|
-
filterValues.push(`%${v}%`);
|
|
218
|
-
}
|
|
219
|
-
else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
|
|
220
|
-
filterValues.push(...v);
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
filterValues.push(v);
|
|
224
|
-
}
|
|
225
|
-
}) : [];
|
|
226
|
-
const limitOffset = `LIMIT $${totalCounter} OFFSET $${totalCounter + 1}`;
|
|
233
|
+
const { sql: where, paramsCount, values: filterValues } = this.whereClauseAndValues(resource, filters);
|
|
234
|
+
const limitOffset = `LIMIT $${paramsCount} OFFSET $${paramsCount + 1}`;
|
|
227
235
|
const d = [...filterValues, limit, offset];
|
|
228
236
|
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `"${s.field}" ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
|
|
229
237
|
const selectQuery = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} ${limitOffset}`;
|
|
@@ -232,20 +240,21 @@ class PostgresConnector extends AdminForthBaseConnector {
|
|
|
232
240
|
}
|
|
233
241
|
const stmt = yield this.db.query(selectQuery, d);
|
|
234
242
|
const rows = stmt.rows;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
};
|
|
243
|
+
return rows.map((row) => {
|
|
244
|
+
const newRow = {};
|
|
245
|
+
for (const [key, value] of Object.entries(row)) {
|
|
246
|
+
newRow[key] = value;
|
|
247
|
+
}
|
|
248
|
+
return newRow;
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
getCount(_a) {
|
|
253
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, filters }) {
|
|
254
|
+
const tableName = resource.table;
|
|
255
|
+
const { sql: where, values: filterValues } = this.whereClauseAndValues(resource, filters);
|
|
256
|
+
const stmt = yield this.db.query(`SELECT COUNT(*) FROM ${tableName} ${where}`, filterValues);
|
|
257
|
+
return stmt.rows[0].count;
|
|
249
258
|
});
|
|
250
259
|
}
|
|
251
260
|
getMinMaxForColumnsWithOriginalTypes(_a) {
|
|
@@ -283,7 +292,8 @@ class PostgresConnector extends AdminForthBaseConnector {
|
|
|
283
292
|
}
|
|
284
293
|
deleteRecord(_a) {
|
|
285
294
|
return __awaiter(this, arguments, void 0, function* ({ resource, recordId }) {
|
|
286
|
-
yield this.db.query(`DELETE FROM ${resource.table} WHERE "${this.getPrimaryKey(resource)}" = $1`, [recordId]);
|
|
295
|
+
const res = yield this.db.query(`DELETE FROM ${resource.table} WHERE "${this.getPrimaryKey(resource)}" = $1`, [recordId]);
|
|
296
|
+
return res.rowCount > 0;
|
|
287
297
|
});
|
|
288
298
|
}
|
|
289
299
|
close() {
|