-
Notifications
You must be signed in to change notification settings - Fork 0
/
AbstractSqlSchemaHarvester.ts
193 lines (169 loc) · 6.37 KB
/
AbstractSqlSchemaHarvester.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import { Level } from '@tverstraten/log-annotations'
import { AbstractSingularBuilder } from '../../../runtime/AbstractSingularBuilder'
import { Artifact } from '../../../system/Artifact'
import { System } from '../../../system/System'
import { SystemComponent } from '../../../system/SystemComponent'
import { SystemComponentArtifact } from '../../../system/SystemComponentArtifact'
import { ValueType } from '../../../system/ValueType'
import { Column } from '../Column'
import { Constraint } from '../Constraint'
import { Domain } from '../Domain'
import { ForeignKey } from '../ForeignKey'
import { PrimaryKey } from '../PrimaryKey'
import { RelationalDatabase } from '../RelationalDatabase'
import { Schema } from '../Schema'
import { Table } from '../Table'
import { View } from '../View'
export abstract class AbstractSqlSchemaHarvester extends AbstractSingularBuilder {
protected schemaRegex: RegExp
protected tableRegex: RegExp
protected columnRegex: RegExp
private regexSet = false
// eslint-disable-next-line max-lines-per-function
constructor(description: string, configurationValues?: { [key: string]: any }) {
super(
description,
{
serverUrl: {
name: 'Server URL',
required: true,
description: 'The url to the server to connect to',
valueType: ValueType.string,
},
database: {
name: 'Database Name',
required: true,
description: 'The name of the database in the server to connect to',
valueType: ValueType.string,
},
userName: {
name: 'User Name',
required: true,
description: 'The user name to present to sql server for authentication',
valueType: ValueType.string,
},
password: {
name: 'User Password',
required: true,
description: 'The character sequence that validates the user name',
valueType: ValueType.string,
},
queryText: {
name: 'Query Text',
required: true,
description: 'The sql text to execute against the database, each row of which will result in a call to harvestRow',
valueType: ValueType.string,
defaultValue: '.*',
},
schemaPattern: {
name: 'Schema pattern',
required: true,
description: 'A regex used against the schema name to determine if it should be included in the harvesting',
valueType: ValueType.string,
defaultValue: '.*',
},
tablePattern: {
name: 'Table pattern',
required: true,
description: 'A regex used against the non-qualified table/view name to determine if it should be included in the harvesting',
valueType: ValueType.string,
defaultValue: '.*',
},
columnPattern: {
name: 'Column pattern',
required: true,
description: 'A regex used against the non-qualified column name to determine if it should be included in the harvesting',
valueType: ValueType.string,
defaultValue: '.*',
},
},
configurationValues
)
this.schemaRegex = new RegExp('.*')
this.tableRegex = new RegExp('.*')
this.columnRegex = new RegExp('.*')
}
async _doesConfigurationWork(): Promise<boolean> {
try {
this.logger.isLevelEnabled(Level.debug) ? this.logger.debug(`_doesConfigurationWork(${this.name}) enter`) : ''
await this.connect()
// make sure the regex's can compile
this.ensureRegexes()
return true
} catch (problem) {
this.logger.error(`_doesConfigurationWork(${this.name}) failed ${problem}`)
return false
} finally {
await this.disconnect()
this.logger.isLevelEnabled(Level.debug) ? this.logger.debug(`_doesConfigurationWork(${this.name}) exit`) : ''
}
}
protected ensureRegexes(): void {
if (!this.regexSet) {
// remember to look for flags
this.schemaRegex = this.regexFromConfigurationString(this.getConfiguredValue('schemaPattern'))
this.tableRegex = this.regexFromConfigurationString(this.getConfiguredValue('tablePattern'))
this.columnRegex = this.regexFromConfigurationString(this.getConfiguredValue('columnPattern'))
this.regexSet = true
}
}
protected validSchema(schema: Schema): boolean {
return this.schemaRegex.test(schema.name)
}
protected validTable(table: Table): boolean {
return this.schemaRegex.test(table.nameSpace) && this.tableRegex.test(table.name)
}
protected validView(view: View): boolean {
return this.schemaRegex.test(view.nameSpace) && this.tableRegex.test(view.name)
}
protected validColumn(column: Column): boolean {
return this.schemaRegex.test(column.schemaName) && this.tableRegex.test(column.tableName) && this.columnRegex.test(column.name)
}
protected validDomain(domain: Domain): boolean {
return this.schemaRegex.test(domain.nameSpace)
}
protected validForeignKey(fk: ForeignKey): boolean {
return (
this.schemaRegex.test(fk.schemaName) &&
this.schemaRegex.test(fk.targetSchemaName) &&
this.tableRegex.test(fk.sourceTableName) &&
this.tableRegex.test(fk.targetTableName) &&
this.columnRegex.test(fk.sourceColumnName)
)
}
protected validPrimaryKey(pk: PrimaryKey): boolean {
return this.schemaRegex.test(pk.schemaName) && this.tableRegex.test(pk.tableName) && this.columnRegex.test(pk.columnName)
}
protected validConstraint(constraint: Constraint): boolean {
return this.schemaRegex.test(constraint.nameSpace) && this.tableRegex.test(constraint.name)
}
async buildInternal(system: System, __component: SystemComponent): Promise<Artifact[]> {
const results = [] as Artifact[]
try {
this.logger.isLevelEnabled(Level.debug) ? this.logger.debug(`buildInternal(${system.name}) enter`) : ''
this.ensureRegexes()
const server = this.getConfiguredValue('serverUrl')
const database = this.getConfiguredValue('database')
const fullDbName = System.fullConstantCase(server, database)
let db = system.descendants[fullDbName] as RelationalDatabase
if (db == null) {
db = new RelationalDatabase(server, database, '')
system.addChild(db)
results.push(new SystemComponentArtifact(db))
}
await this.connect()
const queryResults = await this.performQuery(system)
results.concat(queryResults)
this.logger.isLevelEnabled(Level.debug) ? this.logger.debug(`buildInternal(${system.name}) exit`) : ''
return results
} catch (problem) {
this.logger.error(`buildInternal(${system.name}) failed ${problem}`)
throw problem
} finally {
await this.disconnect()
}
}
protected abstract connect(): void
protected abstract performQuery(system: System): Promise<Artifact[]>
protected abstract disconnect(): void
}