Skip to content

Commit 9cf36ca

Browse files
committed
feat: enhance getAllColumnsInTable method to return detailed column information
1 parent 441eab6 commit 9cf36ca

File tree

8 files changed

+102
-36
lines changed

8 files changed

+102
-36
lines changed
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { v1 as uuid } from "uuid";
2-
import { AdminForthResourceInput } from "adminforth";
2+
import { AdminForthResourceInput, AdminForthDataTypes } from "adminforth";
33

44
export default {
55
dataSource: "{{dataSource}}",
@@ -9,11 +9,13 @@ export default {
99
columns: [
1010
{{#each columns}}
1111
{
12-
name: "{{this}}"
12+
name: "{{this.name}}"{{#if this.type}},
13+
type: AdminForthDataTypes.{{this.type}}{{/if}}{{#if this.isPrimaryKey}},
14+
primaryKey: true{{/if}}
1315
}{{#unless @last}},{{/unless}}
1416
{{/each}}
1517
],
1618
options: {
1719
listPageSize: 10,
1820
},
19-
} as AdminForthResourceInput;
21+
} as AdminForthResourceInput;

adminforth/dataConnectors/baseConnector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
357357
throw new Error('getAllTables() must be implemented in subclass');
358358
}
359359

360-
async getAllColumnsInTable(tableName: string): Promise<string[]> {
360+
async getAllColumnsInTable(tableName: string): Promise<Array<{ name: string; type?: string; isPrimaryKey?: boolean }>> {
361361
throw new Error('getAllColumnsInTable() must be implemented in subclass');
362362
}
363363

adminforth/dataConnectors/clickhouse.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
4343
return jsonResult.data.map((row: any) => row.name);
4444
}
4545

46-
async getAllColumnsInTable(tableName: string): Promise<string[]> {
46+
async getAllColumnsInTable(tableName: string): Promise<Array<{ name: string; type?: string; isPrimaryKey?: boolean }>> {
4747
const res = await this.client.query({
4848
query: `
4949
SELECT name
@@ -57,7 +57,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
5757
});
5858

5959
const jsonResult = await res.json();
60-
return jsonResult.data.map((row: any) => row.name);
60+
return jsonResult.data.map((row: any) => ({ name: row.name }));
6161
}
6262

6363
async discoverFields(resource: AdminForthResource): Promise<{[key: string]: AdminForthResourceColumn}> {

adminforth/dataConnectors/mongo.ts

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,32 +54,89 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS
5454
return collections.map(col => col.name);
5555
}
5656

57-
async getAllColumnsInTable(collectionName: string): Promise<Array<string>> {
57+
async getAllColumnsInTable(collectionName: string): Promise<Array<{ name: string; type?: string; isPrimaryKey?: boolean }>> {
5858

5959
const sampleDocs = await this.client.db().collection(collectionName).find({}).sort({ _id: -1 }).limit(100).toArray();
60-
61-
const fieldSet = new Set<string>();
62-
60+
61+
const fieldTypes = new Map<string, Set<string>>();
62+
63+
function detectType(value: any): string {
64+
if (value === null || value === undefined) return 'string';
65+
if (typeof value === 'string') return 'string';
66+
if (typeof value === 'boolean') return 'boolean';
67+
if (typeof value === 'number') {
68+
return Number.isInteger(value) ? 'integer' : 'float';
69+
}
70+
if (value instanceof Date) return 'datetime';
71+
if (typeof value === 'object') return 'json';
72+
return 'string';
73+
}
74+
75+
function addType(name: string, type: string) {
76+
if (!fieldTypes.has(name)) {
77+
fieldTypes.set(name, new Set());
78+
}
79+
fieldTypes.get(name)!.add(type);
80+
}
81+
6382
function flattenObject(obj: any, prefix = '') {
64-
Object.entries(obj).forEach(([key, value]) => {
83+
Object.entries(obj).forEach(([key, value]) => {
6584
const fullKey = prefix ? `${prefix}.${key}` : key;
85+
6686
if (fullKey.startsWith('_id.') && typeof value !== 'string' && typeof value !== 'number') {
67-
return;
68-
}
69-
if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
70-
flattenObject(value, fullKey);
87+
return;
88+
}
89+
90+
if (
91+
value instanceof Buffer ||
92+
(value && typeof value === 'object' && (value as any)._bsontype === 'Decimal128')
93+
) {
94+
addType(fullKey, 'json');
95+
return;
96+
}
97+
98+
if (
99+
value &&
100+
typeof value === 'object' &&
101+
!Array.isArray(value) &&
102+
!(value instanceof Date)
103+
) {
104+
// Вместо рекурсивного прохода — просто добавляем этот объект целиком как json
105+
addType(fullKey, 'json');
71106
} else {
72-
fieldSet.add(fullKey);
107+
addType(fullKey, detectType(value));
73108
}
74-
});
109+
});
75110
}
76-
111+
77112
for (const doc of sampleDocs) {
78-
flattenObject(doc);
113+
flattenObject(doc);
79114
}
80-
81-
return Array.from(fieldSet);
82-
}
115+
116+
return Array.from(fieldTypes.entries()).map(([name, types]) => {
117+
const primaryKey = name === '_id';
118+
119+
const priority = ['datetime', 'date', 'integer', 'float', 'boolean', 'json', 'string'];
120+
121+
const matched = priority.find(t => types.has(t)) || 'string';
122+
123+
const typeMap: Record<string, string> = {
124+
string: 'STRING',
125+
integer: 'INTEGER',
126+
float: 'FLOAT',
127+
boolean: 'BOOLEAN',
128+
datetime: 'DATETIME',
129+
date: 'DATE',
130+
json: 'JSON',
131+
};
132+
133+
return {
134+
name,
135+
type: typeMap[matched] ?? 'STRING',
136+
...(primaryKey ? { isPrimaryKey: true } : {}),
137+
};
138+
});
139+
}
83140

84141

85142
async discoverFields(resource) {

adminforth/dataConnectors/mysql.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,19 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
5050
return rows.map((row: any) => row.TABLE_NAME);
5151
}
5252

53-
async getAllColumnsInTable(tableName: string): Promise<Array<string>> {
53+
async getAllColumnsInTable(tableName: string): Promise<Array<{ name: string; type?: string; isPrimaryKey?: boolean }>> {
5454
const [rows] = await this.client.query(
55-
`
56-
SELECT column_name
57-
FROM information_schema.columns
58-
WHERE table_name = ? AND table_schema = DATABASE();
59-
`,
60-
[tableName]
55+
`
56+
SELECT column_name
57+
FROM information_schema.columns
58+
WHERE table_name = ? AND table_schema = DATABASE();
59+
`,
60+
[tableName]
6161
);
62-
return rows.map((row: any) => row.COLUMN_NAME);
62+
63+
return rows.map((row: any) => ({
64+
name: row.COLUMN_NAME,
65+
}));
6366
}
6467

6568
async discoverFields(resource) {

adminforth/dataConnectors/postgres.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,15 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
5454
return res.rows.map(row => row.table_name);
5555
}
5656

57-
async getAllColumnsInTable(tableName: string): Promise<Array<string>> {
57+
async getAllColumnsInTable(tableName: string): Promise<Array<{ name: string; type?: string; isPrimaryKey?: boolean }>> {
5858
const res = await this.client.query(`
5959
SELECT column_name
6060
FROM information_schema.columns
6161
WHERE table_name = $1 AND table_schema = 'public';
6262
`, [tableName]);
63-
return res.rows.map(row => row.column_name);
64-
}
63+
64+
return res.rows.map(row => ({ name: row.column_name }));
65+
}
6566

6667
async discoverFields(resource) {
6768

adminforth/dataConnectors/sqlite.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
1616
const rows = stmt.all();
1717
return rows.map((row) => row.name);
1818
}
19-
async getAllColumnsInTable(tableName: string): Promise<Array<string>> {
19+
async getAllColumnsInTable(tableName: string): Promise<Array<{ name: string }>> {
2020
const stmt = this.client.prepare(`PRAGMA table_info(${tableName});`);
2121
const rows = stmt.all();
22-
return rows.map((row) => row.name);
22+
23+
return rows.map(row => ({
24+
name: row.name || '',
25+
}));
2326
}
2427

2528
async discoverFields(resource: AdminForthResource): Promise<{[key: string]: AdminForthResourceColumn}> {

adminforth/types/Back.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export interface IAdminForthDataSourceConnector {
143143
/**
144144
* Function to get all columns in table.
145145
*/
146-
getAllColumnsInTable(tableName: string): Promise<Array<string>>;
146+
getAllColumnsInTable(tableName: string): Promise<Array<{ name: string; type?: string; isPrimaryKey?: boolean }>>;
147147
/**
148148
* Optional.
149149
* You an redefine this function to define how one record should be fetched from database.

0 commit comments

Comments
 (0)