Skip to content

Commit b2a7a49

Browse files
committed
feat: add project, resource, payment schema
1 parent 22e6a04 commit b2a7a49

File tree

2 files changed

+280
-0
lines changed

2 files changed

+280
-0
lines changed

src/common/QueryRunner.ts

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

src/schema/resource/ResourceInfoType.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
<<<<<<< HEAD
12
import { ColumnType, Schema } from "@topcoder-framework/client-relational";
23
import { ResourceInfoType } from "../../models/domain-layer/legacy/resource_info_type";
34
import { AuditColumns } from "../common/AuditColumns";
45

56
export const ResourceInfoTypeSchema: Schema<ResourceInfoType> = {
7+
=======
8+
import { Schema } from "../../common/QueryRunner.js";
9+
import { ColumnType } from "../../../dist/grpc/models/rdb/relational.js";
10+
import { AuditColumns } from "../common/AuditColumns";
11+
12+
export const Resource: Schema = {
13+
>>>>>>> e4ff7a2 (feat: add project, resource, payment schema)
614
dbSchema: "tcs_catalog",
715
tableName: "resource_info_type_lu",
816
columns: {

0 commit comments

Comments
 (0)