@solidxai/core 0.1.10 → 0.1.11-beta.0
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/dist/controllers/datasource-introspection.controller.d.ts +234 -0
- package/dist/controllers/datasource-introspection.controller.d.ts.map +1 -0
- package/dist/controllers/datasource-introspection.controller.js +98 -0
- package/dist/controllers/datasource-introspection.controller.js.map +1 -0
- package/dist/controllers/datasource-management.controller.d.ts +18 -0
- package/dist/controllers/datasource-management.controller.d.ts.map +1 -0
- package/dist/controllers/datasource-management.controller.js +55 -0
- package/dist/controllers/datasource-management.controller.js.map +1 -0
- package/dist/dtos/create-datasource-management.dto.d.ts +25 -0
- package/dist/dtos/create-datasource-management.dto.d.ts.map +1 -0
- package/dist/dtos/create-datasource-management.dto.js +169 -0
- package/dist/dtos/create-datasource-management.dto.js.map +1 -0
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts +45 -0
- package/dist/dtos/datasource-introspection-mapping.dto.d.ts.map +1 -0
- package/dist/dtos/datasource-introspection-mapping.dto.js +233 -0
- package/dist/dtos/datasource-introspection-mapping.dto.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts +26 -0
- package/dist/interfaces/datasource-introspection-provider.interface.d.ts.map +1 -0
- package/dist/interfaces/datasource-introspection-provider.interface.js +3 -0
- package/dist/interfaces/datasource-introspection-provider.interface.js.map +1 -0
- package/dist/seeders/seed-data/solid-core-metadata.json +21 -0
- package/dist/services/authentication.service.js +1 -1
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.d.ts.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.js +2 -2
- package/dist/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.js.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.d.ts.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.js +2 -2
- package/dist/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.js.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-provider-utils.d.ts +3 -0
- package/dist/services/dashboard-providers/mq-dashboard-provider-utils.d.ts.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-provider-utils.js +71 -0
- package/dist/services/dashboard-providers/mq-dashboard-provider-utils.js.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.d.ts.map +1 -1
- package/dist/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.js +3 -3
- package/dist/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.js.map +1 -1
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts +11 -0
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.d.ts.map +1 -0
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js +181 -0
- package/dist/services/datasource-introspection/mssql-datasource-introspection-provider.service.js.map +1 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts +11 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.d.ts.map +1 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js +90 -0
- package/dist/services/datasource-introspection/mysql-datasource-introspection-provider.service.js.map +1 -0
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts +11 -0
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.d.ts.map +1 -0
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js +156 -0
- package/dist/services/datasource-introspection/postgres-datasource-introspection-provider.service.js.map +1 -0
- package/dist/services/datasource-introspection.service.d.ts +288 -0
- package/dist/services/datasource-introspection.service.d.ts.map +1 -0
- package/dist/services/datasource-introspection.service.js +1278 -0
- package/dist/services/datasource-introspection.service.js.map +1 -0
- package/dist/services/datasource-management.service.d.ts +60 -0
- package/dist/services/datasource-management.service.d.ts.map +1 -0
- package/dist/services/datasource-management.service.js +461 -0
- package/dist/services/datasource-management.service.js.map +1 -0
- package/dist/services/field-metadata.service.d.ts.map +1 -1
- package/dist/services/field-metadata.service.js +19 -0
- package/dist/services/field-metadata.service.js.map +1 -1
- package/dist/services/model-metadata.service.d.ts +1 -3
- package/dist/services/model-metadata.service.d.ts.map +1 -1
- package/dist/services/model-metadata.service.js +47 -15
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/role-metadata.service.d.ts +1 -0
- package/dist/services/role-metadata.service.d.ts.map +1 -1
- package/dist/services/role-metadata.service.js +13 -0
- package/dist/services/role-metadata.service.js.map +1 -1
- package/dist/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.d.ts.map +1 -1
- package/dist/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.js +2 -3
- package/dist/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.js.map +1 -1
- package/dist/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.d.ts.map +1 -1
- package/dist/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.js +2 -3
- package/dist/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.js.map +1 -1
- package/dist/services/setting.service.d.ts +1 -0
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +32 -8
- package/dist/services/setting.service.js.map +1 -1
- package/dist/services/solid-ts-morph.service.d.ts +1 -1
- package/dist/services/solid-ts-morph.service.d.ts.map +1 -1
- package/dist/services/solid-ts-morph.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +16 -0
- package/dist/solid-core.module.js.map +1 -1
- package/package.json +1 -1
- package/src/controllers/datasource-introspection.controller.ts +58 -0
- package/src/controllers/datasource-management.controller.ts +24 -0
- package/src/dtos/create-datasource-management.dto.ts +139 -0
- package/src/dtos/datasource-introspection-mapping.dto.ts +182 -0
- package/src/index.ts +5 -0
- package/src/interfaces/datasource-introspection-provider.interface.ts +28 -0
- package/src/seeders/seed-data/solid-core-metadata.json +22 -1
- package/src/services/authentication.service.ts +1 -1
- package/src/services/dashboard-providers/mq-dashboard-latency-trend-provider.service.ts +9 -4
- package/src/services/dashboard-providers/mq-dashboard-messages-over-time-provider.service.ts +9 -4
- package/src/services/dashboard-providers/mq-dashboard-provider-utils.ts +82 -0
- package/src/services/dashboard-providers/mq-dashboard-queue-sla-heatmap-provider.service.ts +10 -4
- package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts +185 -0
- package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts +95 -0
- package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts +160 -0
- package/src/services/datasource-introspection.service.ts +1536 -0
- package/src/services/datasource-management.service.ts +566 -0
- package/src/services/field-metadata.service.ts +21 -1
- package/src/services/model-metadata.service.ts +50 -13
- package/src/services/role-metadata.service.ts +14 -0
- package/src/services/selection-providers/mq-dashboard-message-broker-variable-options-provider.service.ts +2 -4
- package/src/services/selection-providers/mq-dashboard-queue-name-variable-options-provider.service.ts +2 -4
- package/src/services/setting.service.ts +43 -12
- package/src/services/solid-ts-morph.service.ts +2 -2
- package/src/solid-core.module.ts +16 -0
|
@@ -2,7 +2,13 @@ import { Injectable } from "@nestjs/common";
|
|
|
2
2
|
import { DashboardWidgetDataProvider } from "src/decorators/dashboard-widget-data-provider.decorator";
|
|
3
3
|
import { IDashboardWidgetDataProvider, IDashboardWidgetDataProviderContext, IDashboardWidgetDataResponseEnvelope } from "src/interfaces";
|
|
4
4
|
import { MqMessageRepository } from "src/repository/mq-message.repository";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
applyMqDashboardFilters,
|
|
7
|
+
buildMqDashboardBucketExpression,
|
|
8
|
+
normalizeBucket,
|
|
9
|
+
normalizeMqDashboardBucketValue,
|
|
10
|
+
toNumber,
|
|
11
|
+
} from "src/services/dashboard-providers/mq-dashboard-provider-utils";
|
|
6
12
|
|
|
7
13
|
type HeatmapLegendThreshold = {
|
|
8
14
|
label: string;
|
|
@@ -71,7 +77,7 @@ export class MqDashboardQueueSlaHeatmapProvider implements IDashboardWidgetDataP
|
|
|
71
77
|
applyMqDashboardFilters(qb, ctxt.variables ?? {}, { ensureQueueJoin: true });
|
|
72
78
|
qb.andWhere("mqMessage.elapsedMillis IS NOT NULL");
|
|
73
79
|
|
|
74
|
-
const bucketExpr =
|
|
80
|
+
const bucketExpr = buildMqDashboardBucketExpression(qb, bucket, "mqMessage.createdAt");
|
|
75
81
|
const rows = await qb
|
|
76
82
|
.select(bucketExpr, "bucket")
|
|
77
83
|
.addSelect("mqMessageQueue.name", "queueName")
|
|
@@ -93,7 +99,7 @@ export class MqDashboardQueueSlaHeatmapProvider implements IDashboardWidgetDataP
|
|
|
93
99
|
const xCategories = Array.from(
|
|
94
100
|
new Set(
|
|
95
101
|
rows
|
|
96
|
-
.map((row) => row?.bucket
|
|
102
|
+
.map((row) => normalizeMqDashboardBucketValue(row?.bucket))
|
|
97
103
|
.filter((bucketValue) => !!bucketValue)
|
|
98
104
|
)
|
|
99
105
|
).sort();
|
|
@@ -108,7 +114,7 @@ export class MqDashboardQueueSlaHeatmapProvider implements IDashboardWidgetDataP
|
|
|
108
114
|
const pointDetails: HeatmapPointDetail[] = [];
|
|
109
115
|
|
|
110
116
|
for (const row of rows) {
|
|
111
|
-
const bucketValue = row?.bucket
|
|
117
|
+
const bucketValue = normalizeMqDashboardBucketValue(row?.bucket);
|
|
112
118
|
const queueName = `${row?.queueName ?? ""}`.trim();
|
|
113
119
|
const xIndex = xIndexMap.get(bucketValue);
|
|
114
120
|
const yIndex = yIndexMap.get(queueName);
|
package/src/services/datasource-introspection/mssql-datasource-introspection-provider.service.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { DataSource } from "typeorm";
|
|
3
|
+
import {
|
|
4
|
+
DatasourceIntrospectionColumn,
|
|
5
|
+
DatasourceIntrospectionProvider,
|
|
6
|
+
DatasourceIntrospectionTableRef,
|
|
7
|
+
} from "src/interfaces/datasource-introspection-provider.interface";
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class MssqlDatasourceIntrospectionProviderService implements DatasourceIntrospectionProvider {
|
|
11
|
+
readonly type = "mssql";
|
|
12
|
+
|
|
13
|
+
supports(type: string) {
|
|
14
|
+
return type?.toLowerCase() === this.type;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async listTables(dataSource: DataSource): Promise<DatasourceIntrospectionTableRef[]> {
|
|
18
|
+
const rows = await dataSource.query(`
|
|
19
|
+
SELECT
|
|
20
|
+
TABLE_SCHEMA AS tableSchema,
|
|
21
|
+
TABLE_NAME AS tableName
|
|
22
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
23
|
+
WHERE TABLE_TYPE = 'BASE TABLE'
|
|
24
|
+
AND TABLE_SCHEMA NOT IN ('sys', 'INFORMATION_SCHEMA')
|
|
25
|
+
ORDER BY TABLE_SCHEMA, TABLE_NAME
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
return rows.map((row: any) => ({
|
|
29
|
+
tableSchema: row.tableSchema ?? null,
|
|
30
|
+
tableName: row.tableName,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async listColumns(dataSource: DataSource): Promise<DatasourceIntrospectionColumn[]> {
|
|
35
|
+
const rows = await dataSource.query(`
|
|
36
|
+
SELECT
|
|
37
|
+
schema_name(t.schema_id) AS tableSchema,
|
|
38
|
+
t.name AS tableName,
|
|
39
|
+
c.name AS columnName,
|
|
40
|
+
ty.name AS dataType,
|
|
41
|
+
c.is_identity AS isAutoGenerated,
|
|
42
|
+
CASE WHEN c.is_nullable = 1 THEN 'YES' ELSE 'NO' END AS isNullable,
|
|
43
|
+
dc.definition AS defaultValue,
|
|
44
|
+
CASE
|
|
45
|
+
WHEN ty.name IN ('nchar', 'nvarchar') AND c.max_length > 0 THEN c.max_length / 2
|
|
46
|
+
ELSE c.max_length
|
|
47
|
+
END AS characterMaximumLength,
|
|
48
|
+
c.precision AS numericPrecision,
|
|
49
|
+
c.scale AS numericScale,
|
|
50
|
+
c.column_id AS ordinalPosition
|
|
51
|
+
FROM sys.tables t
|
|
52
|
+
INNER JOIN sys.columns c ON c.object_id = t.object_id
|
|
53
|
+
INNER JOIN sys.types ty ON ty.user_type_id = c.user_type_id
|
|
54
|
+
LEFT JOIN sys.default_constraints dc ON dc.object_id = c.default_object_id
|
|
55
|
+
WHERE schema_name(t.schema_id) NOT IN ('sys', 'INFORMATION_SCHEMA')
|
|
56
|
+
ORDER BY schema_name(t.schema_id), t.name, c.column_id
|
|
57
|
+
`);
|
|
58
|
+
|
|
59
|
+
return rows.map((row: any) => ({
|
|
60
|
+
tableSchema: row.tableSchema ?? null,
|
|
61
|
+
tableName: row.tableName,
|
|
62
|
+
columnName: row.columnName,
|
|
63
|
+
dataType: row.dataType,
|
|
64
|
+
isAutoGenerated: Boolean(row.isAutoGenerated),
|
|
65
|
+
isNullable: String(row.isNullable).toUpperCase() === "YES",
|
|
66
|
+
defaultValue: row.defaultValue ?? null,
|
|
67
|
+
characterMaximumLength: this.toNumberOrNull(row.characterMaximumLength),
|
|
68
|
+
numericPrecision: this.toNumberOrNull(row.numericPrecision),
|
|
69
|
+
numericScale: this.toNumberOrNull(row.numericScale),
|
|
70
|
+
ordinalPosition: this.toNumberOrNull(row.ordinalPosition),
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getTableDefinition(dataSource: DataSource, table: DatasourceIntrospectionTableRef): Promise<string | null> {
|
|
75
|
+
const rows = await dataSource.query(
|
|
76
|
+
`
|
|
77
|
+
DECLARE @schemaName NVARCHAR(128) = @0;
|
|
78
|
+
DECLARE @tableName NVARCHAR(128) = @1;
|
|
79
|
+
|
|
80
|
+
WITH target_table AS (
|
|
81
|
+
SELECT t.object_id, s.name AS schema_name, t.name AS table_name
|
|
82
|
+
FROM sys.tables t
|
|
83
|
+
INNER JOIN sys.schemas s ON s.schema_id = t.schema_id
|
|
84
|
+
WHERE t.name = @tableName
|
|
85
|
+
AND (@schemaName IS NULL OR s.name = @schemaName)
|
|
86
|
+
),
|
|
87
|
+
column_lines AS (
|
|
88
|
+
SELECT
|
|
89
|
+
c.column_id,
|
|
90
|
+
' [' + c.name + '] '
|
|
91
|
+
+ UPPER(ty.name)
|
|
92
|
+
+ CASE
|
|
93
|
+
WHEN ty.name IN ('varchar', 'char', 'varbinary', 'binary') THEN
|
|
94
|
+
'(' + CASE WHEN c.max_length = -1 THEN 'MAX' ELSE CAST(c.max_length AS VARCHAR(16)) END + ')'
|
|
95
|
+
WHEN ty.name IN ('nvarchar', 'nchar') THEN
|
|
96
|
+
'(' + CASE WHEN c.max_length = -1 THEN 'MAX' ELSE CAST(c.max_length / 2 AS VARCHAR(16)) END + ')'
|
|
97
|
+
WHEN ty.name IN ('decimal', 'numeric') THEN
|
|
98
|
+
'(' + CAST(c.precision AS VARCHAR(16)) + ',' + CAST(c.scale AS VARCHAR(16)) + ')'
|
|
99
|
+
WHEN ty.name IN ('datetime2', 'datetimeoffset', 'time') THEN
|
|
100
|
+
'(' + CAST(c.scale AS VARCHAR(16)) + ')'
|
|
101
|
+
ELSE
|
|
102
|
+
''
|
|
103
|
+
END
|
|
104
|
+
+ CASE
|
|
105
|
+
WHEN ic.object_id IS NOT NULL THEN
|
|
106
|
+
' IDENTITY(' + CAST(CONVERT(BIGINT, ic.seed_value) AS VARCHAR(32)) + ',' + CAST(CONVERT(BIGINT, ic.increment_value) AS VARCHAR(32)) + ')'
|
|
107
|
+
ELSE
|
|
108
|
+
''
|
|
109
|
+
END
|
|
110
|
+
+ CASE
|
|
111
|
+
WHEN dc.definition IS NOT NULL THEN ' DEFAULT ' + dc.definition
|
|
112
|
+
ELSE ''
|
|
113
|
+
END
|
|
114
|
+
+ CASE
|
|
115
|
+
WHEN c.is_nullable = 1 THEN ' NULL'
|
|
116
|
+
ELSE ' NOT NULL'
|
|
117
|
+
END AS line
|
|
118
|
+
FROM target_table tt
|
|
119
|
+
INNER JOIN sys.columns c ON c.object_id = tt.object_id
|
|
120
|
+
INNER JOIN sys.types ty ON ty.user_type_id = c.user_type_id
|
|
121
|
+
LEFT JOIN sys.identity_columns ic
|
|
122
|
+
ON ic.object_id = c.object_id
|
|
123
|
+
AND ic.column_id = c.column_id
|
|
124
|
+
LEFT JOIN sys.default_constraints dc ON dc.object_id = c.default_object_id
|
|
125
|
+
),
|
|
126
|
+
constraint_lines AS (
|
|
127
|
+
SELECT
|
|
128
|
+
kc.name AS constraint_name,
|
|
129
|
+
' CONSTRAINT [' + kc.name + '] PRIMARY KEY ('
|
|
130
|
+
+ STUFF((
|
|
131
|
+
SELECT ', [' + c.name + ']'
|
|
132
|
+
FROM sys.index_columns ic
|
|
133
|
+
INNER JOIN sys.columns c
|
|
134
|
+
ON c.object_id = ic.object_id
|
|
135
|
+
AND c.column_id = ic.column_id
|
|
136
|
+
WHERE ic.object_id = kc.parent_object_id
|
|
137
|
+
AND ic.index_id = kc.unique_index_id
|
|
138
|
+
ORDER BY ic.key_ordinal
|
|
139
|
+
FOR XML PATH(''), TYPE
|
|
140
|
+
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
|
|
141
|
+
+ ')' AS line
|
|
142
|
+
FROM target_table tt
|
|
143
|
+
INNER JOIN sys.key_constraints kc ON kc.parent_object_id = tt.object_id
|
|
144
|
+
WHERE kc.type = 'PK'
|
|
145
|
+
)
|
|
146
|
+
SELECT
|
|
147
|
+
'CREATE TABLE [' + tt.schema_name + '].[' + tt.table_name + '] (' + CHAR(10)
|
|
148
|
+
+ ISNULL((
|
|
149
|
+
SELECT STUFF((
|
|
150
|
+
SELECT ',' + CHAR(10) + cl.line
|
|
151
|
+
FROM column_lines cl
|
|
152
|
+
ORDER BY cl.column_id
|
|
153
|
+
FOR XML PATH(''), TYPE
|
|
154
|
+
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
|
|
155
|
+
), '')
|
|
156
|
+
+ ISNULL((
|
|
157
|
+
SELECT CASE
|
|
158
|
+
WHEN EXISTS (SELECT 1 FROM constraint_lines)
|
|
159
|
+
THEN ',' + CHAR(10) + STUFF((
|
|
160
|
+
SELECT ',' + CHAR(10) + cn.line
|
|
161
|
+
FROM constraint_lines cn
|
|
162
|
+
ORDER BY cn.constraint_name
|
|
163
|
+
FOR XML PATH(''), TYPE
|
|
164
|
+
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
|
|
165
|
+
ELSE ''
|
|
166
|
+
END
|
|
167
|
+
), '')
|
|
168
|
+
+ CHAR(10) + ');' AS ddl
|
|
169
|
+
FROM target_table tt
|
|
170
|
+
`,
|
|
171
|
+
[table.tableSchema, table.tableName],
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return rows?.[0]?.ddl ?? null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private toNumberOrNull(value: unknown) {
|
|
178
|
+
if (value === null || value === undefined || value === "") {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const parsed = Number(value);
|
|
183
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
184
|
+
}
|
|
185
|
+
}
|
package/src/services/datasource-introspection/mysql-datasource-introspection-provider.service.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { DataSource } from "typeorm";
|
|
3
|
+
import {
|
|
4
|
+
DatasourceIntrospectionColumn,
|
|
5
|
+
DatasourceIntrospectionProvider,
|
|
6
|
+
DatasourceIntrospectionTableRef,
|
|
7
|
+
} from "src/interfaces/datasource-introspection-provider.interface";
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class MysqlDatasourceIntrospectionProviderService implements DatasourceIntrospectionProvider {
|
|
11
|
+
readonly type = "mysql";
|
|
12
|
+
|
|
13
|
+
supports(type: string) {
|
|
14
|
+
return type?.toLowerCase() === this.type;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async listTables(dataSource: DataSource): Promise<DatasourceIntrospectionTableRef[]> {
|
|
18
|
+
const rows = await dataSource.query(`
|
|
19
|
+
SELECT
|
|
20
|
+
TABLE_SCHEMA AS tableSchema,
|
|
21
|
+
TABLE_NAME AS tableName
|
|
22
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
23
|
+
WHERE TABLE_TYPE = 'BASE TABLE'
|
|
24
|
+
AND TABLE_SCHEMA = DATABASE()
|
|
25
|
+
ORDER BY TABLE_SCHEMA, TABLE_NAME
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
return rows.map((row: any) => ({
|
|
29
|
+
tableSchema: row.tableSchema ?? null,
|
|
30
|
+
tableName: row.tableName,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async listColumns(dataSource: DataSource): Promise<DatasourceIntrospectionColumn[]> {
|
|
35
|
+
const rows = await dataSource.query(`
|
|
36
|
+
SELECT
|
|
37
|
+
TABLE_SCHEMA AS tableSchema,
|
|
38
|
+
TABLE_NAME AS tableName,
|
|
39
|
+
COLUMN_NAME AS columnName,
|
|
40
|
+
DATA_TYPE AS dataType,
|
|
41
|
+
EXTRA AS extra,
|
|
42
|
+
IS_NULLABLE AS isNullable,
|
|
43
|
+
COLUMN_DEFAULT AS defaultValue,
|
|
44
|
+
CHARACTER_MAXIMUM_LENGTH AS characterMaximumLength,
|
|
45
|
+
NUMERIC_PRECISION AS numericPrecision,
|
|
46
|
+
NUMERIC_SCALE AS numericScale,
|
|
47
|
+
ORDINAL_POSITION AS ordinalPosition
|
|
48
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
49
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
50
|
+
ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
return rows.map((row: any) => ({
|
|
54
|
+
tableSchema: row.tableSchema ?? null,
|
|
55
|
+
tableName: row.tableName,
|
|
56
|
+
columnName: row.columnName,
|
|
57
|
+
dataType: row.dataType,
|
|
58
|
+
isAutoGenerated: String(row.extra ?? "").toLowerCase().includes("auto_increment"),
|
|
59
|
+
isNullable: String(row.isNullable).toUpperCase() === "YES",
|
|
60
|
+
defaultValue: row.defaultValue ?? null,
|
|
61
|
+
characterMaximumLength: this.toNumberOrNull(row.characterMaximumLength),
|
|
62
|
+
numericPrecision: this.toNumberOrNull(row.numericPrecision),
|
|
63
|
+
numericScale: this.toNumberOrNull(row.numericScale),
|
|
64
|
+
ordinalPosition: this.toNumberOrNull(row.ordinalPosition),
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getTableDefinition(dataSource: DataSource, table: DatasourceIntrospectionTableRef): Promise<string | null> {
|
|
69
|
+
const schemaName = table.tableSchema ?? dataSource.options.database;
|
|
70
|
+
const rows = await dataSource.query(
|
|
71
|
+
`SHOW CREATE TABLE \`${schemaName}\`.\`${table.tableName}\``,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const firstRow = rows?.[0];
|
|
75
|
+
if (!firstRow || typeof firstRow !== "object") {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const ddlKey = Object.keys(firstRow).find((key) => key.toLowerCase().startsWith("create table"));
|
|
80
|
+
if (!ddlKey) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return String(firstRow[ddlKey] ?? "").trim() || null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private toNumberOrNull(value: unknown) {
|
|
88
|
+
if (value === null || value === undefined || value === "") {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const parsed = Number(value);
|
|
93
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/services/datasource-introspection/postgres-datasource-introspection-provider.service.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { DataSource } from "typeorm";
|
|
3
|
+
import {
|
|
4
|
+
DatasourceIntrospectionColumn,
|
|
5
|
+
DatasourceIntrospectionProvider,
|
|
6
|
+
DatasourceIntrospectionTableRef,
|
|
7
|
+
} from "src/interfaces/datasource-introspection-provider.interface";
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class PostgresDatasourceIntrospectionProviderService implements DatasourceIntrospectionProvider {
|
|
11
|
+
readonly type = "postgres";
|
|
12
|
+
|
|
13
|
+
supports(type: string) {
|
|
14
|
+
return type?.toLowerCase() === this.type;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async listTables(dataSource: DataSource): Promise<DatasourceIntrospectionTableRef[]> {
|
|
18
|
+
const rows = await dataSource.query(`
|
|
19
|
+
SELECT
|
|
20
|
+
TABLE_SCHEMA AS "tableSchema",
|
|
21
|
+
TABLE_NAME AS "tableName"
|
|
22
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
23
|
+
WHERE TABLE_TYPE = 'BASE TABLE'
|
|
24
|
+
AND TABLE_SCHEMA NOT IN ('pg_catalog', 'information_schema')
|
|
25
|
+
ORDER BY TABLE_SCHEMA, TABLE_NAME
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
return rows.map((row: any) => ({
|
|
29
|
+
tableSchema: row.tableSchema ?? null,
|
|
30
|
+
tableName: row.tableName,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async listColumns(dataSource: DataSource): Promise<DatasourceIntrospectionColumn[]> {
|
|
35
|
+
const rows = await dataSource.query(`
|
|
36
|
+
SELECT
|
|
37
|
+
TABLE_SCHEMA AS "tableSchema",
|
|
38
|
+
TABLE_NAME AS "tableName",
|
|
39
|
+
COLUMN_NAME AS "columnName",
|
|
40
|
+
DATA_TYPE AS "dataType",
|
|
41
|
+
CASE
|
|
42
|
+
WHEN is_identity = 'YES' THEN true
|
|
43
|
+
WHEN COLUMN_DEFAULT LIKE 'nextval(%' THEN true
|
|
44
|
+
ELSE false
|
|
45
|
+
END AS "isAutoGenerated",
|
|
46
|
+
IS_NULLABLE AS "isNullable",
|
|
47
|
+
COLUMN_DEFAULT AS "defaultValue",
|
|
48
|
+
CHARACTER_MAXIMUM_LENGTH AS "characterMaximumLength",
|
|
49
|
+
NUMERIC_PRECISION AS "numericPrecision",
|
|
50
|
+
NUMERIC_SCALE AS "numericScale",
|
|
51
|
+
ORDINAL_POSITION AS "ordinalPosition"
|
|
52
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
53
|
+
WHERE TABLE_SCHEMA NOT IN ('pg_catalog', 'information_schema')
|
|
54
|
+
ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION
|
|
55
|
+
`);
|
|
56
|
+
|
|
57
|
+
return rows.map((row: any) => ({
|
|
58
|
+
tableSchema: row.tableSchema ?? null,
|
|
59
|
+
tableName: row.tableName,
|
|
60
|
+
columnName: row.columnName,
|
|
61
|
+
dataType: row.dataType,
|
|
62
|
+
isAutoGenerated: Boolean(row.isAutoGenerated),
|
|
63
|
+
isNullable: String(row.isNullable).toUpperCase() === "YES",
|
|
64
|
+
defaultValue: row.defaultValue ?? null,
|
|
65
|
+
characterMaximumLength: this.toNumberOrNull(row.characterMaximumLength),
|
|
66
|
+
numericPrecision: this.toNumberOrNull(row.numericPrecision),
|
|
67
|
+
numericScale: this.toNumberOrNull(row.numericScale),
|
|
68
|
+
ordinalPosition: this.toNumberOrNull(row.ordinalPosition),
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getTableDefinition(dataSource: DataSource, table: DatasourceIntrospectionTableRef): Promise<string | null> {
|
|
73
|
+
const rows = await dataSource.query(
|
|
74
|
+
`
|
|
75
|
+
WITH target_table AS (
|
|
76
|
+
SELECT c.oid, n.nspname AS schema_name, c.relname AS table_name
|
|
77
|
+
FROM pg_class c
|
|
78
|
+
INNER JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
79
|
+
WHERE c.relkind = 'r'
|
|
80
|
+
AND c.relname = $1
|
|
81
|
+
AND ($2::text IS NULL OR n.nspname = $2)
|
|
82
|
+
LIMIT 1
|
|
83
|
+
),
|
|
84
|
+
column_lines AS (
|
|
85
|
+
SELECT
|
|
86
|
+
a.attnum,
|
|
87
|
+
format(
|
|
88
|
+
' %I %s%s%s%s',
|
|
89
|
+
a.attname,
|
|
90
|
+
format_type(a.atttypid, a.atttypmod),
|
|
91
|
+
CASE
|
|
92
|
+
WHEN a.attidentity = 'a' THEN ' GENERATED ALWAYS AS IDENTITY'
|
|
93
|
+
WHEN a.attidentity = 'd' THEN ' GENERATED BY DEFAULT AS IDENTITY'
|
|
94
|
+
ELSE ''
|
|
95
|
+
END,
|
|
96
|
+
CASE
|
|
97
|
+
WHEN ad.adbin IS NOT NULL THEN ' DEFAULT ' || pg_get_expr(ad.adbin, ad.adrelid)
|
|
98
|
+
ELSE ''
|
|
99
|
+
END,
|
|
100
|
+
CASE
|
|
101
|
+
WHEN a.attnotnull THEN ' NOT NULL'
|
|
102
|
+
ELSE ''
|
|
103
|
+
END
|
|
104
|
+
) AS line
|
|
105
|
+
FROM target_table tt
|
|
106
|
+
INNER JOIN pg_attribute a ON a.attrelid = tt.oid
|
|
107
|
+
LEFT JOIN pg_attrdef ad
|
|
108
|
+
ON ad.adrelid = a.attrelid
|
|
109
|
+
AND ad.adnum = a.attnum
|
|
110
|
+
WHERE a.attnum > 0
|
|
111
|
+
AND NOT a.attisdropped
|
|
112
|
+
),
|
|
113
|
+
constraint_lines AS (
|
|
114
|
+
SELECT
|
|
115
|
+
c.conname,
|
|
116
|
+
format(' CONSTRAINT %I %s', c.conname, pg_get_constraintdef(c.oid, true)) AS line
|
|
117
|
+
FROM target_table tt
|
|
118
|
+
INNER JOIN pg_constraint c ON c.conrelid = tt.oid
|
|
119
|
+
WHERE c.contype IN ('p', 'u', 'f', 'c')
|
|
120
|
+
)
|
|
121
|
+
SELECT format(
|
|
122
|
+
'CREATE TABLE %I.%I (\n%s%s\n);',
|
|
123
|
+
tt.schema_name,
|
|
124
|
+
tt.table_name,
|
|
125
|
+
COALESCE(
|
|
126
|
+
(
|
|
127
|
+
SELECT string_agg(line, ',\n' ORDER BY attnum)
|
|
128
|
+
FROM column_lines
|
|
129
|
+
),
|
|
130
|
+
''
|
|
131
|
+
),
|
|
132
|
+
COALESCE(
|
|
133
|
+
(
|
|
134
|
+
SELECT CASE
|
|
135
|
+
WHEN EXISTS (SELECT 1 FROM constraint_lines)
|
|
136
|
+
THEN ',\n' || string_agg(line, ',\n' ORDER BY conname)
|
|
137
|
+
ELSE ''
|
|
138
|
+
END
|
|
139
|
+
FROM constraint_lines
|
|
140
|
+
),
|
|
141
|
+
''
|
|
142
|
+
)
|
|
143
|
+
) AS ddl
|
|
144
|
+
FROM target_table tt
|
|
145
|
+
`,
|
|
146
|
+
[table.tableName, table.tableSchema],
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return rows?.[0]?.ddl ?? null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private toNumberOrNull(value: unknown) {
|
|
153
|
+
if (value === null || value === undefined || value === "") {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const parsed = Number(value);
|
|
158
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
159
|
+
}
|
|
160
|
+
}
|