@technicity/data-service-generator 0.5.13 → 0.6.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.
@@ -114,7 +114,7 @@ async function generate(input) {
114
114
  const tmpBuildOutputPath = path.join(tmpDirPath, "sdk-ts");
115
115
  const outdir = path.resolve(input.outdir);
116
116
  const sdkOutputPath = path.join(outdir, "sdk-ts");
117
- const nccVersion = "^0.29.0";
117
+ const nccVersion = "^0.33.0";
118
118
  child_process.execSync("npm i", { cwd: tmpDirPath, stdio: "inherit" });
119
119
  child_process.execSync(`npx -p @vercel/ncc@${nccVersion} ncc build ./${sdkFilename} -o ${tmpBuildOutputPath} -e ./artifacts`, { cwd: tmpDirPath, stdio: "inherit" });
120
120
  // TODO: workaround for artifacts.js not being output by ncc
@@ -219,6 +219,10 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts)
219
219
  return this.runtime.$prepareWhere(artifacts, table, where);
220
220
  }
221
221
 
222
+ async $queryRaw(sql: string, values?: any[]) {
223
+ return this.runtime.$queryRaw(sql, values);
224
+ }
225
+
222
226
  ${(await Promise.all(input.flatMap(async (x) => {
223
227
  let findOnes = [];
224
228
  const primaryColumn = await getPrimaryColumn(x.table);
@@ -523,27 +527,26 @@ async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMapped
523
527
  mappedFieldsMap.set(r.table, await getMappedFields(r.table));
524
528
  }
525
529
  }
526
- properties = {
527
- ...properties,
528
- ...oneToManyRelations.reduce((acc, v) => {
529
- let tsType = getTypeDataPostName(v.table);
530
- const mappedFields = mappedFieldsMap.get(v.table);
531
- if (includeMappedFields &&
532
- mappedFields != null &&
533
- mappedFields.length > 0) {
534
- tsType = `Omit<${tsType}, ${mappedFields
535
- .map((x) => JSON.stringify(x.as))
536
- .join(" | ")}>`;
537
- }
538
- tsType = `{$create: ${tsType}[]}`;
539
- acc[v.name] = { tsType };
540
- return acc;
541
- }, {}),
542
- };
543
530
  const uuidColumn = await getUuidColumn(table);
544
531
  const jsonSchema = {
545
532
  type: "object",
546
- properties,
533
+ properties: {
534
+ ...properties,
535
+ ...oneToManyRelations.reduce((acc, v) => {
536
+ let tsType = getTypeDataPostName(v.table);
537
+ const mappedFields = mappedFieldsMap.get(v.table);
538
+ if (includeMappedFields &&
539
+ mappedFields != null &&
540
+ mappedFields.length > 0) {
541
+ tsType = `Omit<${tsType}, ${mappedFields
542
+ .map((x) => JSON.stringify(x.as))
543
+ .join(" | ")}>`;
544
+ }
545
+ tsType = `{$create: ${tsType}[]}`;
546
+ acc[v.name] = { tsType };
547
+ return acc;
548
+ }, {}),
549
+ },
547
550
  additionalProperties: false,
548
551
  required: Object.keys(properties)
549
552
  .filter(
@@ -812,27 +815,23 @@ async function getJSONSchemaWhere(table) {
812
815
  };
813
816
  }
814
817
  async function getTypeOrderBy(table, name) {
815
- return (0, json_schema_to_typescript_1.compile)((await getJSONSchemaOrderBy(table)), name, json2TsOpts);
818
+ return (0, json_schema_to_typescript_1.compile)((await getJSONSchemaOrderBy(table, name)), name, json2TsOpts);
816
819
  }
817
- async function getJSONSchemaOrderBy(table) {
820
+ async function getJSONSchemaOrderBy(table, name) {
818
821
  const fieldNames = await getTableMeta(table).then((xs) => xs.map((x) => x.Field));
819
- const fieldSchemas = fieldNames.map((k) => ({
820
- type: "object",
821
- properties: { [k]: { enum: ["asc", "desc"] } },
822
- required: [k],
823
- additionalProperties: false,
824
- }));
822
+ const def = {
823
+ oneOf: fieldNames.map((k) => ({
824
+ type: "object",
825
+ properties: { [k]: { enum: ["asc", "desc"] } },
826
+ required: [k],
827
+ additionalProperties: false,
828
+ })),
829
+ };
830
+ const defName = `_${name}`;
831
+ const _schema = { $ref: `#/definitions/${defName}` };
825
832
  return {
826
- oneOf: [
827
- ...fieldSchemas,
828
- {
829
- type: "array",
830
- items: { oneOf: fieldSchemas },
831
- // minItems: 1,
832
- // maxItems: fieldNames.length,
833
- // uniqueItems: true,
834
- },
835
- ],
833
+ definitions: { [defName]: def },
834
+ oneOf: [_schema, { type: "array", items: _schema }],
836
835
  };
837
836
  }
838
837
  function getTypeShared() {
@@ -1,5 +1,6 @@
1
1
  export interface IRuntime {
2
2
  resolve(input: TResolveParams): Promise<any>;
3
+ $queryRaw(sql: string, values?: any[]): Promise<any>;
3
4
  $use: (middleware: TMiddleware) => void;
4
5
  $whereNeedsProcessing(where: any): boolean;
5
6
  $prepareWhere(artifacts: IArtifacts, table: string, data: any): Promise<any>;
@@ -25,10 +26,10 @@ export declare type TBeginTransaction = () => Promise<{
25
26
  commit: () => Promise<void>;
26
27
  }>;
27
28
  export declare type ISupplementClientOpts = boolean;
28
- export declare type IOrderBy = Array<{
29
+ export declare type IOrderBy = {
29
30
  column: string;
30
31
  direction: any;
31
- }>;
32
+ }[];
32
33
  export declare type IArgs = {
33
34
  [k: string]: any;
34
35
  };
@@ -1,19 +1,16 @@
1
- import type { IRuntime, TMiddleware, TResolveParams, IArtifacts, ISupplementClientOpts } from "./IRuntime";
1
+ import type { IRuntime, TMiddleware, TResolveParams, IArtifacts, TDbCall } from "./IRuntime";
2
2
  import { KSQL } from "../ksql";
3
- import { MSSQL } from "./lib/MSSQL";
3
+ declare type TGetTableName = (table: string) => string;
4
4
  export declare class RuntimeKSQL implements IRuntime {
5
5
  #private;
6
6
  constructor(clientOpts: ConstructorParameters<typeof KSQL>[0], otherOpts: {
7
- mssql: ConstructorParameters<typeof MSSQL>[0];
8
- supplementClientOpts?: ISupplementClientOpts;
7
+ getBaseTableName: TGetTableName;
8
+ _dbCall: TDbCall;
9
9
  }, artifacts: IArtifacts);
10
10
  resolve(input: TResolveParams): Promise<any>;
11
+ $queryRaw(sql: string, values?: any[]): Promise<any>;
11
12
  $use(middleware: TMiddleware): Promise<void>;
12
13
  $whereNeedsProcessing(where: any): boolean;
13
14
  $prepareWhere(artifacts: IArtifacts, table: string, data: any): Promise<{}>;
14
- private post;
15
- private patch;
16
- private patchList;
17
- private del;
18
- private deleteList;
19
15
  }
16
+ export {};
@@ -10,78 +10,37 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _RuntimeKSQL_ksql, _RuntimeKSQL_mssqlClient, _RuntimeKSQL_middlewareHandler, _RuntimeKSQL_tableKeyMap;
13
+ var _RuntimeKSQL_ksql, _RuntimeKSQL_middlewareHandler, _RuntimeKSQL_dbCall, _RuntimeKSQL_formatQuery, _RuntimeKSQL_getBaseTableName;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.RuntimeKSQL = void 0;
16
16
  const SqlString = require("sqlstring");
17
+ const async_hooks_1 = require("async_hooks");
18
+ const _ = require("lodash");
19
+ const graphql_relay_1 = require("graphql-relay");
17
20
  const shared_1 = require("./lib/shared");
18
- const getWhere_1 = require("./lib/getWhere");
19
21
  const ksql_1 = require("../ksql");
20
22
  const SDKNotFoundError_1 = require("./lib/SDKNotFoundError");
21
- const MSSQL_1 = require("./lib/MSSQL");
22
- const typeCastMSSQL_1 = require("./lib/typeCastMSSQL");
23
+ const stringifyWhere_1 = require("./lib/stringifyWhere");
24
+ const cursor_1 = require("./lib/cursor");
25
+ const escapeId = SqlString.escapeId.bind(SqlString);
23
26
  class RuntimeKSQL {
24
27
  constructor(clientOpts, otherOpts, artifacts) {
25
28
  _RuntimeKSQL_ksql.set(this, void 0);
26
- _RuntimeKSQL_mssqlClient.set(this, void 0);
27
29
  _RuntimeKSQL_middlewareHandler.set(this, void 0);
28
- _RuntimeKSQL_tableKeyMap.set(this, void 0);
29
- __classPrivateFieldSet(this, _RuntimeKSQL_ksql, new ksql_1.KSQL(clientOpts), "f");
30
- __classPrivateFieldSet(this, _RuntimeKSQL_mssqlClient, new MSSQL_1.MSSQL(otherOpts.mssql, otherOpts?.supplementClientOpts ? (0, typeCastMSSQL_1.typeCastMSSQL)() : undefined), "f");
30
+ _RuntimeKSQL_dbCall.set(this, void 0);
31
+ _RuntimeKSQL_formatQuery.set(this, void 0);
32
+ _RuntimeKSQL_getBaseTableName.set(this, void 0);
33
+ __classPrivateFieldSet(this, _RuntimeKSQL_ksql, new ksql_1.KSQL(clientOpts ?? {}), "f");
31
34
  __classPrivateFieldSet(this, _RuntimeKSQL_middlewareHandler, new shared_1.MiddlewareHandler(), "f");
32
- __classPrivateFieldSet(this, _RuntimeKSQL_tableKeyMap, {}, "f");
33
- for (let table in artifacts) {
34
- let map = {};
35
- for (let kk of artifacts[table].scalarFields) {
36
- map[kk.toLocaleUpperCase()] = kk;
37
- }
38
- __classPrivateFieldGet(this, _RuntimeKSQL_tableKeyMap, "f")[table] = map;
39
- }
35
+ __classPrivateFieldSet(this, _RuntimeKSQL_dbCall, otherOpts._dbCall ?? __classPrivateFieldGet(this, _RuntimeKSQL_ksql, "f").streamQuery.bind(__classPrivateFieldGet(this, _RuntimeKSQL_ksql, "f")), "f");
36
+ __classPrivateFieldSet(this, _RuntimeKSQL_getBaseTableName, otherOpts.getBaseTableName, "f");
37
+ __classPrivateFieldSet(this, _RuntimeKSQL_formatQuery, SqlString.format.bind(SqlString), "f");
40
38
  }
41
39
  async resolve(input) {
42
- if (input.action === "findMany" || input.action === "findManyPaginated") {
43
- const primaryKey = input.artifacts[input.resource].primaryKey;
44
- // Overwrite
45
- input.fields = [primaryKey];
46
- const resolved = await (0, shared_1.resolve)(input, __classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f").dbCall.bind(__classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f")), __classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f").formatQuery.bind(__classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f")), __classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f").beginTransaction.bind(__classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f")), "mssql", __classPrivateFieldGet(this, _RuntimeKSQL_middlewareHandler, "f"), input.context ?? {});
47
- const keys = input.action === "findMany"
48
- ? resolved.map((x) => x[primaryKey])
49
- : resolved.results.map((x) => x[primaryKey]);
50
- if (keys.length === 0) {
51
- return resolved;
52
- }
53
- const sql = SqlString.format("SELECT * FROM ?? WHERE ?? IN (?);", [
54
- getKSQLTablename(input.resource, input?.context?.hydrate),
55
- primaryKey,
56
- keys,
57
- ]);
58
- const results = await __classPrivateFieldGet(this, _RuntimeKSQL_ksql, "f").streamQuery(sql);
59
- for (let x of results) {
60
- mapKeys(x, __classPrivateFieldGet(this, _RuntimeKSQL_tableKeyMap, "f")[input.resource]);
61
- }
62
- if (input.action == "findMany") {
63
- return results;
64
- }
65
- resolved.results = results;
66
- return resolved;
67
- }
68
- else {
69
- const table = input.resource;
70
- const tableKSQL = getKSQLTablename(input.resource, input?.context?.hydrate);
71
- const where = (0, getWhere_1.getWhere)(SqlString.escapeId(tableKSQL), input.args, "mysql");
72
- if (!where) {
73
- throw new Error("Invalid $where");
74
- }
75
- const sql = SqlString.format(`SELECT * FROM ?? WHERE ${where};`, [
76
- tableKSQL,
77
- ]);
78
- let out = await __classPrivateFieldGet(this, _RuntimeKSQL_ksql, "f").streamQuery(sql).then((xs) => xs[0]);
79
- if (out == null) {
80
- throw new SDKNotFoundError_1.SDKNotFoundError();
81
- }
82
- mapKeys(out, __classPrivateFieldGet(this, _RuntimeKSQL_tableKeyMap, "f")[table]);
83
- return out;
84
- }
40
+ return resolve(input, __classPrivateFieldGet(this, _RuntimeKSQL_dbCall, "f").bind(this), __classPrivateFieldGet(this, _RuntimeKSQL_formatQuery, "f").bind(this), __classPrivateFieldGet(this, _RuntimeKSQL_middlewareHandler, "f"), __classPrivateFieldGet(this, _RuntimeKSQL_getBaseTableName, "f"));
41
+ }
42
+ async $queryRaw(sql, values) {
43
+ return __classPrivateFieldGet(this, _RuntimeKSQL_dbCall, "f").call(this, SqlString.format(sql, values ?? []));
85
44
  }
86
45
  async $use(middleware) {
87
46
  __classPrivateFieldGet(this, _RuntimeKSQL_middlewareHandler, "f").register(middleware);
@@ -90,39 +49,340 @@ class RuntimeKSQL {
90
49
  return (0, shared_1.whereNeedsProcessing)(where);
91
50
  }
92
51
  async $prepareWhere(artifacts, table, data) {
93
- return (0, shared_1._prepareWhere)(artifacts, table, data, __classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f").dbCall.bind(__classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f")), __classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f").formatQuery.bind(__classPrivateFieldGet(this, _RuntimeKSQL_mssqlClient, "f")));
52
+ return (0, shared_1._prepareWhere)(artifacts, table, data, __classPrivateFieldGet(this, _RuntimeKSQL_dbCall, "f").bind(this), __classPrivateFieldGet(this, _RuntimeKSQL_formatQuery, "f").bind(this));
53
+ }
54
+ }
55
+ exports.RuntimeKSQL = RuntimeKSQL;
56
+ _RuntimeKSQL_ksql = new WeakMap(), _RuntimeKSQL_middlewareHandler = new WeakMap(), _RuntimeKSQL_dbCall = new WeakMap(), _RuntimeKSQL_formatQuery = new WeakMap(), _RuntimeKSQL_getBaseTableName = new WeakMap();
57
+ async function resolve(input, dbCall, formatQuery, middlewareHandler, getBaseTableName) {
58
+ // https://github.com/prisma/prisma/blob/822198e5ba21535364d20c86901b8c3778ebf6a3/packages/client/src/runtime/getPrismaClient.ts#L1087
59
+ let index = -1;
60
+ if (middlewareHandler.length() > 0) {
61
+ const resource = new async_hooks_1.AsyncResource("sdk-request");
62
+ const params = input;
63
+ const consumer = (paramsMaybeMutated) => {
64
+ const nextMiddleware = middlewareHandler.get(++index);
65
+ if (nextMiddleware != null) {
66
+ return nextMiddleware(paramsMaybeMutated, consumer);
67
+ }
68
+ const paramsChanged = { ...input, ...params };
69
+ return _resolve(paramsChanged, dbCall, formatQuery, getBaseTableName);
70
+ };
71
+ return resource.runInAsyncScope(() => consumer(params));
72
+ }
73
+ return _resolve(input, dbCall, formatQuery, getBaseTableName);
74
+ }
75
+ function _resolve(input, dbCall, formatQuery, getBaseTableName) {
76
+ if (input.action === "findUnique") {
77
+ return getData(input, dbCall, formatQuery, getBaseTableName);
78
+ }
79
+ if (input.action === "findMany") {
80
+ return getData(input, dbCall, formatQuery, getBaseTableName);
94
81
  }
95
- async post(input) {
82
+ if (input.action === "findManyPaginated") {
83
+ return getData(input, dbCall, formatQuery, getBaseTableName);
84
+ }
85
+ if (input.action === "create") {
96
86
  throw new Error("Not implemented.");
97
87
  }
98
- async patch(input) {
88
+ if (input.action === "update") {
99
89
  throw new Error("Not implemented.");
100
90
  }
101
- async patchList(input) {
91
+ if (input.action === "updateMany") {
102
92
  throw new Error("Not implemented.");
103
93
  }
104
- async del(input) {
94
+ if (input.action === "delete") {
105
95
  throw new Error("Not implemented.");
106
96
  }
107
- async deleteList(input) {
97
+ if (input.action === "deleteMany") {
108
98
  throw new Error("Not implemented.");
109
99
  }
100
+ throw new Error("Invalid action: " + input.action);
110
101
  }
111
- exports.RuntimeKSQL = RuntimeKSQL;
112
- _RuntimeKSQL_ksql = new WeakMap(), _RuntimeKSQL_mssqlClient = new WeakMap(), _RuntimeKSQL_middlewareHandler = new WeakMap(), _RuntimeKSQL_tableKeyMap = new WeakMap();
113
- function getKSQLTablename(tablename, hydrate = true) {
114
- return hydrate
115
- ? `${tablename.toLocaleUpperCase()}_TAB_HYD`
116
- : `${tablename.toLocaleUpperCase()}_TAB`;
102
+ async function getData(input, dbCall, formatQuery, getBaseTableName) {
103
+ const { action } = input;
104
+ const grabMany = action === "findMany" ||
105
+ action === "findManyPaginated" ||
106
+ action === "updateMany" ||
107
+ action === "deleteMany";
108
+ const data = await _getData(input, grabMany, dbCall, formatQuery, getBaseTableName);
109
+ if (data == null && !grabMany) {
110
+ throw new SDKNotFoundError_1.SDKNotFoundError();
111
+ }
112
+ // We only need to remove extra keys if input.fields isn't
113
+ // specified, since otherwise there wouldn't be extra keys
114
+ const shouldRemoveExtraKeys = input.fields != null;
115
+ if (action === "findManyPaginated" && data?.results != null) {
116
+ (0, shared_1.postProcess)(data.results, input.fields ?? [], shouldRemoveExtraKeys);
117
+ }
118
+ else {
119
+ (0, shared_1.postProcess)(data, input.fields ?? [], shouldRemoveExtraKeys);
120
+ }
121
+ return data;
117
122
  }
118
- // Mutates item
119
- function mapKeys(item, keyMap) {
120
- delete item.__deleted;
121
- // for (let k in item) {
122
- // const v = keyMap[k];
123
- // if (v) {
124
- // item[v] = item[k];
125
- // delete item[k];
126
- // }
127
- // }
123
+ async function _getData(input, grabMany, dbCall, formatQuery, getBaseTableName) {
124
+ const { resource: table, artifacts, args, action } = input;
125
+ const tableArtifacts = artifacts[table];
126
+ let fields = input.fields;
127
+ if (!Array.isArray(fields)) {
128
+ fields = tableArtifacts.scalarFields;
129
+ }
130
+ let where = undefined;
131
+ if (args?.$where != null) {
132
+ let argsMapped = args;
133
+ if (Array.isArray(argsMapped?.$where)) {
134
+ argsMapped = _.cloneDeep(argsMapped);
135
+ argsMapped.$where = argsMapped.$where[0];
136
+ }
137
+ const whereResult = (0, stringifyWhere_1.stringifyWhere)({
138
+ where: argsMapped.$where,
139
+ table: escapeId(getBaseTableName(table)),
140
+ dialect: "mysql",
141
+ args: argsMapped,
142
+ });
143
+ if (whereResult) {
144
+ where = whereResult;
145
+ }
146
+ }
147
+ let columns = [];
148
+ let mappedFields = [];
149
+ let relationFields = [];
150
+ for (let x of fields) {
151
+ if (typeof x === "string") {
152
+ const mappedField = tableArtifacts.mappedFields?.[x];
153
+ if (mappedField != null) {
154
+ mappedFields.push(x);
155
+ if (!columns.includes(mappedField.foreignKey)) {
156
+ columns.push(mappedField.foreignKey);
157
+ }
158
+ }
159
+ else {
160
+ columns.push(x);
161
+ }
162
+ }
163
+ else {
164
+ relationFields.push(x);
165
+ const relationField = tableArtifacts.relationFields?.[x.name];
166
+ if (relationField != null) {
167
+ if (relationField.type === "one-to-many__many-to-one" &&
168
+ !columns.includes(relationField.relation.foreignKey)) {
169
+ columns.push(relationField.relation.foreignKey);
170
+ }
171
+ else if (relationField.type === "many-to-many" &&
172
+ !columns.includes(relationField.relations[0].referencedKey)) {
173
+ columns.push(relationField.relations[0].referencedKey);
174
+ }
175
+ }
176
+ }
177
+ }
178
+ if (action === "findManyPaginated") {
179
+ // For cursor
180
+ if (!columns.includes(tableArtifacts.primaryKey)) {
181
+ columns.push(tableArtifacts.primaryKey);
182
+ }
183
+ }
184
+ let s = formatQuery(`SELECT ?? FROM ??`, [columns, getBaseTableName(table)]);
185
+ if (where) {
186
+ s += ` WHERE ${where}`;
187
+ }
188
+ s += ";";
189
+ return dbCall(s).then((_result) => {
190
+ let result = grabMany ? _result : _result[0];
191
+ if (result == null) {
192
+ return null;
193
+ }
194
+ if (grabMany && Array.isArray(result)) {
195
+ // Important: ordering must be before pagination, since pagination is done in code
196
+ let orderBy;
197
+ if (args == null || args.$orderBy == null) {
198
+ orderBy = [{ column: tableArtifacts.primaryKey, direction: "asc" }];
199
+ }
200
+ else if (Array.isArray(args.$orderBy)) {
201
+ orderBy = args.$orderBy.map((x) => {
202
+ const [column, direction] = Object.entries(x)[0];
203
+ return { column, direction };
204
+ });
205
+ }
206
+ else {
207
+ const [column, direction] = Object.entries(args.$orderBy)[0];
208
+ orderBy = [{ column, direction }];
209
+ }
210
+ for (let x of orderBy) {
211
+ if (!["asc", "desc"].includes(x.direction)) {
212
+ throw new Error(`Expected \`asc\` or \`desc\` for \`direction\` but got: ${x.direction}`);
213
+ }
214
+ }
215
+ result = _.orderBy(result, orderBy.map((x) => x.column), orderBy.map((x) => x.direction));
216
+ if (action === "findManyPaginated") {
217
+ result = paginate(result, args, tableArtifacts.primaryKey, action);
218
+ return Promise.all(result.results.map((x) => resolveDependentFields(table, x, mappedFields, relationFields, artifacts, dbCall, formatQuery, getBaseTableName).then((xx) => mergeSubResults(x, xx)))).then((xx) => {
219
+ result.results = xx;
220
+ return result;
221
+ });
222
+ }
223
+ const limit = args?.$limit;
224
+ if (typeof limit === "number") {
225
+ result = result.slice(0, limit);
226
+ }
227
+ return Promise.all(result.map((x) => resolveDependentFields(table, x, mappedFields, relationFields, artifacts, dbCall, formatQuery, getBaseTableName).then((xx) => mergeSubResults(x, xx))));
228
+ }
229
+ return resolveDependentFields(table, result, mappedFields, relationFields, artifacts, dbCall, formatQuery, getBaseTableName).then((xx) => mergeSubResults(result, xx));
230
+ });
231
+ }
232
+ async function resolveDependentFields(table, parentData, mappedFields, relationFields, artifacts, dbCall, formatQuery, getBaseTableName) {
233
+ const tableArtifacts = artifacts[table];
234
+ return Promise.all([]
235
+ .concat(mappedFields.map((x) => {
236
+ const mappedField = tableArtifacts.mappedFields?.[x];
237
+ if (mappedField == null) {
238
+ throw new Error(`Invalid mappedField: ${x}`);
239
+ }
240
+ if (parentData?.[mappedField.foreignKey] == null) {
241
+ return { [x]: null };
242
+ }
243
+ const s = formatQuery(`SELECT ?? FROM ?? WHERE ?? = ?;`, [
244
+ mappedField.name,
245
+ getBaseTableName(mappedField.referencedTable),
246
+ mappedField.referencedKey,
247
+ parentData?.[mappedField.foreignKey],
248
+ ]);
249
+ return dbCall(s).then((xs) => ({
250
+ [x]: xs?.[0]?.[mappedField.name] ?? null,
251
+ }));
252
+ }))
253
+ .concat(relationFields.map((x) => {
254
+ if (typeof x === "string") {
255
+ throw new Error(`Invalid field: ${x}`);
256
+ }
257
+ const relationField = tableArtifacts.relationFields?.[x.name];
258
+ if (relationField == null) {
259
+ throw new Error("Invalid field: " + table + "." + x.name);
260
+ }
261
+ if (relationField.type === "many-to-many") {
262
+ const junctionReferencedKey = relationField.relations[0].referencedKey;
263
+ const junctionKeyValue = parentData?.[junctionReferencedKey];
264
+ if (junctionKeyValue == null) {
265
+ return { [x.as ?? x.name]: [] };
266
+ }
267
+ let whereJunction = x.args != null && Array.isArray(x.args.$where)
268
+ ? _.cloneDeep(x.args.$where[1])
269
+ : {};
270
+ whereJunction = {
271
+ $and: [
272
+ { [relationField.relations[0].foreignKey]: junctionKeyValue },
273
+ whereJunction,
274
+ ],
275
+ };
276
+ const key = relationField.relations[1].foreignKey;
277
+ const s = formatQuery(`SELECT ?? FROM ?? WHERE ${(0, stringifyWhere_1.stringifyWhere)({
278
+ where: whereJunction,
279
+ table: escapeId(getBaseTableName(relationField.junctionTable)),
280
+ dialect: "mysql",
281
+ // Since we're paginating, empty is fine
282
+ args: {},
283
+ })};`, [key, getBaseTableName(relationField.junctionTable)]);
284
+ return dbCall(s)
285
+ .then((xs) => xs.map((x) => x?.[key]))
286
+ .then((keys) => {
287
+ const whereDest = {
288
+ [relationField.relations[1].referencedKey]: { $in: keys },
289
+ };
290
+ const argsMapped = x.args == null ? {} : _.cloneDeep(x.args);
291
+ if (Array.isArray(argsMapped.$where)) {
292
+ argsMapped.$where = {
293
+ $and: [whereDest, argsMapped.$where[0]],
294
+ };
295
+ }
296
+ else if (argsMapped.$where != null) {
297
+ argsMapped.$where = { $and: [whereDest, argsMapped.$where] };
298
+ }
299
+ else {
300
+ argsMapped.$where = whereDest;
301
+ }
302
+ return _getData({
303
+ resource: relationField.table,
304
+ args: argsMapped,
305
+ fields: x.fields,
306
+ artifacts,
307
+ action: relationField.grabMany ? "findMany" : "findUnique",
308
+ }, relationField.grabMany, dbCall, formatQuery, getBaseTableName).then((xx) => ({ [x.as ?? x.name]: xx }));
309
+ });
310
+ }
311
+ if (relationField.type === "one-to-many__many-to-one") {
312
+ const referencedKey = relationField.relation.referencedKey;
313
+ const foreignKeyValue = parentData?.[relationField.relation.foreignKey];
314
+ if (foreignKeyValue == null) {
315
+ return { [x.as ?? x.name]: relationField.grabMany ? [] : null };
316
+ }
317
+ const whereRef = { [referencedKey]: foreignKeyValue };
318
+ const argsMapped = x.args == null ? {} : _.cloneDeep(x.args);
319
+ if (argsMapped.$where == null) {
320
+ argsMapped.$where = whereRef;
321
+ }
322
+ else {
323
+ argsMapped.$where = { $and: [whereRef, argsMapped.$where] };
324
+ }
325
+ return _getData({
326
+ resource: relationField.table,
327
+ args: argsMapped,
328
+ fields: x.fields,
329
+ artifacts,
330
+ action: relationField.grabMany ? "findMany" : "findUnique",
331
+ }, relationField.grabMany, dbCall, formatQuery, getBaseTableName).then((xx) => ({ [x.as ?? x.name]: xx }));
332
+ }
333
+ throw new Error(`Invalid relationField.type`);
334
+ })));
335
+ }
336
+ function mergeSubResults(result, subResults) {
337
+ for (let x of subResults) {
338
+ result = { ...result, ...x };
339
+ }
340
+ return result;
341
+ }
342
+ function paginate(data, args, primaryKey, action) {
343
+ if (action === "findManyPaginated") {
344
+ if (typeof args?.$paginate?.limit === "number") {
345
+ const totalCount = data.length;
346
+ const limit = args.$paginate.limit;
347
+ const offset = args.$paginate.offset ?? 0;
348
+ data = data.slice(offset, offset + limit);
349
+ return {
350
+ results: data,
351
+ paginationInfo: { totalCount },
352
+ };
353
+ }
354
+ if (typeof args?.$paginate?.first === "number" ||
355
+ typeof args?.$paginate?.last === "number") {
356
+ const totalCount = data.length;
357
+ let argsMapped = args;
358
+ // Convert cursor(s) for graphql-relay
359
+ for (let k of ["after", "before"]) {
360
+ if (args?.$paginate?.[k] != null) {
361
+ argsMapped = _.cloneDeep(argsMapped);
362
+ const offset = data.findIndex((x) => x[primaryKey] === (0, cursor_1.decodeCursor)(args.$paginate[k]));
363
+ if (offset === -1) {
364
+ throw new Error(`Invalid cursor: ${args.$paginate[k]}`);
365
+ }
366
+ argsMapped.$paginate[k] = (0, graphql_relay_1.offsetToCursor)(offset);
367
+ }
368
+ }
369
+ const connection = (0, graphql_relay_1.connectionFromArray)(data, argsMapped.$paginate);
370
+ const results = connection.edges.map((x) => x.node);
371
+ // Convert cursors back
372
+ const startKey = results[0]?.[primaryKey];
373
+ const endKey = results[results.length - 1]?.[primaryKey];
374
+ const startCursor = startKey == null ? null : (0, cursor_1.encodeCursor)(startKey);
375
+ const endCursor = endKey == null ? null : (0, cursor_1.encodeCursor)(endKey);
376
+ return {
377
+ results,
378
+ paginationInfo: {
379
+ ...connection.pageInfo,
380
+ startCursor,
381
+ endCursor,
382
+ totalCount,
383
+ },
384
+ };
385
+ }
386
+ }
387
+ throw new Error(`Invalid pagination action: ${action}`);
128
388
  }
@@ -8,6 +8,7 @@ export declare class RuntimeMSSQL implements IRuntime {
8
8
  typeCast?: Parameters<typeof typeCastMSSQL>[0];
9
9
  }, artifacts: IArtifacts);
10
10
  resolve(input: TResolveParams): Promise<any>;
11
+ $queryRaw(sql: string, values?: any[]): Promise<any>;
11
12
  $use(middleware: TMiddleware): Promise<void>;
12
13
  $whereNeedsProcessing(where: any): boolean;
13
14
  $prepareWhere(artifacts: IArtifacts, table: string, data: any): Promise<{}>;
@@ -29,6 +29,9 @@ class RuntimeMSSQL {
29
29
  async resolve(input) {
30
30
  return (0, shared_1.resolve)(input, this.dbCall.bind(this), this.formatQuery.bind(this), this.beginTransaction.bind(this), __classPrivateFieldGet(this, _RuntimeMSSQL_dialect, "f"), __classPrivateFieldGet(this, _RuntimeMSSQL_middlewareHandler, "f"), input.context ?? {});
31
31
  }
32
+ async $queryRaw(sql, values) {
33
+ return this.dbCall(this.formatQuery(sql, values ?? []));
34
+ }
32
35
  async $use(middleware) {
33
36
  __classPrivateFieldGet(this, _RuntimeMSSQL_middlewareHandler, "f").register(middleware);
34
37
  }
@@ -7,6 +7,7 @@ export declare class RuntimeMySQL implements IRuntime {
7
7
  supplementClientOpts?: ISupplementClientOpts;
8
8
  }, artifacts: IArtifacts);
9
9
  resolve(input: TResolveParams): Promise<any>;
10
+ $queryRaw(sql: string, values?: any[]): Promise<any>;
10
11
  $use(middleware: TMiddleware): Promise<void>;
11
12
  $whereNeedsProcessing(where: any): boolean;
12
13
  $prepareWhere(artifacts: IArtifacts, table: string, data: any): Promise<{}>;
@@ -65,6 +65,9 @@ class RuntimeMySQL {
65
65
  async resolve(input) {
66
66
  return (0, shared_1.resolve)(input, this.dbCall.bind(this), this.formatQuery.bind(this), this.beginTransaction.bind(this), __classPrivateFieldGet(this, _RuntimeMySQL_dialect, "f"), __classPrivateFieldGet(this, _RuntimeMySQL_middlewareHandler, "f"), input.context ?? {});
67
67
  }
68
+ async $queryRaw(sql, values) {
69
+ return this.dbCall(this.formatQuery(sql, values ?? []));
70
+ }
68
71
  async $use(middleware) {
69
72
  __classPrivateFieldGet(this, _RuntimeMySQL_middlewareHandler, "f").register(middleware);
70
73
  }
@@ -32,6 +32,13 @@ function getOrderBy(args, primaryKey) {
32
32
  out = flipOrderByDirection(out);
33
33
  }
34
34
  }
35
+ if (Array.isArray(out)) {
36
+ for (let x of out) {
37
+ if (!["asc", "desc"].includes(x.direction)) {
38
+ throw new Error(`Expected \`asc\` or \`desc\` for \`direction\` but got: ${x.direction}`);
39
+ }
40
+ }
41
+ }
35
42
  return { orderBy: out, flip };
36
43
  }
37
44
  exports.getOrderBy = getOrderBy;
@@ -1,4 +1,4 @@
1
- import type { IArtifacts, IDialect, TDbCall, TFormatQuery, TBeginTransaction, TContext } from "../IRuntime";
1
+ import type { IGetSQLASTInput, IArtifacts, IDialect, TDbCall, TFormatQuery, TBeginTransaction, TContext } from "../IRuntime";
2
2
  import type { TMiddleware, TResolveParams } from "../IRuntime";
3
3
  export declare function resolve(input: TResolveParams, dbCall: TDbCall, formatQuery: TFormatQuery, beginTransaction: TBeginTransaction, dialect: IDialect, middlewareHandler: MiddlewareHandler<TMiddleware>, context: TContext): Promise<any>;
4
4
  export declare class MiddlewareHandler<M extends Function> {
@@ -8,5 +8,6 @@ export declare class MiddlewareHandler<M extends Function> {
8
8
  has(id: number): boolean;
9
9
  length(): number;
10
10
  }
11
+ export declare function postProcess(data: any, fields: IGetSQLASTInput["fields"], shouldRemoveExtraKeys: boolean): void;
11
12
  export declare function whereNeedsProcessing(where: any): boolean;
12
13
  export declare function _prepareWhere(artifacts: IArtifacts, table: string, data: any, dbCall: TDbCall, formatQuery: TFormatQuery): Promise<{}>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._prepareWhere = exports.whereNeedsProcessing = exports.MiddlewareHandler = exports.resolve = void 0;
3
+ exports._prepareWhere = exports.whereNeedsProcessing = exports.postProcess = exports.MiddlewareHandler = exports.resolve = void 0;
4
4
  // @ts-ignore
5
5
  // import * as queryAST from "join-monster/dist/query-ast-to-sql-ast";
6
6
  // @ts-ignore
@@ -102,7 +102,8 @@ async function getData(input, dbCall, formatQuery, dialect) {
102
102
  limit = input.args.$paginate.limit;
103
103
  offset = input.args.$paginate.offset;
104
104
  }
105
- else {
105
+ else if (typeof input?.args?.$paginate?.first === "number" ||
106
+ typeof input?.args?.$paginate?.last === "number") {
106
107
  paginationType = "cursor";
107
108
  limit = (typeof input?.args?.$paginate?.first === "number"
108
109
  ? input?.args?.$paginate?.first
@@ -119,11 +120,19 @@ async function getData(input, dbCall, formatQuery, dialect) {
119
120
  primaryKey,
120
121
  (0, cursor_1.decodeCursor)(cursor),
121
122
  ])).then((xs) => xs[0]);
123
+ if (rowWithMatchingCursor == null) {
124
+ throw new Error(`Invalid cursor: ${cursor}`);
125
+ }
122
126
  }
123
127
  }
128
+ else {
129
+ throw new Error(`Invalid $paginate: ${input?.args?.$paginate}`);
130
+ }
124
131
  }
125
132
  else if (action === "findMany") {
126
- limit = input?.args?.$limit;
133
+ if (typeof input?.args?.$limit === "number") {
134
+ limit = input?.args?.$limit;
135
+ }
127
136
  }
128
137
  // we need to read the query AST and build a new "SQL AST" from which the SQL and
129
138
  // const sqlAST = queryAST.queryASTToSqlAST(resolveInfo, options, context);
@@ -700,6 +709,7 @@ function postProcess(data, fields, shouldRemoveExtraKeys) {
700
709
  }
701
710
  (0, runTransforms_1.runTransforms)(data, fields);
702
711
  }
712
+ exports.postProcess = postProcess;
703
713
  function removeExtraKeys(data, fields) {
704
714
  if (data == null || (Array.isArray(data) && data[0] == null)) {
705
715
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technicity/data-service-generator",
3
- "version": "0.5.13",
3
+ "version": "0.6.0",
4
4
  "main": "./dist/index.js",
5
5
  "files": [
6
6
  "dist"
@@ -15,8 +15,9 @@
15
15
  "bluebird": "^3.7.2",
16
16
  "change-case": "^4.1.1",
17
17
  "fs-extra": "10.0.0",
18
- "graphql": "14.7.0",
19
- "join-monster": "apalm/join-monster#125168ac665480f5df1e16ebea49f223e0ebf84a",
18
+ "graphql": "15.8.0",
19
+ "graphql-relay": "^0.9.0",
20
+ "join-monster": "apalm/join-monster#340bcad96da4c268e874d07552d564c98ecf39ea",
20
21
  "json-schema-to-typescript": "10.1.5",
21
22
  "lodash": "^4.17.20",
22
23
  "mssql": "^6.3.1",
@@ -28,7 +29,7 @@
28
29
  },
29
30
  "devDependencies": {
30
31
  "@types/fs-extra": "9.0.13",
31
- "@types/lodash": "4.14.176",
32
+ "@types/lodash": "4.14.177",
32
33
  "@types/mssql": "^6.0.7",
33
34
  "@types/node": "14.17.9",
34
35
  "@types/prettier": "^2.1.5",
@@ -36,7 +37,7 @@
36
37
  "@types/uuid": "^8.3.1",
37
38
  "env-cmd": "^10.1.0",
38
39
  "mocha": "9.1.3",
39
- "sinon": "^11.1.2",
40
+ "sinon": "12.0.1",
40
41
  "typescript": "^4.0.5"
41
42
  }
42
43
  }