1
- import { ColumnType , Query , QueryRequest , Value } from "../grpc/models/rdb/relational" ;
1
+ /* TODO:
2
+ 1. Move this to @topcoder-framework
3
+ 2. Cleanup the exported interfaces
4
+ 3. Make "Client" a constructor parameter that implements a "Client" interface
5
+ 4 "ExecuteSqlQuery" should return a Promise<T> where T is the type of the result for "read" queries, but should return "number" for "write" queries indicating either
6
+ a) the number of rows affected or
7
+ b) the ID of the row insertede
8
+ */
9
+
10
+ import { ColumnType , Operator , Query , QueryRequest , Value } from "../grpc/models/rdb/relational" ;
2
11
3
12
import { relationalClient } from "../grpc/client/relational" ;
13
+ import { TableColumns , TableColumn } from "./TableColumn" ;
4
14
5
- export interface TableOptions {
15
+ export type Schema = {
6
16
dbSchema : string ;
7
17
tableName : string ;
8
- columns : {
9
- name : string ;
10
- type : ColumnType ;
11
- } [ ] ;
12
18
idColumn ?: string ;
13
19
idSequence ?: string ;
14
20
idTable ?: string ;
15
- }
21
+ columns : TableColumns ;
22
+ } ;
16
23
17
- interface SqlQuery {
24
+ interface ExecuteSqlQuery {
18
25
exec ( ) : Promise < unknown > ;
19
26
}
20
27
21
- export interface SelectQuery extends SqlQuery {
22
- query ( columns : string [ ] ) : SelectQuery ;
23
- where ( ) : SelectQuery ;
24
- join ( ) : SelectQuery ;
28
+ type JoinAndWhereClause = JoinClause & WhereClause & ExecuteSqlQuery ;
29
+
30
+ export interface SelectQuery {
31
+ select ( columns : TableColumn [ ] ) : JoinAndWhereClause & LimitClause & OffsetClause ;
32
+ }
33
+
34
+ export interface JoinClause {
35
+ join ( ) : JoinAndWhereClause ;
36
+ }
37
+
38
+ export interface WhereClause {
39
+ where ( whereCriteria : {
40
+ key : string ,
41
+ operator : Operator ,
42
+ value : Value
43
+ } ) : JoinAndWhereClause & LimitClause & OffsetClause ;
44
+ }
45
+
46
+ export interface LimitClause {
47
+ limit ( limit : number ) : OffsetClause & ExecuteSqlQuery ;
25
48
}
26
49
27
- export interface InsertQuery extends SqlQuery {
28
- insert ( input : unknown ) : InsertQuery ;
50
+ export interface OffsetClause {
51
+ offset ( offset : number ) : ExecuteSqlQuery ;
29
52
}
30
53
31
- export interface UpdateQuery extends SqlQuery {
32
- update ( input : Record < string , unknown > ) : UpdateQuery ;
54
+ export interface InsertQuery < CreateInput > {
55
+ insert ( input : CreateInput ) : ExecuteSqlQuery ;
33
56
}
34
57
35
- export interface DeleteQuery extends SqlQuery {
36
- delete ( ) : DeleteQuery ;
58
+ export interface UpdateQuery < UpdateInput > {
59
+ update ( lookupCriteria : { [ key : string ] : unknown } , input : UpdateInput ) : ExecuteSqlQuery ;
37
60
}
38
61
39
- export class QueryRunner < T , CreateInput extends object >
40
- implements SelectQuery , InsertQuery , UpdateQuery , DeleteQuery
62
+ export interface DeleteQuery {
63
+ delete ( ) : ExecuteSqlQuery ;
64
+ }
65
+
66
+ export class QueryRunner < T , CreateInput extends { [ key : string ] : unknown } , UpdateInput extends { [ key : string ] : unknown } >
67
+ implements
68
+ SelectQuery , JoinClause , WhereClause , LimitClause , OffsetClause ,
69
+ InsertQuery < CreateInput > ,
70
+ UpdateQuery < UpdateInput > ,
71
+ DeleteQuery ,
72
+ ExecuteSqlQuery
41
73
{
42
74
#query: Query | null = null ;
43
75
44
- // TODO: Optimize this as each instantiation of this class will create a new object with the same keys and values.
45
- #attributeKeyTypeMap: Record < string , ColumnType > = this . options . columns . reduce (
46
- ( acc , cur ) => ( {
47
- ...acc ,
48
- [ cur . name . replace ( / ( [ - _ ] [ a - z ] ) / gi, ( $1 ) => {
49
- return $1 . toUpperCase ( ) . replace ( "-" , "" ) . replace ( "_" , "" ) ;
50
- } ) ] : cur . type ,
51
- } ) ,
52
- { }
53
- ) ;
54
-
55
- constructor ( protected options : TableOptions ) { }
76
+ constructor ( private schema : Schema ) { }
56
77
57
- query ( columns : string [ ] ) : SelectQuery {
78
+ select ( columns : TableColumn [ ] ) : JoinAndWhereClause & LimitClause & OffsetClause {
58
79
this . #query = {
59
80
query : {
60
81
$case : "select" ,
61
82
select : {
62
- schema : this . options . dbSchema ,
63
- table : this . options . tableName ,
83
+ schema : this . schema . dbSchema ,
84
+ table : this . schema . tableName ,
64
85
column : columns . map ( ( col ) => ( {
65
- tableName : this . options . tableName ,
66
- name : col ,
67
- type : this . #attributeKeyTypeMap [ col ] ,
86
+ tableName : this . schema . tableName ,
87
+ name : col . name ,
88
+ type : col . type
68
89
} ) ) ,
69
90
where : [ ] ,
70
91
join : [ ] ,
@@ -78,22 +99,48 @@ export class QueryRunner<T, CreateInput extends object>
78
99
return this ;
79
100
}
80
101
81
- where ( ) : SelectQuery {
102
+ // TODO: use "convenience" methods from lib-util to build the clause
103
+ where ( whereCriteria : {
104
+ key : string ,
105
+ operator : Operator ,
106
+ value : Value
107
+ } ) : JoinAndWhereClause & LimitClause & OffsetClause {
108
+ if ( this . #query?. query ?. $case != "select" ) {
109
+ throw new Error ( "Cannot set where clause on a non-select query" ) ;
110
+ }
111
+
112
+ this . #query. query . select . where . push ( whereCriteria ) ;
113
+ return this ;
114
+ }
115
+
116
+ join ( ) : JoinAndWhereClause {
117
+ // TODO: Implement join clause
118
+ return this ;
119
+ }
120
+
121
+ limit ( limit : number ) : OffsetClause & ExecuteSqlQuery {
122
+ if ( this . #query?. query ?. $case != "select" ) {
123
+ throw new Error ( "Cannot set limit on a non-select query" ) ;
124
+ }
125
+ this . #query. query . select . limit = limit ;
82
126
return this ;
83
127
}
84
128
85
- join ( ) : SelectQuery {
129
+ offset ( offset : number ) : ExecuteSqlQuery {
130
+ if ( this . #query?. query ?. $case != "select" ) {
131
+ throw new Error ( "Cannot set offset on a non-select query" ) ;
132
+ }
133
+ this . #query. query . select . offset = offset ;
86
134
return this ;
87
135
}
88
136
89
- insert ( input : CreateInput ) : InsertQuery {
90
- console . log ( "Create Input" , input ) ;
137
+ insert ( input : CreateInput ) : ExecuteSqlQuery {
91
138
this . #query = {
92
139
query : {
93
140
$case : "insert" ,
94
141
insert : {
95
- schema : this . options . dbSchema ,
96
- table : this . options . tableName ,
142
+ schema : this . schema . dbSchema ,
143
+ table : this . schema . tableName ,
97
144
columnValue : [
98
145
{
99
146
column : "create_date" ,
@@ -116,11 +163,11 @@ export class QueryRunner<T, CreateInput extends object>
116
163
...Object . entries ( input )
117
164
. filter ( ( [ _key , value ] ) => value !== undefined )
118
165
. map ( ( [ key , value ] ) => ( {
119
- column : key . replace ( / ( [ A - Z ] ) / g , "_$1" ) . toLowerCase ( ) ,
166
+ column : this . schema . columns [ key ] . name ,
120
167
value : this . toValue ( key , value ) ,
121
168
} ) ) ,
122
169
] ,
123
- idTable : this . options . tableName ,
170
+ idTable : this . schema . tableName ,
124
171
idColumn : "project_phase_id" ,
125
172
idSequence : "project_phase_id_seq" ,
126
173
} ,
@@ -130,18 +177,15 @@ export class QueryRunner<T, CreateInput extends object>
130
177
return this ;
131
178
}
132
179
133
- update ( input : Record < string , unknown > ) : UpdateQuery {
180
+ update ( input : Record < string , unknown > ) : ExecuteSqlQuery {
134
181
return this ;
135
182
}
136
183
137
- delete ( ) : DeleteQuery {
184
+ delete ( ) : ExecuteSqlQuery {
138
185
return this ;
139
186
}
140
187
141
188
async exec ( ) : Promise < number | T [ ] > {
142
- console . log ( "Execute query" ) ;
143
- console . log ( "Insert query" , JSON . stringify ( this . #query) ) ;
144
-
145
189
if ( ! this . #query) {
146
190
throw new Error ( "No query to execute" ) ;
147
191
}
@@ -182,7 +226,8 @@ export class QueryRunner<T, CreateInput extends object>
182
226
}
183
227
184
228
private toValue ( key : string , value : unknown ) : Value {
185
- const dataType : ColumnType = this . #attributeKeyTypeMap[ key ] ;
229
+ const dataType : ColumnType = this . schema . columns [ key ] . type ;
230
+
186
231
if ( dataType == null ) {
187
232
throw new Error ( `Unknown column ${ key } ` ) ;
188
233
}
0 commit comments