adminforth 1.1.73 → 1.1.74

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.
@@ -0,0 +1,107 @@
1
+ import { AdminForthResource, AdminForthDataSourceConnectorBase, AdminForthSortDirections, AdminForthFilterOperators, AdminForthResourceColumn } from "../types/AdminForthConfig.js";
2
+
3
+
4
+ export default class AdminForthBaseConnector implements AdminForthDataSourceConnectorBase {
5
+ getPrimaryKey(resource: AdminForthResource): string {
6
+ for (const col of resource.dataSourceColumns) {
7
+ if (col.primaryKey) {
8
+ return col.name;
9
+ }
10
+ }
11
+ }
12
+
13
+ getRecordByPrimaryKeyWithOriginalTypes(resource: AdminForthResource, id: string): Promise<any> {
14
+ throw new Error('Method not implemented.');
15
+ }
16
+
17
+ getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
18
+ resource: AdminForthResource,
19
+ limit: number,
20
+ offset: number,
21
+ sort: { field: string, direction: AdminForthSortDirections }[],
22
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
23
+ }): Promise<{ data: any[], total: number }> {
24
+ throw new Error('Method not implemented.');
25
+ }
26
+
27
+ discoverFields(resource: AdminForthResource): Promise<{ [key: string]: AdminForthResourceColumn; }> {
28
+ throw new Error('Method not implemented.');
29
+ }
30
+
31
+ getFieldValue(field: AdminForthResourceColumn, value: any) {
32
+ throw new Error('Method not implemented.');
33
+ }
34
+
35
+ setFieldValue(field: AdminForthResourceColumn, value: any) {
36
+ throw new Error('Method not implemented.');
37
+ }
38
+
39
+ getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource; columns: AdminForthResourceColumn[]; }): Promise<{ [key: string]: { min: any; max: any; }; }> {
40
+ throw new Error('Method not implemented.');
41
+ }
42
+
43
+ createRecord({ resource, record }: { resource: AdminForthResource; record: any; }): Promise<void> {
44
+ throw new Error('Method not implemented.');
45
+ }
46
+
47
+ updateRecord({ resource, recordId, newValues }: { resource: AdminForthResource; recordId: string; newValues: any; }): Promise<void> {
48
+ throw new Error('Method not implemented.');
49
+ }
50
+
51
+ deleteRecord({ resource, recordId }: { resource: AdminForthResource; recordId: string; }): Promise<void> {
52
+ throw new Error('Method not implemented.');
53
+ }
54
+
55
+
56
+ async getData({ resource, limit, offset, sort, filters }: {
57
+ resource: AdminForthResource,
58
+ limit: number,
59
+ offset: number,
60
+ sort: { field: string, direction: AdminForthSortDirections }[],
61
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
62
+ }): Promise<{ data: any[], total: number }> {
63
+ if (filters) {
64
+ filters.map((f) => {
65
+ if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
66
+ f.value = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
67
+ } else {
68
+ f.value = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
69
+ }
70
+ });
71
+ }
72
+
73
+ const d = await this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters });
74
+ // call getFieldValue for each field
75
+ d.data.map((record) => {
76
+ for (const col of resource.dataSourceColumns) {
77
+ record[col.name] = this.getFieldValue(col, record[col.name]);
78
+ }
79
+ });
80
+
81
+ return d;
82
+ }
83
+
84
+ async getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource; columns: AdminForthResourceColumn[]; }): Promise<{ [key: string]: { min: any; max: any; }; }> {
85
+ const mm = await this.getMinMaxForColumnsWithOriginalTypes({ resource, columns });
86
+ const result = {};
87
+ for (const col of columns) {
88
+ result[col.name] = {
89
+ min: this.getFieldValue(col, mm[col.name].min),
90
+ max: this.getFieldValue(col, mm[col.name].max),
91
+ };
92
+ }
93
+ return result;
94
+ }
95
+
96
+ getRecordByPrimaryKey(resource: AdminForthResource, recordId: string): Promise<any> {
97
+ return this.getRecordByPrimaryKeyWithOriginalTypes(resource, recordId).then((record) => {
98
+ const newRecord = {};
99
+ for (const col of resource.dataSourceColumns) {
100
+ newRecord[col.name] = this.getFieldValue(col, record[col.name]);
101
+ }
102
+ return newRecord;
103
+ });
104
+ }
105
+
106
+
107
+ }
@@ -83,7 +83,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
83
83
  return value;
84
84
  }
85
85
 
86
- async getRecordByPrimaryKey(resource, key) {
86
+ async getRecordByPrimaryKeyWithOriginalTypes(resource, key) {
87
87
  const tableName = resource.table;
88
88
  const collection = this.db.db().collection(tableName);
89
89
  const row = await collection.findOne({ [this.getPrimaryKey(resource)]: key });
@@ -97,7 +97,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
97
97
  continue; // should I continue or throw an error?
98
98
  throw new Error(`Resource '${resource.table}' has no column '${key_1}' defined`);
99
99
  }
100
- newRow[key_1] = this.getFieldValue(dbKey, value);
100
+ newRow[key_1] = value;
101
101
  }
102
102
  console.log('newRow', newRow);
103
103
  return newRow;
@@ -121,7 +121,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
121
121
  return value;
122
122
  }
123
123
 
124
- async getData({ resource, limit, offset, sort, filters }) {
124
+ async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }) {
125
125
  // const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
126
126
  const tableName = resource.table;
127
127
 
@@ -140,7 +140,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
140
140
  return { data: result, total }
141
141
  }
142
142
 
143
- async getMinMaxForColumns({ resource, columns }) {
143
+ async getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
144
144
  const tableName = resource.table;
145
145
  const collection = this.db.db().collection(tableName);
146
146
  const result = {};
@@ -160,18 +160,12 @@ class MongoConnector implements AdminForthDataSourceConnector {
160
160
  const columns = Object.keys(record);
161
161
  const newRecord = {};
162
162
  for (const colName of columns) {
163
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
164
- if (col) {
165
- newRecord[colName] = this.setFieldValue(col, record[colName]);
166
- } else {
167
- newRecord[colName] = record[colName];
168
- }
163
+ newRecord[colName] = record[colName];
169
164
  }
170
-
171
165
  await collection.insertOne(newRecord);
172
166
  }
173
167
 
174
- async updateRecord({ resource, recordId, record, newValues }) {
168
+ async updateRecord({ resource, recordId, newValues }) {
175
169
  const collection = this.db.db().collection(resource.table);
176
170
  await collection.updateOne({ [this.getPrimaryKey(resource)]: recordId }, { $set: newValues });
177
171
  }
@@ -1,15 +1,16 @@
1
1
  import dayjs from 'dayjs';
2
2
  import pkg from 'pg';
3
3
  import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
4
-
4
+ import AdminForthBaseConnector from './baseConnector.js';
5
5
  const { Client } = pkg;
6
6
 
7
7
 
8
- class PostgresConnector implements AdminForthDataSourceConnector {
8
+ class PostgresConnector extends AdminForthBaseConnector implements AdminForthDataSourceConnector {
9
9
 
10
10
  db: any;
11
11
 
12
12
  constructor({ url }) {
13
+ super();
13
14
  this.db = new Client({
14
15
  connectionString: url
15
16
  });
@@ -34,12 +35,12 @@ class PostgresConnector implements AdminForthDataSourceConnector {
34
35
  [AdminForthFilterOperators.ILIKE]: 'ILIKE',
35
36
  [AdminForthFilterOperators.IN]: 'IN',
36
37
  [AdminForthFilterOperators.NIN]: 'NOT IN',
37
- };
38
+ };
38
39
 
39
- SortDirectionsMap = {
40
+ SortDirectionsMap = {
40
41
  [AdminForthSortDirections.asc]: 'ASC',
41
42
  [AdminForthSortDirections.desc]: 'DESC',
42
- };
43
+ };
43
44
 
44
45
  async discoverFields(resource) {
45
46
  const tableName = resource.table;
@@ -125,8 +126,8 @@ class PostgresConnector implements AdminForthDataSourceConnector {
125
126
  field.default = row.dflt_value;
126
127
  field.required = row.notnull && !row.dflt_value;
127
128
  fieldTypes[row.name] = field
128
- });
129
- return fieldTypes;
129
+ });
130
+ return fieldTypes;
130
131
  }
131
132
 
132
133
  getFieldValue(field, value) {
@@ -153,15 +154,8 @@ class PostgresConnector implements AdminForthDataSourceConnector {
153
154
  return value;
154
155
  }
155
156
 
156
- getPrimaryKey(resource) {
157
- for (const col of resource.dataSourceColumns) {
158
- if (col.primaryKey) {
159
- return col.name;
160
- }
161
- }
162
- }
163
157
 
164
- async getRecordByPrimaryKey(resource, key) {
158
+ async getRecordByPrimaryKeyWithOriginalTypes(resource, key) {
165
159
  const tableName = resource.table;
166
160
  const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
167
161
  const stmt = await this.db.query(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = $1`, [key]);
@@ -171,7 +165,7 @@ class PostgresConnector implements AdminForthDataSourceConnector {
171
165
  }
172
166
  const newRow = {};
173
167
  for (const [key_1, value] of Object.entries(row)) {
174
- newRow[key_1] = this.getFieldValue(resource.dataSourceColumns.find((col_1) => col_1.name == key_1), value);
168
+ newRow[key_1] = value;
175
169
  }
176
170
  return newRow;
177
171
  }
@@ -192,7 +186,7 @@ class PostgresConnector implements AdminForthDataSourceConnector {
192
186
  return value;
193
187
  }
194
188
 
195
- async getData({ resource, limit, offset, sort, filters }) {
189
+ async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }) {
196
190
  const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
197
191
  const tableName = resource.table;
198
192
 
@@ -220,12 +214,7 @@ class PostgresConnector implements AdminForthDataSourceConnector {
220
214
  const filterValues = [];
221
215
  filters.length ? filters.forEach((f) => {
222
216
  // for arrays do set in map
223
- let v;
224
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
225
- v = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
226
- } else {
227
- v = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
228
- }
217
+ let v = f.value;
229
218
 
230
219
  if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
231
220
  filterValues.push(`%${v}%`);
@@ -247,12 +236,11 @@ class PostgresConnector implements AdminForthDataSourceConnector {
247
236
  const rows = stmt.rows;
248
237
 
249
238
  const total = (await this.db.query(`SELECT COUNT(*) FROM ${tableName} ${where}`, filterValues)).rows[0].count;
250
- // run all fields via getFieldValue
251
239
  return {
252
240
  data: rows.map((row) => {
253
241
  const newRow = {};
254
242
  for (const [key, value] of Object.entries(row)) {
255
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
243
+ newRow[key] = value;
256
244
  }
257
245
  return newRow;
258
246
  }),
@@ -260,15 +248,14 @@ class PostgresConnector implements AdminForthDataSourceConnector {
260
248
  };
261
249
  }
262
250
 
263
- async getMinMaxForColumns({ resource, columns }) {
251
+ async getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
264
252
  const tableName = resource.table;
265
253
  const result = {};
266
254
  await Promise.all(columns.map(async (col) => {
267
255
  const stmt = await this.db.query(`SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`);
268
256
  const { min, max } = stmt.rows[0];
269
257
  result[col.name] = {
270
- min: this.getFieldValue(col, min),
271
- max: this.getFieldValue(col, max),
258
+ min, max,
272
259
  };
273
260
  }))
274
261
  return result;
@@ -278,21 +265,14 @@ class PostgresConnector implements AdminForthDataSourceConnector {
278
265
  const tableName = resource.table;
279
266
  const columns = Object.keys(record);
280
267
  const placeholders = columns.map((_, i) => `$${i + 1}`).join(', ');
281
- const values = columns.map((colName) => {
282
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
283
- if (col) {
284
- return this.setFieldValue(col, record[colName])
285
- } else {
286
- return record[colName];
287
- }
288
- });
268
+ const values = columns.map((colName) => record[colName]);
289
269
  for (let i = 0; i < columns.length; i++) {
290
270
  columns[i] = `"${columns[i]}"`;
291
271
  }
292
272
  await this.db.query(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`, values);
293
273
  }
294
274
 
295
- async updateRecord({ resource, recordId, record, newValues }) {
275
+ async updateRecord({ resource, recordId, newValues }) {
296
276
  const values = [...Object.values(newValues), recordId];
297
277
  const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
298
278
  await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
@@ -1,19 +1,19 @@
1
1
  import betterSqlite3 from 'better-sqlite3';
2
- import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
3
-
2
+ import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector, AdminForthResource, AdminForthResourceColumn } from '../types/AdminForthConfig.js';
3
+ import AdminForthBaseConnector from './baseConnector.js';
4
4
  import dayjs from 'dayjs';
5
5
 
6
- class SQLiteConnector implements AdminForthDataSourceConnector {
6
+ class SQLiteConnector extends AdminForthBaseConnector implements AdminForthDataSourceConnector {
7
7
 
8
8
  db: any;
9
9
 
10
- constructor({ url }) {
10
+ constructor({ url }: { url: string }) {
11
+ super();
11
12
  // create connection here
12
-
13
13
  this.db = betterSqlite3(url.replace('sqlite://', ''));
14
14
  }
15
15
 
16
- async discoverFields(resource) {
16
+ async discoverFields(resource: AdminForthResource): Promise<{[key: string]: AdminForthResourceColumn}> {
17
17
  const tableName = resource.table;
18
18
  const stmt = this.db.prepare(`PRAGMA table_info(${tableName})`);
19
19
  const rows = await stmt.all();
@@ -59,15 +59,7 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
59
59
  return fieldTypes;
60
60
  }
61
61
 
62
- getPrimaryKey(resource) {
63
- for (const col of resource.dataSourceColumns) {
64
- if (col.primaryKey) {
65
- return col.name;
66
- }
67
- }
68
- }
69
-
70
- getFieldValue(field, value) {
62
+ getFieldValue(field: AdminForthResourceColumn, value: any): any {
71
63
  if (field.type == AdminForthDataTypes.DATETIME) {
72
64
  if (!value) {
73
65
  return null;
@@ -93,7 +85,7 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
93
85
  return value;
94
86
  }
95
87
 
96
- async getRecordByPrimaryKey(resource, key) {
88
+ async getRecordByPrimaryKeyWithOriginalTypes(resource: AdminForthResource, key: any): Promise<any> {
97
89
  const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
98
90
  const tableName = resource.table;
99
91
  const stmt = this.db.prepare(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = ?`);
@@ -103,12 +95,12 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
103
95
  }
104
96
  const newRow = {};
105
97
  for (const [key, value] of Object.entries(row)) {
106
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
98
+ newRow[key] = value;
107
99
  }
108
100
  return newRow;
109
101
  }
110
102
 
111
- setFieldValue(field, value) {
103
+ setFieldValue(field: AdminForthResourceColumn, value: any): any {
112
104
  if (field.type == AdminForthDataTypes.DATETIME) {
113
105
  if (!value) {
114
106
  return null;
@@ -146,7 +138,13 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
146
138
  };
147
139
 
148
140
 
149
- getData({ resource, limit, offset, sort, filters }) {
141
+ async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
142
+ resource: AdminForthResource,
143
+ limit: number,
144
+ offset: number,
145
+ sort: { field: string, direction: AdminForthSortDirections }[],
146
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
147
+ }): Promise<{ data: any[], total: number }> {
150
148
  const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
151
149
  const tableName = resource.table;
152
150
 
@@ -165,17 +163,11 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
165
163
  return `${field} ${operator} ${placeholder}`
166
164
  }).join(' AND ')}` : '';
167
165
 
168
-
169
166
  const filterValues = [];
170
167
 
171
168
  filters.length ? filters.forEach((f) => {
172
169
  // for arrays do set in map
173
- let v;
174
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
175
- v = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
176
- } else {
177
- v = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
178
- }
170
+ const v = f.value;
179
171
 
180
172
  if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
181
173
  filterValues.push(`%${v}%`);
@@ -196,15 +188,17 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
196
188
  if (process.env.HEAVY_DEBUG) {
197
189
  console.log('🪲 SQLITE Query', q, 'params:', d);
198
190
  }
199
- const rows = stmt.all(d);
191
+ const rows = await stmt.all(d);
192
+
193
+ const total = (
194
+ await this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues])
195
+ )['COUNT(*)'];
200
196
 
201
- const total = this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues])['COUNT(*)'];
202
- // run all fields via getFieldValue
203
197
  return {
204
198
  data: rows.map((row) => {
205
199
  const newRow = {};
206
200
  for (const [key, value] of Object.entries(row)) {
207
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
201
+ newRow[key] = value;
208
202
  }
209
203
  return newRow;
210
204
  }),
@@ -212,50 +206,45 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
212
206
  };
213
207
  }
214
208
 
215
- async getMinMaxForColumns({ resource, columns }) {
209
+ async getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }> {
216
210
  const tableName = resource.table;
217
211
  const result = {};
218
212
  await Promise.all(columns.map(async (col) => {
219
213
  const stmt = await this.db.prepare(`SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`);
220
214
  const { min, max } = stmt.get();
221
215
  result[col.name] = {
222
- min: this.getFieldValue(col, min),
223
- max: this.getFieldValue(col, max),
216
+ min, max,
224
217
  };
225
218
  }))
226
219
  return result;
227
220
  }
228
221
 
229
- async createRecord({ resource, record }) {
230
- const tableName = resource.table;
231
- const columns = Object.keys(record);
232
- const placeholders = columns.map(() => '?').join(', ');
233
- const values = columns.map((colName) => {
234
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
235
- if (col) {
236
- return this.setFieldValue(col, record[colName])
237
- } else {
238
- return record[colName];
239
- }
240
- });
241
- this.db.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`).run(values);
222
+ async createRecord({ resource, record }: { resource: AdminForthResource, record: any }) {
223
+ const tableName = resource.table;
224
+ const columns = Object.keys(record);
225
+ const placeholders = columns.map(() => '?').join(', ');
226
+ const values = columns.map((colName) => record[colName]);
227
+ const q = this.db.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`)
228
+ await q.run(values);
242
229
  }
243
230
 
244
- async updateRecord({ resource, recordId, record, newValues }) {
245
- const columnsWithPlaceholders = Object.keys(newValues).map((col) => `${col} = ?`);
246
- const values = [...Object.values(newValues), recordId];
231
+ async updateRecord({ resource, recordId, newValues }: { resource: AdminForthResource, recordId: any, newValues: any }) {
232
+ const columnsWithPlaceholders = Object.keys(newValues).map((col) => `${col} = ?`);
233
+ const values = [...Object.values(newValues), recordId];
247
234
 
248
- this.db.prepare(
249
- `UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`
250
- ).run(values);
235
+ const q = this.db.prepare(
236
+ `UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`
237
+ )
238
+ await q.run(values);
251
239
  }
252
240
 
253
- async deleteRecord({ resource, recordId }) {
254
- this.db.prepare(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`).run(recordId);
241
+ async deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }) {
242
+ const q = this.db.prepare(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`);
243
+ await q.run(recordId);
255
244
  }
256
245
 
257
246
  close() {
258
- this.db.close();
247
+ this.db.close();
259
248
  }
260
249
  }
261
250
 
@@ -0,0 +1,90 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { AdminForthFilterOperators } from "../types/AdminForthConfig.js";
11
+ export default class AdminForthBaseConnector {
12
+ getPrimaryKey(resource) {
13
+ for (const col of resource.dataSourceColumns) {
14
+ if (col.primaryKey) {
15
+ return col.name;
16
+ }
17
+ }
18
+ }
19
+ getRecordByPrimaryKeyWithOriginalTypes(resource, id) {
20
+ throw new Error('Method not implemented.');
21
+ }
22
+ getDataWithOriginalTypes({ resource, limit, offset, sort, filters }) {
23
+ throw new Error('Method not implemented.');
24
+ }
25
+ discoverFields(resource) {
26
+ throw new Error('Method not implemented.');
27
+ }
28
+ getFieldValue(field, value) {
29
+ throw new Error('Method not implemented.');
30
+ }
31
+ setFieldValue(field, value) {
32
+ throw new Error('Method not implemented.');
33
+ }
34
+ getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
35
+ throw new Error('Method not implemented.');
36
+ }
37
+ createRecord({ resource, record }) {
38
+ throw new Error('Method not implemented.');
39
+ }
40
+ updateRecord({ resource, recordId, newValues }) {
41
+ throw new Error('Method not implemented.');
42
+ }
43
+ deleteRecord({ resource, recordId }) {
44
+ throw new Error('Method not implemented.');
45
+ }
46
+ getData(_a) {
47
+ return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
48
+ if (filters) {
49
+ filters.map((f) => {
50
+ if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
51
+ f.value = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
52
+ }
53
+ else {
54
+ f.value = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
55
+ }
56
+ });
57
+ }
58
+ const d = yield this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters });
59
+ // call getFieldValue for each field
60
+ d.data.map((record) => {
61
+ for (const col of resource.dataSourceColumns) {
62
+ record[col.name] = this.getFieldValue(col, record[col.name]);
63
+ }
64
+ });
65
+ return d;
66
+ });
67
+ }
68
+ getMinMaxForColumns(_a) {
69
+ return __awaiter(this, arguments, void 0, function* ({ resource, columns }) {
70
+ const mm = yield this.getMinMaxForColumnsWithOriginalTypes({ resource, columns });
71
+ const result = {};
72
+ for (const col of columns) {
73
+ result[col.name] = {
74
+ min: this.getFieldValue(col, mm[col.name].min),
75
+ max: this.getFieldValue(col, mm[col.name].max),
76
+ };
77
+ }
78
+ return result;
79
+ });
80
+ }
81
+ getRecordByPrimaryKey(resource, recordId) {
82
+ return this.getRecordByPrimaryKeyWithOriginalTypes(resource, recordId).then((record) => {
83
+ const newRecord = {};
84
+ for (const col of resource.dataSourceColumns) {
85
+ newRecord[col.name] = this.getFieldValue(col, record[col.name]);
86
+ }
87
+ return newRecord;
88
+ });
89
+ }
90
+ }
@@ -84,7 +84,7 @@ class MongoConnector {
84
84
  }
85
85
  return value;
86
86
  }
87
- getRecordByPrimaryKey(resource, key) {
87
+ getRecordByPrimaryKeyWithOriginalTypes(resource, key) {
88
88
  return __awaiter(this, void 0, void 0, function* () {
89
89
  const tableName = resource.table;
90
90
  const collection = this.db.db().collection(tableName);
@@ -99,7 +99,7 @@ class MongoConnector {
99
99
  continue; // should I continue or throw an error?
100
100
  throw new Error(`Resource '${resource.table}' has no column '${key_1}' defined`);
101
101
  }
102
- newRow[key_1] = this.getFieldValue(dbKey, value);
102
+ newRow[key_1] = value;
103
103
  }
104
104
  console.log('newRow', newRow);
105
105
  return newRow;
@@ -124,7 +124,7 @@ class MongoConnector {
124
124
  }
125
125
  return value;
126
126
  }
127
- getData(_a) {
127
+ getDataWithOriginalTypes(_a) {
128
128
  return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
129
129
  // const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
130
130
  const tableName = resource.table;
@@ -142,7 +142,7 @@ class MongoConnector {
142
142
  return { data: result, total };
143
143
  });
144
144
  }
145
- getMinMaxForColumns(_a) {
145
+ getMinMaxForColumnsWithOriginalTypes(_a) {
146
146
  return __awaiter(this, arguments, void 0, function* ({ resource, columns }) {
147
147
  const tableName = resource.table;
148
148
  const collection = this.db.db().collection(tableName);
@@ -164,19 +164,13 @@ class MongoConnector {
164
164
  const columns = Object.keys(record);
165
165
  const newRecord = {};
166
166
  for (const colName of columns) {
167
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
168
- if (col) {
169
- newRecord[colName] = this.setFieldValue(col, record[colName]);
170
- }
171
- else {
172
- newRecord[colName] = record[colName];
173
- }
167
+ newRecord[colName] = record[colName];
174
168
  }
175
169
  yield collection.insertOne(newRecord);
176
170
  });
177
171
  }
178
172
  updateRecord(_a) {
179
- return __awaiter(this, arguments, void 0, function* ({ resource, recordId, record, newValues }) {
173
+ return __awaiter(this, arguments, void 0, function* ({ resource, recordId, newValues }) {
180
174
  const collection = this.db.db().collection(resource.table);
181
175
  yield collection.updateOne({ [this.getPrimaryKey(resource)]: recordId }, { $set: newValues });
182
176
  });
@@ -10,9 +10,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import dayjs from 'dayjs';
11
11
  import pkg from 'pg';
12
12
  import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
13
+ import AdminForthBaseConnector from './baseConnector.js';
13
14
  const { Client } = pkg;
14
- class PostgresConnector {
15
+ class PostgresConnector extends AdminForthBaseConnector {
15
16
  constructor({ url }) {
17
+ super();
16
18
  this.OperatorsMap = {
17
19
  [AdminForthFilterOperators.EQ]: '=',
18
20
  [AdminForthFilterOperators.NE]: '!=',
@@ -152,14 +154,7 @@ class PostgresConnector {
152
154
  }
153
155
  return value;
154
156
  }
155
- getPrimaryKey(resource) {
156
- for (const col of resource.dataSourceColumns) {
157
- if (col.primaryKey) {
158
- return col.name;
159
- }
160
- }
161
- }
162
- getRecordByPrimaryKey(resource, key) {
157
+ getRecordByPrimaryKeyWithOriginalTypes(resource, key) {
163
158
  return __awaiter(this, void 0, void 0, function* () {
164
159
  const tableName = resource.table;
165
160
  const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
@@ -170,7 +165,7 @@ class PostgresConnector {
170
165
  }
171
166
  const newRow = {};
172
167
  for (const [key_1, value] of Object.entries(row)) {
173
- newRow[key_1] = this.getFieldValue(resource.dataSourceColumns.find((col_1) => col_1.name == key_1), value);
168
+ newRow[key_1] = value;
174
169
  }
175
170
  return newRow;
176
171
  });
@@ -192,7 +187,7 @@ class PostgresConnector {
192
187
  }
193
188
  return value;
194
189
  }
195
- getData(_a) {
190
+ getDataWithOriginalTypes(_a) {
196
191
  return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
197
192
  const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
198
193
  const tableName = resource.table;
@@ -220,13 +215,7 @@ class PostgresConnector {
220
215
  const filterValues = [];
221
216
  filters.length ? filters.forEach((f) => {
222
217
  // for arrays do set in map
223
- let v;
224
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
225
- v = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
226
- }
227
- else {
228
- v = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
229
- }
218
+ let v = f.value;
230
219
  if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
231
220
  filterValues.push(`%${v}%`);
232
221
  }
@@ -247,12 +236,11 @@ class PostgresConnector {
247
236
  const stmt = yield this.db.query(selectQuery, d);
248
237
  const rows = stmt.rows;
249
238
  const total = (yield this.db.query(`SELECT COUNT(*) FROM ${tableName} ${where}`, filterValues)).rows[0].count;
250
- // run all fields via getFieldValue
251
239
  return {
252
240
  data: rows.map((row) => {
253
241
  const newRow = {};
254
242
  for (const [key, value] of Object.entries(row)) {
255
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
243
+ newRow[key] = value;
256
244
  }
257
245
  return newRow;
258
246
  }),
@@ -260,7 +248,7 @@ class PostgresConnector {
260
248
  };
261
249
  });
262
250
  }
263
- getMinMaxForColumns(_a) {
251
+ getMinMaxForColumnsWithOriginalTypes(_a) {
264
252
  return __awaiter(this, arguments, void 0, function* ({ resource, columns }) {
265
253
  const tableName = resource.table;
266
254
  const result = {};
@@ -268,8 +256,7 @@ class PostgresConnector {
268
256
  const stmt = yield this.db.query(`SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`);
269
257
  const { min, max } = stmt.rows[0];
270
258
  result[col.name] = {
271
- min: this.getFieldValue(col, min),
272
- max: this.getFieldValue(col, max),
259
+ min, max,
273
260
  };
274
261
  })));
275
262
  return result;
@@ -280,15 +267,7 @@ class PostgresConnector {
280
267
  const tableName = resource.table;
281
268
  const columns = Object.keys(record);
282
269
  const placeholders = columns.map((_, i) => `$${i + 1}`).join(', ');
283
- const values = columns.map((colName) => {
284
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
285
- if (col) {
286
- return this.setFieldValue(col, record[colName]);
287
- }
288
- else {
289
- return record[colName];
290
- }
291
- });
270
+ const values = columns.map((colName) => record[colName]);
292
271
  for (let i = 0; i < columns.length; i++) {
293
272
  columns[i] = `"${columns[i]}"`;
294
273
  }
@@ -296,7 +275,7 @@ class PostgresConnector {
296
275
  });
297
276
  }
298
277
  updateRecord(_a) {
299
- return __awaiter(this, arguments, void 0, function* ({ resource, recordId, record, newValues }) {
278
+ return __awaiter(this, arguments, void 0, function* ({ resource, recordId, newValues }) {
300
279
  const values = [...Object.values(newValues), recordId];
301
280
  const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
302
281
  yield this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
@@ -9,10 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import betterSqlite3 from 'better-sqlite3';
11
11
  import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
12
+ import AdminForthBaseConnector from './baseConnector.js';
12
13
  import dayjs from 'dayjs';
13
- class SQLiteConnector {
14
+ class SQLiteConnector extends AdminForthBaseConnector {
14
15
  constructor({ url }) {
15
- // create connection here
16
+ super();
16
17
  this.OperatorsMap = {
17
18
  [AdminForthFilterOperators.EQ]: '=',
18
19
  [AdminForthFilterOperators.NE]: '!=',
@@ -29,6 +30,7 @@ class SQLiteConnector {
29
30
  [AdminForthSortDirections.asc]: 'ASC',
30
31
  [AdminForthSortDirections.desc]: 'DESC',
31
32
  };
33
+ // create connection here
32
34
  this.db = betterSqlite3(url.replace('sqlite://', ''));
33
35
  }
34
36
  discoverFields(resource) {
@@ -85,13 +87,6 @@ class SQLiteConnector {
85
87
  return fieldTypes;
86
88
  });
87
89
  }
88
- getPrimaryKey(resource) {
89
- for (const col of resource.dataSourceColumns) {
90
- if (col.primaryKey) {
91
- return col.name;
92
- }
93
- }
94
- }
95
90
  getFieldValue(field, value) {
96
91
  if (field.type == AdminForthDataTypes.DATETIME) {
97
92
  if (!value) {
@@ -118,7 +113,7 @@ class SQLiteConnector {
118
113
  }
119
114
  return value;
120
115
  }
121
- getRecordByPrimaryKey(resource, key) {
116
+ getRecordByPrimaryKeyWithOriginalTypes(resource, key) {
122
117
  return __awaiter(this, void 0, void 0, function* () {
123
118
  const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
124
119
  const tableName = resource.table;
@@ -129,7 +124,7 @@ class SQLiteConnector {
129
124
  }
130
125
  const newRow = {};
131
126
  for (const [key, value] of Object.entries(row)) {
132
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
127
+ newRow[key] = value;
133
128
  }
134
129
  return newRow;
135
130
  });
@@ -153,65 +148,60 @@ class SQLiteConnector {
153
148
  }
154
149
  return value;
155
150
  }
156
- getData({ resource, limit, offset, sort, filters }) {
157
- const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
158
- const tableName = resource.table;
159
- const where = filters.length ? `WHERE ${filters.map((f, i) => {
160
- let placeholder = '?';
161
- let field = f.field;
162
- let operator = this.OperatorsMap[f.operator];
163
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
164
- placeholder = `(${f.value.map(() => '?').join(', ')})`;
165
- }
166
- else if (f.operator == AdminForthFilterOperators.ILIKE) {
167
- placeholder = `LOWER(?)`;
168
- field = `LOWER(${f.field})`;
169
- operator = 'LIKE';
170
- }
171
- return `${field} ${operator} ${placeholder}`;
172
- }).join(' AND ')}` : '';
173
- const filterValues = [];
174
- filters.length ? filters.forEach((f) => {
175
- // for arrays do set in map
176
- let v;
177
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
178
- v = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
179
- }
180
- else {
181
- v = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
182
- }
183
- if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
184
- filterValues.push(`%${v}%`);
185
- }
186
- else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
187
- filterValues.push(...v);
188
- }
189
- else {
190
- filterValues.push(v);
191
- }
192
- }) : [];
193
- const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
194
- const q = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT ? OFFSET ?`;
195
- const stmt = this.db.prepare(q);
196
- const d = [...filterValues, limit, offset];
197
- if (process.env.HEAVY_DEBUG) {
198
- console.log('🪲 SQLITE Query', q, 'params:', d);
199
- }
200
- const rows = stmt.all(d);
201
- const total = this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues])['COUNT(*)'];
202
- // run all fields via getFieldValue
203
- return {
204
- data: rows.map((row) => {
205
- const newRow = {};
206
- for (const [key, value] of Object.entries(row)) {
207
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
151
+ getDataWithOriginalTypes(_a) {
152
+ return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
153
+ const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
154
+ const tableName = resource.table;
155
+ const where = filters.length ? `WHERE ${filters.map((f, i) => {
156
+ let placeholder = '?';
157
+ let field = f.field;
158
+ let operator = this.OperatorsMap[f.operator];
159
+ if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
160
+ placeholder = `(${f.value.map(() => '?').join(', ')})`;
208
161
  }
209
- return newRow;
210
- }),
211
- total,
212
- };
162
+ else if (f.operator == AdminForthFilterOperators.ILIKE) {
163
+ placeholder = `LOWER(?)`;
164
+ field = `LOWER(${f.field})`;
165
+ operator = 'LIKE';
166
+ }
167
+ return `${field} ${operator} ${placeholder}`;
168
+ }).join(' AND ')}` : '';
169
+ const filterValues = [];
170
+ filters.length ? filters.forEach((f) => {
171
+ // for arrays do set in map
172
+ const v = f.value;
173
+ if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
174
+ filterValues.push(`%${v}%`);
175
+ }
176
+ else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
177
+ filterValues.push(...v);
178
+ }
179
+ else {
180
+ filterValues.push(v);
181
+ }
182
+ }) : [];
183
+ const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
184
+ const q = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT ? OFFSET ?`;
185
+ const stmt = this.db.prepare(q);
186
+ const d = [...filterValues, limit, offset];
187
+ if (process.env.HEAVY_DEBUG) {
188
+ console.log('🪲 SQLITE Query', q, 'params:', d);
189
+ }
190
+ const rows = yield stmt.all(d);
191
+ const total = (yield this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues]))['COUNT(*)'];
192
+ return {
193
+ data: rows.map((row) => {
194
+ const newRow = {};
195
+ for (const [key, value] of Object.entries(row)) {
196
+ newRow[key] = value;
197
+ }
198
+ return newRow;
199
+ }),
200
+ total,
201
+ };
202
+ });
213
203
  }
214
- getMinMaxForColumns(_a) {
204
+ getMinMaxForColumnsWithOriginalTypes(_a) {
215
205
  return __awaiter(this, arguments, void 0, function* ({ resource, columns }) {
216
206
  const tableName = resource.table;
217
207
  const result = {};
@@ -219,8 +209,7 @@ class SQLiteConnector {
219
209
  const stmt = yield this.db.prepare(`SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`);
220
210
  const { min, max } = stmt.get();
221
211
  result[col.name] = {
222
- min: this.getFieldValue(col, min),
223
- max: this.getFieldValue(col, max),
212
+ min, max,
224
213
  };
225
214
  })));
226
215
  return result;
@@ -231,28 +220,23 @@ class SQLiteConnector {
231
220
  const tableName = resource.table;
232
221
  const columns = Object.keys(record);
233
222
  const placeholders = columns.map(() => '?').join(', ');
234
- const values = columns.map((colName) => {
235
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
236
- if (col) {
237
- return this.setFieldValue(col, record[colName]);
238
- }
239
- else {
240
- return record[colName];
241
- }
242
- });
243
- this.db.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`).run(values);
223
+ const values = columns.map((colName) => record[colName]);
224
+ const q = this.db.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`);
225
+ yield q.run(values);
244
226
  });
245
227
  }
246
228
  updateRecord(_a) {
247
- return __awaiter(this, arguments, void 0, function* ({ resource, recordId, record, newValues }) {
229
+ return __awaiter(this, arguments, void 0, function* ({ resource, recordId, newValues }) {
248
230
  const columnsWithPlaceholders = Object.keys(newValues).map((col) => `${col} = ?`);
249
231
  const values = [...Object.values(newValues), recordId];
250
- this.db.prepare(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`).run(values);
232
+ const q = this.db.prepare(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`);
233
+ yield q.run(values);
251
234
  });
252
235
  }
253
236
  deleteRecord(_a) {
254
237
  return __awaiter(this, arguments, void 0, function* ({ resource, recordId }) {
255
- this.db.prepare(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`).run(recordId);
238
+ const q = this.db.prepare(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`);
239
+ yield q.run(recordId);
256
240
  });
257
241
  }
258
242
  close() {
package/dist/index.js CHANGED
@@ -968,7 +968,14 @@ class AdminForth {
968
968
  if (!allowed) {
969
969
  return { error };
970
970
  }
971
- yield this.createResourceRecord({ resource, record: body['record'], adminUser });
971
+ const { record } = body;
972
+ // call setFieldValue for each column
973
+ for (const column of resource.columns) {
974
+ if (record[column.name] !== undefined) {
975
+ record[column.name] = this.connectors[resource.dataSource].setFieldValue(column, record[column.name]);
976
+ }
977
+ }
978
+ yield this.createResourceRecord({ resource, record, adminUser });
972
979
  const connector = this.connectors[resource.dataSource];
973
980
  return {
974
981
  newRecordId: body['record'][connector.getPrimaryKey(resource)]
package/index.ts CHANGED
@@ -1102,7 +1102,15 @@ class AdminForth implements AdminForthClass {
1102
1102
  return { error };
1103
1103
  }
1104
1104
 
1105
- await this.createResourceRecord({ resource, record: body['record'], adminUser });
1105
+ const { record } = body;
1106
+ // call setFieldValue for each column
1107
+ for (const column of resource.columns) {
1108
+ if (record[column.name] !== undefined) {
1109
+ record[column.name] = this.connectors[resource.dataSource].setFieldValue(column, record[column.name]);
1110
+ }
1111
+ }
1112
+
1113
+ await this.createResourceRecord({ resource, record, adminUser });
1106
1114
  const connector = this.connectors[resource.dataSource];
1107
1115
 
1108
1116
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.1.73",
3
+ "version": "1.1.74",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -82,9 +82,113 @@ export interface AdminForthDataSourceConnector {
82
82
  /**
83
83
  * Function which will be called to fetch record from database.
84
84
  */
85
+ getRecordByPrimaryKeyWithOriginalTypes(resource: AdminForthResource, recordId: string): Promise<any>;
86
+
87
+
88
+ /**
89
+ * Function should go over all columns of table defined in resource.table and try to guess
90
+ * data and constraints for each columns.
91
+ * Type should be saved to:
92
+ * - {@link AdminForthResourceColumn.type}
93
+ * Constraints:
94
+ * - {@link AdminForthResourceColumn.required}
95
+ * - {@link AdminForthResourceColumn.primaryKey}
96
+ * For string fields:
97
+ * - {@link AdminForthResourceColumn.maxLength}
98
+ * For numbers:
99
+ * - {@link AdminForthResourceColumn.min}
100
+ * - {@link AdminForthResourceColumn.max}
101
+ * - {@link AdminForthResourceColumn.minValue}, {@link AdminForthResourceColumn.maxValue}, {@link AdminForthResourceColumn.enum}, {@link AdminForthResourceColumn.foreignResource}, {@link AdminForthResourceColumn.sortable}, {@link AdminForthResourceColumn.backendOnly}, {@link AdminForthResourceColumn.masked}, {@link AdminForthResourceColumn.virtual}, {@link AdminForthResourceColumn.components}, {@link AdminForthResourceColumn.allowMinMaxQuery}, {@link AdminForthResourceColumn.editingNote}, {@link AdminForthResourceColumn.showIn}, {@link AdminForthResourceColumn.isUnique}, {@link AdminForthResourceColumn.validation})
102
+ * Also you can additionally save original column type to {@link AdminForthResourceColumn._underlineType}. This might be later used
103
+ * in {@link AdminForthDataSourceConnector.getFieldValue} and {@link AdminForthDataSourceConnector.setFieldValue} methods.
104
+ *
105
+ *
106
+ * @param resource
107
+ */
108
+ discoverFields(resource: AdminForthResource): Promise<{[key: string]: AdminForthResourceColumn}>;
109
+
110
+
111
+ /**
112
+ * Used to transform record after fetching from database.
113
+ * According to AdminForth convention, if {@link AdminForthResourceColumn.type} is set to {@link AdminForthDataTypes.DATETIME} then it should be transformed to ISO string.
114
+ * @param field
115
+ * @param value
116
+ */
117
+ getFieldValue(field: AdminForthResourceColumn, value: any): any;
118
+
119
+ /**
120
+ * Used to transform record before saving to database. Should perform operation inverse to {@link AdminForthDataSourceConnector.getFieldValue}
121
+ * @param field
122
+ * @param value
123
+ */
124
+ setFieldValue(field: AdminForthResourceColumn, value: any): any;
125
+
126
+ /**
127
+ * Used to fetch data from database.
128
+ * This method is reused both to list records and show one record (by passing limit 1 and offset 0) .
129
+ *
130
+ * Fields are returned from db "as is" then {@link AdminForthBaseConnector.getData} will transform each field using {@link AdminForthDataSourceConnector.getFieldValue}
131
+ */
132
+ getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
133
+ resource: AdminForthResource,
134
+ limit: number,
135
+ offset: number,
136
+ sort: { field: string, direction: AdminForthSortDirections }[],
137
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
138
+ }): Promise<{data: Array<any>, total: number}>;
139
+
140
+
141
+ /**
142
+ * Optional method which used to get min and max values for columns in resource.
143
+ * Called only for columns which have {@link AdminForthResourceColumn.allowMinMaxQuery} set to true.
144
+ *
145
+ * Internally should call {@link AdminForthDataSourceConnector.getFieldValue} for both min and max values.
146
+ */
147
+ getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }>;
148
+
149
+
150
+ /**
151
+ * Used to create record in database.
152
+ */
153
+ createRecord({ resource, record }: { resource: AdminForthResource, record: any }): Promise<void>;
154
+
155
+ /**
156
+ * Used to update record in database.
157
+ * recordId is value of field which is marked as {@link AdminForthResourceColumn.primaryKey}
158
+ * newValues is array of fields which should be updated (might be not all fields in record, but only changed fields).
159
+ */
160
+ updateRecord({ resource, recordId, newValues }:
161
+ { resource: AdminForthResource, recordId: string, newValues: any }
162
+ ): Promise<void>;
163
+
164
+ /**
165
+ * Used to delete record in database.
166
+ */
167
+ deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }): Promise<void>;
168
+ }
169
+
170
+
171
+ /**
172
+ * Interface that exposes methods to interact with AdminForth in standard way
173
+ */
174
+ export interface AdminForthDataSourceConnectorBase extends AdminForthDataSourceConnector {
175
+
176
+ getPrimaryKey(resource: AdminForthResource): string;
177
+
178
+ getData({ resource, limit, offset, sort, filters }: {
179
+ resource: AdminForthResource,
180
+ limit: number,
181
+ offset: number,
182
+ sort: { field: string, direction: AdminForthSortDirections }[],
183
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
184
+ }): Promise<{ data: Array<any>, total: number }>;
185
+
85
186
  getRecordByPrimaryKey(resource: AdminForthResource, recordId: string): Promise<any>;
187
+
188
+ getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }>;
86
189
  }
87
190
 
191
+
88
192
  export interface AdminForthDataSourceConnectorConstructor {
89
193
  new ({ url }: { url: string }): AdminForthDataSourceConnector;
90
194
  }
@@ -95,7 +199,7 @@ export interface AdminForthClass {
95
199
  express: GenericHttpServer;
96
200
 
97
201
  connectors: {
98
- [key: string]: AdminForthDataSourceConnector,
202
+ [key: string]: AdminForthDataSourceConnectorBase;
99
203
  };
100
204
 
101
205
  createResourceRecord(params: { resource: AdminForthResource, record: any, adminUser: AdminUser }): Promise<any>;
@@ -425,21 +529,57 @@ export type AdminForthResourceColumn = {
425
529
  * Custom components which will be used to render this field in the admin panel.
426
530
  */
427
531
  components?: AdminForthFieldComponents
532
+
533
+ /**
534
+ * Maximum length of string that can be entered in this field.
535
+ */
428
536
  maxLength?: number,
537
+
538
+ /**
539
+ * Minimum length of string that can be entered in this field.
540
+ */
429
541
  minLength?: number,
542
+
430
543
  min?: number,
431
544
  max?: number,
545
+
546
+ /**
547
+ * Minimum value that can be entered in this field.
548
+ */
432
549
  minValue?: number,
550
+
551
+ /**
552
+ * Maximum value that can be entered in this field.
553
+ */
433
554
  maxValue?: number,
555
+
556
+ /**
557
+ * Enum of possible values for this field.
558
+ */
434
559
  enum?: Array<AdminForthColumnEnumItem>,
560
+
561
+ /**
562
+ * Foreign resource which has pk column with values same that written in this column.
563
+ */
435
564
  foreignResource?:AdminForthForeignResource,
565
+
436
566
  sortable?: boolean,
437
- backendOnly?: boolean, // if true field will not be passed to UI under no circumstances, but will be presented in hooks
567
+
568
+ /**
569
+ * if true field will !not be passed to UI under no circumstances, but will be presented in hooks
570
+ */
571
+ backendOnly?: boolean,
438
572
 
439
573
  /**
440
574
  * Masked fields will be displayed as `*****` on Edit and Create pages.
441
575
  */
442
576
  masked?: boolean,
577
+
578
+
579
+ /**
580
+ * Internal type which indicates original type of column in database.
581
+ */
582
+ _underlineType?: string,
443
583
  }
444
584
 
445
585