Skip to content

Commit 3258cde

Browse files
committed
Merge branch 'main' into feature/legacy-challenge
2 parents 85320c2 + ca79250 commit 3258cde

File tree

4 files changed

+306
-2
lines changed

4 files changed

+306
-2
lines changed

src/common/QueryRunner.ts

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
const { GRPC_RDB_SERVER_HOST, GRPC_RDB_SERVER_PORT } = process.env;
2+
3+
import {
4+
ColumnType,
5+
Operator,
6+
Query,
7+
QueryRequest,
8+
RelationalClient,
9+
Value,
10+
} from "@topcoder-framework/client-relational";
11+
import { TableColumn, TableColumns } from "./TableColumn";
12+
13+
export type Schema = {
14+
dbSchema: string;
15+
tableName: string;
16+
idColumn?: string;
17+
idSequence?: string;
18+
idTable?: string;
19+
columns: TableColumns;
20+
};
21+
22+
interface ExecuteSqlQuery {
23+
exec(): Promise<unknown>;
24+
}
25+
26+
type JoinAndWhereClause = JoinClause & WhereClause & ExecuteSqlQuery;
27+
type JoinWhereLimitAndOffset = JoinAndWhereClause & LimitClause & OffsetClause;
28+
29+
export interface SelectQuery {
30+
select(columns: TableColumn[]): JoinWhereLimitAndOffset;
31+
}
32+
33+
export interface JoinClause {
34+
join(): JoinAndWhereClause;
35+
}
36+
37+
export interface WhereClause {
38+
where(whereCriteria: { key: string; operator: Operator; value: Value }): JoinWhereLimitAndOffset;
39+
}
40+
41+
export interface LimitClause {
42+
limit(limit: number): OffsetClause & ExecuteSqlQuery;
43+
}
44+
45+
export interface OffsetClause {
46+
offset(offset: number): ExecuteSqlQuery;
47+
}
48+
49+
export interface InsertQuery<CreateInput> {
50+
insert(input: CreateInput): ExecuteSqlQuery;
51+
}
52+
53+
export interface UpdateQuery<UpdateInput> {
54+
update(lookupCriteria: { [key: string]: unknown }, input: UpdateInput): ExecuteSqlQuery;
55+
}
56+
57+
export interface DeleteQuery {
58+
delete(): ExecuteSqlQuery;
59+
}
60+
61+
export class QueryRunner<
62+
T,
63+
CreateInput extends { [key: string]: unknown },
64+
UpdateInput extends { [key: string]: unknown }
65+
> implements
66+
SelectQuery,
67+
JoinClause,
68+
WhereClause,
69+
LimitClause,
70+
OffsetClause,
71+
InsertQuery<CreateInput>,
72+
UpdateQuery<UpdateInput>,
73+
DeleteQuery,
74+
ExecuteSqlQuery
75+
{
76+
#query: Query | null = null;
77+
#client: RelationalClient;
78+
79+
constructor(private schema: Schema) {
80+
console.log("Connecting to GRPC server at", GRPC_RDB_SERVER_HOST, GRPC_RDB_SERVER_PORT, "...");
81+
this.#client = new RelationalClient(GRPC_RDB_SERVER_HOST!, parseInt(GRPC_RDB_SERVER_PORT!));
82+
}
83+
84+
select(columns: TableColumn[]): JoinWhereLimitAndOffset {
85+
this.#query = {
86+
query: {
87+
$case: "select",
88+
select: {
89+
schema: this.schema.dbSchema,
90+
table: this.schema.tableName,
91+
column: columns.map((col) => ({
92+
tableName: this.schema.tableName,
93+
name: col.name,
94+
type: col.type,
95+
})),
96+
where: [],
97+
join: [],
98+
groupBy: [],
99+
orderBy: [],
100+
limit: 100,
101+
offset: 0,
102+
},
103+
},
104+
};
105+
106+
console.log("Query", JSON.stringify(this.#query, null, 2));
107+
return this;
108+
}
109+
110+
// TODO: use "convenience" methods from lib-util to build the clause
111+
where(whereCriteria: { key: string; operator: Operator; value: Value }): JoinWhereLimitAndOffset {
112+
if (this.#query?.query?.$case != "select") {
113+
throw new Error("Cannot set where clause on a non-select query");
114+
}
115+
116+
this.#query.query.select.where.push(whereCriteria);
117+
return this;
118+
}
119+
120+
join(): JoinAndWhereClause {
121+
// TODO: Implement join clause
122+
return this;
123+
}
124+
125+
limit(limit: number): OffsetClause & ExecuteSqlQuery {
126+
if (this.#query?.query?.$case != "select") {
127+
throw new Error("Cannot set limit on a non-select query");
128+
}
129+
this.#query.query.select.limit = limit;
130+
return this;
131+
}
132+
133+
offset(offset: number): ExecuteSqlQuery {
134+
if (this.#query?.query?.$case != "select") {
135+
throw new Error("Cannot set offset on a non-select query");
136+
}
137+
this.#query.query.select.offset = offset;
138+
return this;
139+
}
140+
141+
insert(input: CreateInput): ExecuteSqlQuery {
142+
this.#query = {
143+
query: {
144+
$case: "insert",
145+
insert: {
146+
schema: this.schema.dbSchema,
147+
table: this.schema.tableName,
148+
columnValue: [
149+
{
150+
column: "create_date",
151+
value: {
152+
value: {
153+
$case: "datetimeValue",
154+
datetimeValue: "CURRENT",
155+
},
156+
},
157+
},
158+
{
159+
column: "modify_date",
160+
value: {
161+
value: {
162+
$case: "datetimeValue",
163+
datetimeValue: "CURRENT",
164+
},
165+
},
166+
},
167+
...Object.entries(input)
168+
.filter(([_key, value]) => value !== undefined)
169+
.map(([key, value]) => ({
170+
column: this.schema.columns[key].name,
171+
value: this.toValue(key, value),
172+
})),
173+
],
174+
idTable: this.schema.tableName,
175+
idColumn: this.schema.idColumn ?? undefined,
176+
idSequence: this.schema.idSequence ?? undefined,
177+
},
178+
},
179+
};
180+
181+
return this;
182+
}
183+
184+
update(input: Record<string, unknown>): ExecuteSqlQuery {
185+
return this;
186+
}
187+
188+
delete(): ExecuteSqlQuery {
189+
return this;
190+
}
191+
192+
async exec(): Promise<number | T[]> {
193+
if (!this.#query) {
194+
throw new Error("No query to execute");
195+
}
196+
197+
const queryRequest: QueryRequest = {
198+
query: this.#query,
199+
};
200+
201+
const queryResponse = await this.#client.query(queryRequest);
202+
203+
switch (this.#query.query?.$case) {
204+
case "select":
205+
if (queryResponse.result?.$case != "selectResult") {
206+
throw new Error("Unexpected result type");
207+
}
208+
return queryResponse.result.selectResult.rows.map((row) => {
209+
return row as T;
210+
});
211+
case "insert":
212+
if (queryResponse.result?.$case != "insertResult") {
213+
throw new Error("Unexpected result type");
214+
}
215+
console.log("running insert query");
216+
return queryResponse.result.insertResult.lastInsertId;
217+
case "update":
218+
if (queryResponse.result?.$case != "updateResult") {
219+
throw new Error("Unexpected result type");
220+
}
221+
return queryResponse.result.updateResult.affectedRows;
222+
case "delete":
223+
if (queryResponse.result?.$case != "deleteResult") {
224+
throw new Error("Unexpected result type");
225+
}
226+
return queryResponse.result.deleteResult.affectedRows;
227+
default:
228+
throw new Error("Unexpected query type");
229+
}
230+
}
231+
232+
private toValue(key: string, value: unknown): Value {
233+
const dataType: ColumnType = this.schema.columns[key].type;
234+
235+
if (dataType == null) {
236+
throw new Error(`Unknown column ${key}`);
237+
}
238+
239+
if (dataType === ColumnType.COLUMN_TYPE_INT) {
240+
return { value: { $case: "intValue", intValue: value as number } };
241+
}
242+
243+
if (dataType === ColumnType.COLUMN_TYPE_FLOAT) {
244+
return { value: { $case: "floatValue", floatValue: value as number } };
245+
}
246+
247+
if (dataType === ColumnType.COLUMN_TYPE_DATE) {
248+
return { value: { $case: "dateValue", dateValue: value as string } };
249+
}
250+
251+
if (dataType == ColumnType.COLUMN_TYPE_DATETIME) {
252+
return {
253+
value: { $case: "datetimeValue", datetimeValue: value as string },
254+
};
255+
}
256+
257+
if (dataType == ColumnType.COLUMN_TYPE_STRING) {
258+
return { value: { $case: "stringValue", stringValue: value as string } };
259+
}
260+
261+
if (dataType == ColumnType.COLUMN_TYPE_BOOLEAN) {
262+
return {
263+
value: { $case: "booleanValue", booleanValue: value as boolean },
264+
};
265+
}
266+
267+
throw new Error(`Unsupported data type ${dataType}`);
268+
}
269+
}

src/common/TableColumn.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ColumnType } from "@topcoder-framework/client-relational";
2+
3+
export type TableColumn = {
4+
name: string;
5+
type: ColumnType;
6+
};
7+
8+
export type TableColumns = {
9+
[key: string]: TableColumn;
10+
};

src/service/LegacyChallenge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import {
44
CheckChallengeExistsResponse,
55
CreateChallengeInput,
66
LegacyChallengeId,
7-
LegacyChallengeList,
7+
LegacyChallengeList
88
} from "../models/domain-layer/legacy/challenge";
99

1010
import {
1111
LegacyChallengeServer,
12-
LegacyChallengeService,
12+
LegacyChallengeService
1313
} from "../models/domain-layer/legacy/services/challenge";
1414

1515
import { CreateResult, LookupCriteria } from "@topcoder-framework/lib-common";

yarn.lock

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@
1717
"@grpc/proto-loader" "^0.7.0"
1818
"@types/node" ">=12.12.47"
1919

20+
"@grpc/grpc-js@^1.8.0":
21+
version "1.8.7"
22+
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.8.7.tgz#2154fc0134462ad45f4134e8b54682a25ed05956"
23+
integrity sha512-dRAWjRFN1Zy9mzPNLkFFIWT8T6C9euwluzCHZUKuhC+Bk3MayNPcpgDRyG+sg+n2sitEUySKxUynirVpu9ItKw==
24+
dependencies:
25+
"@grpc/proto-loader" "^0.7.0"
26+
"@types/node" ">=12.12.47"
27+
2028
"@grpc/proto-loader@^0.7.0":
2129
version "0.7.4"
2230
resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.4.tgz#4946a84fbf47c3ddd4e6a97acb79d69a9f47ebf2"
@@ -99,23 +107,40 @@
99107
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
100108
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
101109

110+
<<<<<<< HEAD
102111
"@topcoder-framework/client-relational@^0.4.22-ci.0", "@topcoder-framework/client-relational@^0.4.24-ci.0":
103112
version "0.4.24-ci.0"
104113
resolved "http://localhost:4873/@topcoder-framework%2fclient-relational/-/client-relational-0.4.24-ci.0.tgz#ada970a70f7c9c34371752523ce20cbf734a40a7"
105114
integrity sha512-cnyzp4414BQuFml//y2A9Vgo0ULMSFEEnKjrq8kdpitvncVIenkowai6Geic37tOvnwZG3xxBNtmiHpDw3u/4A==
115+
=======
116+
"@topcoder-framework/client-relational@0.4.23-ci.0", "@topcoder-framework/client-relational@^0.4.23-ci.0":
117+
version "0.4.23-ci.0"
118+
resolved "http://localhost:4873/@topcoder-framework%2fclient-relational/-/client-relational-0.4.23-ci.0.tgz#73e5dc1a5fa0c92f0dc79f62042cd6f2b988fa02"
119+
integrity sha512-tRGCA//dqqc952KOSPoNBmN//ry+Vp3Yi9zm29zdDqIdKdUpyQn0yKNyVJ9ZhYSFXymW4c3vGt8O7el48wEjLg==
120+
>>>>>>> main
106121
dependencies:
107122
"@grpc/grpc-js" "^1.8.0"
108123
"@topcoder-framework/lib-common" "0.4.23-ci.0"
109124
topcoder-interface "github:topcoder-platform/plat-interface-definition#v0.0.10"
110125
tslib "^2.4.1"
111126

127+
<<<<<<< HEAD
112128
"@topcoder-framework/lib-common@0.4.23-ci.0", "@topcoder-framework/lib-common@^0.4.24-ci.0":
113129
version "0.4.24-ci.0"
114130
resolved "http://localhost:4873/@topcoder-framework%2flib-common/-/lib-common-0.4.24-ci.0.tgz#3f32ce108fa799bb5522200f21c4173e1328f816"
115131
integrity sha512-h3TrHGUIewfd/psX7yvoT/eUrh+E3KDpTCWEyVrmb3YD8CcvqFzeks2ryTZ1JADJHJoREQUFRNuW5odApQbJIg==
116132
dependencies:
117133
"@grpc/grpc-js" "^1.8.0"
118134
"@topcoder-framework/client-relational" "^0.4.22-ci.0"
135+
=======
136+
"@topcoder-framework/lib-common@0.4.23-ci.0", "@topcoder-framework/lib-common@^0.4.23-ci.0":
137+
version "0.4.23-ci.0"
138+
resolved "http://localhost:4873/@topcoder-framework%2flib-common/-/lib-common-0.4.23-ci.0.tgz#b2728bc27fca017cca5982b53eebcdfb90e04acc"
139+
integrity sha512-t3uvwmr5qE2VKcil9XMzh+0Z0lGihdltojhX8Cq4zqpeJHK9zrgBhU1WFXlPwBjNu3RglI5qaeTwvENE6ktR6w==
140+
dependencies:
141+
"@grpc/grpc-js" "^1.8.0"
142+
"@topcoder-framework/client-relational" "0.4.23-ci.0"
143+
>>>>>>> main
119144
rimraf "^3.0.2"
120145
topcoder-interface "github:topcoder-platform/plat-interface-definition#v0.0.10"
121146
tslib "^2.4.1"

0 commit comments

Comments
 (0)