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.
Files changed (36) hide show
  1. package/auth.ts +6 -5
  2. package/dataConnectors/baseConnector.ts +91 -21
  3. package/dataConnectors/clickhouse.ts +58 -37
  4. package/dataConnectors/mongo.ts +44 -14
  5. package/dataConnectors/postgres.ts +53 -36
  6. package/dataConnectors/sqlite.ts +44 -35
  7. package/dist/auth.js +6 -6
  8. package/dist/dataConnectors/baseConnector.js +75 -17
  9. package/dist/dataConnectors/clickhouse.js +59 -48
  10. package/dist/dataConnectors/mongo.js +28 -12
  11. package/dist/dataConnectors/postgres.js +62 -52
  12. package/dist/dataConnectors/sqlite.js +62 -45
  13. package/dist/index.js +52 -33
  14. package/dist/modules/configValidator.js +25 -16
  15. package/dist/modules/operationalResource.js +88 -0
  16. package/dist/modules/restApi.js +106 -96
  17. package/dist/modules/utils.js +16 -0
  18. package/dist/types/AdminForthConfig.js +37 -0
  19. package/index.ts +76 -43
  20. package/modules/configValidator.ts +26 -21
  21. package/modules/operationalResource.ts +91 -0
  22. package/modules/restApi.ts +91 -80
  23. package/modules/utils.ts +20 -0
  24. package/package.json +3 -2
  25. package/spa/src/App.vue +12 -9
  26. package/spa/src/components/Filters.vue +1 -1
  27. package/spa/src/components/ResourceForm.vue +7 -4
  28. package/spa/src/components/ResourceListTable.vue +124 -112
  29. package/spa/src/components/SkeleteLoader.vue +4 -4
  30. package/spa/src/components/ValueRenderer.vue +1 -1
  31. package/spa/src/router/index.ts +0 -8
  32. package/spa/src/spa_types/core.ts +1 -0
  33. package/spa/src/stores/filters.ts +3 -2
  34. package/spa/src/views/ListView.vue +16 -25
  35. package/spa/src/views/LoginView.vue +26 -9
  36. package/types/AdminForthConfig.ts +140 -38
@@ -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
- async getDataWithOriginalTypes({ resource, limit, offset, sort, filters, getTotals }): Promise<{ data: any[], total: number }> {
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
- const filterValues = [];
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
- filterValues.push(`%${v}%`);
164
+ acc.push(`%${f.value}%`);
166
165
  } else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
167
- filterValues.push(...v);
166
+ acc.push(...f.value);
168
167
  } else {
169
- filterValues.push(v);
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
- let total = 0;
186
- if (getTotals) {
187
- const totalStmt = this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`);
188
- total = totalStmt.get([...filterValues])['COUNT(*)'];
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
- return {
192
- data: rows.map((row) => {
193
- const newRow = {};
194
- for (const [key, value] of Object.entries(row)) {
195
- newRow[key] = value;
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
- if (pk === null) {
84
- decoded.isRoot = true;
85
- }
86
- else {
87
- const dbUser = yield this.adminforth.getUserByPk(pk);
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.data.length > 0 ? data.data[0] : null;
29
+ return data.length > 0 ? data[0] : null;
30
30
  });
31
31
  }
32
- getDataWithOriginalTypes({ resource, limit, offset, sort, filters, getTotals }) {
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
- createRecord({ resource, record }) {
51
- // transform value using setFieldValue and call createRecordOriginalValues
52
- const newRecord = Object.assign({}, record);
53
- for (const col of resource.dataSourceColumns) {
54
- newRecord[col.name] = this.setFieldValue(col, record[col.name]);
55
- }
56
- process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record', newRecord);
57
- return this.createRecordOriginalValues({ resource, record: newRecord });
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(resource.dataSourceColumns.find((col) => col.name == f.field), val));
121
+ f.value = f.value.map((val) => this.setFieldValue(fieldObj, val));
71
122
  }
72
123
  else {
73
- f.value = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
124
+ f.value = this.setFieldValue(fieldObj, f.value);
74
125
  }
75
126
  });
76
127
  }
77
- const d = yield this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters, getTotals: true });
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
- d.data.map((record) => {
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 d;
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, getTotals }) {
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 = filters.length ? `WHERE ${filters.map((f, i) => {
192
- const column = resource.dataSourceColumns.find((col) => col.name == f.field);
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
- let total = 0;
228
- if (getTotals) {
229
- const countQ = yield this.client.query({
230
- query: `SELECT COUNT(*) FROM ${tableName} ${where}`,
231
- format: 'JSONEachRow',
232
- query_params: d,
233
- });
234
- const countResp = yield countQ.json();
235
- total = countResp[0]['COUNT()'];
236
- }
237
- return {
238
- data: rows.map((row) => {
239
- const newRow = {};
240
- for (const [key, value] of Object.entries(row)) {
241
- newRow[key] = value;
242
- }
243
- return newRow;
244
- }),
245
- total,
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
- getDataWithOriginalTypes(_a) {
112
- return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters, getTotals }) {
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
- let total = 0;
121
- if (getTotals) {
122
- total = yield collection.countDocuments(query);
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(sort)
130
+ .sort(sortArray)
126
131
  .skip(offset)
127
132
  .limit(limit)
128
133
  .toArray();
129
- return { data: result, total };
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, getTotals }) {
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
- let totalCounter = 1;
192
- const where = filters.length ? `WHERE ${filters.map((f, i) => {
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
- let total = 0;
236
- if (getTotals) {
237
- total = (yield this.db.query(`SELECT COUNT(*) FROM ${tableName} ${where}`, filterValues)).rows[0].count;
238
- }
239
- return {
240
- data: rows.map((row) => {
241
- const newRow = {};
242
- for (const [key, value] of Object.entries(row)) {
243
- newRow[key] = value;
244
- }
245
- return newRow;
246
- }),
247
- total,
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() {