@teamkeel/functions-runtime 0.382.1 → 0.384.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamkeel/functions-runtime",
3
- "version": "0.382.1",
3
+ "version": "0.384.0",
4
4
  "description": "Internal package used by @teamkeel/sdk",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -27,7 +27,7 @@
27
27
  "change-case": "^4.1.2",
28
28
  "json-rpc-2.0": "^1.7.0",
29
29
  "ksuid": "^3.0.0",
30
- "kysely": "^0.23.5",
30
+ "kysely": "^0.25.0",
31
31
  "pg": "^8.11.3",
32
32
  "traceparent": "^1.0.0"
33
33
  }
package/src/ModelAPI.js CHANGED
@@ -54,7 +54,12 @@ class ModelAPI {
54
54
 
55
55
  return tracing.withSpan(name, () => {
56
56
  const db = useDatabase();
57
- return create(db, this._tableName, this._tableConfigMap, values);
57
+ return create(
58
+ db,
59
+ this._tableName,
60
+ this._tableConfigMap,
61
+ snakeCaseObject(values)
62
+ );
58
63
  });
59
64
  }
60
65
 
@@ -26,6 +26,7 @@ beforeEach(async () => {
26
26
  CREATE TABLE post(
27
27
  id text PRIMARY KEY,
28
28
  title text,
29
+ tags text[],
29
30
  author_id text references person(id)
30
31
  );
31
32
  CREATE TABLE author(
@@ -101,7 +102,6 @@ test("ModelAPI.create - throws if database constraint fails", async () => {
101
102
  favouriteNumber: 10,
102
103
  });
103
104
  const promise = personAPI.create({
104
- id: KSUID.randomSync().string,
105
105
  id: row.id,
106
106
  name: "Jim",
107
107
  married: false,
@@ -112,6 +112,23 @@ test("ModelAPI.create - throws if database constraint fails", async () => {
112
112
  );
113
113
  });
114
114
 
115
+ test("ModelAPI.create - arrays", async () => {
116
+ const person = await personAPI.create({
117
+ id: KSUID.randomSync().string,
118
+ name: "Jim",
119
+ married: false,
120
+ favouriteNumber: 10,
121
+ });
122
+
123
+ const row = await postAPI.create({
124
+ id: "id",
125
+ title: "My Post",
126
+ tags: ["tag 1", "tag 2"],
127
+ authorId: person.id,
128
+ });
129
+ expect(row.tags).toEqual(["tag 1", "tag 2"]);
130
+ });
131
+
115
132
  test("ModelAPI.findOne", async () => {
116
133
  const created = await personAPI.create({
117
134
  id: KSUID.randomSync().string,
@@ -1006,7 +1023,7 @@ describe("QueryBuilder", () => {
1006
1023
  expect(deletedId).toEqual(p.id);
1007
1024
  });
1008
1025
 
1009
- test("Model API chained delete - non existent id", async () => {
1026
+ test("ModelAPI chained delete - non existent id", async () => {
1010
1027
  const fakeId = "xxx";
1011
1028
 
1012
1029
  // the error message returned from the runtime will actually be 'record not found'
@@ -1017,7 +1034,7 @@ describe("QueryBuilder", () => {
1017
1034
  );
1018
1035
  });
1019
1036
 
1020
- test("Model API chained findOne", async () => {
1037
+ test("ModelAPI chained findOne", async () => {
1021
1038
  const p = await personAPI.create({
1022
1039
  id: KSUID.randomSync().string,
1023
1040
  name: "Jake",
@@ -1058,7 +1075,7 @@ describe("QueryBuilder", () => {
1058
1075
  // expect(results[0].id).toEqual(p2);
1059
1076
  // });
1060
1077
 
1061
- test("Model API chained update", async () => {
1078
+ test("ModelAPI chained update", async () => {
1062
1079
  const p1 = await postAPI.create({
1063
1080
  id: KSUID.randomSync().string,
1064
1081
  title: "adam",
@@ -1099,4 +1116,336 @@ describe("QueryBuilder", () => {
1099
1116
  .update({ title: "bob" })
1100
1117
  ).resolves.toEqual(null);
1101
1118
  });
1119
+
1120
+ test("ModelAPI.findMany - array equals", async () => {
1121
+ const person = await personAPI.create({
1122
+ id: KSUID.randomSync().string,
1123
+ name: "Jake",
1124
+ favouriteNumber: 8,
1125
+ date: new Date("2021-12-31"),
1126
+ });
1127
+ const p1 = await postAPI.create({
1128
+ id: KSUID.randomSync().string,
1129
+ title: "Post 1",
1130
+ tags: ["Tag 1", "Tag 2"],
1131
+ authorId: person.id,
1132
+ });
1133
+ const p2 = await postAPI.create({
1134
+ id: KSUID.randomSync().string,
1135
+ title: "Post 2",
1136
+ tags: ["Tag 2", "Tag 3"],
1137
+ authorId: person.id,
1138
+ });
1139
+ const p3 = await postAPI.create({
1140
+ id: KSUID.randomSync().string,
1141
+ title: "Post 2",
1142
+ tags: [],
1143
+ authorId: person.id,
1144
+ });
1145
+ const p4 = await postAPI.create({
1146
+ id: KSUID.randomSync().string,
1147
+ title: "Post 2",
1148
+ tags: null,
1149
+ authorId: person.id,
1150
+ });
1151
+
1152
+ const rows = await postAPI.findMany({
1153
+ where: {
1154
+ tags: {
1155
+ equals: ["Tag 1", "Tag 2"],
1156
+ },
1157
+ },
1158
+ });
1159
+
1160
+ expect(rows.length).toEqual(1);
1161
+ expect(rows.map((x) => x.id).sort()).toEqual([p1.id].sort());
1162
+ });
1163
+
1164
+ test("ModelAPI.findMany - array equals implicit", async () => {
1165
+ const person = await personAPI.create({
1166
+ id: KSUID.randomSync().string,
1167
+ name: "Jake",
1168
+ favouriteNumber: 8,
1169
+ date: new Date("2021-12-31"),
1170
+ });
1171
+ const p1 = await postAPI.create({
1172
+ id: KSUID.randomSync().string,
1173
+ title: "Post 1",
1174
+ tags: ["Tag 1", "Tag 2"],
1175
+ authorId: person.id,
1176
+ });
1177
+ const p2 = await postAPI.create({
1178
+ id: KSUID.randomSync().string,
1179
+ title: "Post 2",
1180
+ tags: ["Tag 2", "Tag 3"],
1181
+ authorId: person.id,
1182
+ });
1183
+ const p3 = await postAPI.create({
1184
+ id: KSUID.randomSync().string,
1185
+ title: "Post 2",
1186
+ tags: [],
1187
+ authorId: person.id,
1188
+ });
1189
+
1190
+ const rows = await postAPI.findMany({
1191
+ where: {
1192
+ tags: ["Tag 1", "Tag 2"],
1193
+ },
1194
+ });
1195
+
1196
+ expect(rows.length).toEqual(1);
1197
+ expect(rows.map((x) => x.id).sort()).toEqual([p1.id].sort());
1198
+ });
1199
+
1200
+ test("ModelAPI.findMany - array not equals", async () => {
1201
+ const person = await personAPI.create({
1202
+ id: KSUID.randomSync().string,
1203
+ name: "Jake",
1204
+ favouriteNumber: 8,
1205
+ date: new Date("2021-12-31"),
1206
+ });
1207
+ const p1 = await postAPI.create({
1208
+ id: KSUID.randomSync().string,
1209
+ title: "Post 1",
1210
+ tags: ["Tag 1", "Tag 2"],
1211
+ authorId: person.id,
1212
+ });
1213
+ const p2 = await postAPI.create({
1214
+ id: KSUID.randomSync().string,
1215
+ title: "Post 2",
1216
+ tags: ["Tag 2", "Tag 3"],
1217
+ authorId: person.id,
1218
+ });
1219
+ const p3 = await postAPI.create({
1220
+ id: KSUID.randomSync().string,
1221
+ title: "Post 2",
1222
+ tags: [],
1223
+ authorId: person.id,
1224
+ });
1225
+ const p4 = await postAPI.create({
1226
+ id: KSUID.randomSync().string,
1227
+ title: "Post 2",
1228
+ tags: null,
1229
+ authorId: person.id,
1230
+ });
1231
+
1232
+ const rows = await postAPI.findMany({
1233
+ where: {
1234
+ tags: {
1235
+ notEquals: ["Tag 1", "Tag 2"],
1236
+ },
1237
+ },
1238
+ });
1239
+
1240
+ expect(rows.length).toEqual(3);
1241
+ expect(rows.map((x) => x.id).sort()).toEqual([p4.id, p3.id, p2.id].sort());
1242
+ });
1243
+
1244
+ test("ModelAPI.findMany - array any equals", async () => {
1245
+ const person = await personAPI.create({
1246
+ id: KSUID.randomSync().string,
1247
+ name: "Jake",
1248
+ favouriteNumber: 8,
1249
+ date: new Date("2021-12-31"),
1250
+ });
1251
+ const p1 = await postAPI.create({
1252
+ id: KSUID.randomSync().string,
1253
+ title: "Post 1",
1254
+ tags: ["Tag 1", "Tag 2"],
1255
+ authorId: person.id,
1256
+ });
1257
+ const p2 = await postAPI.create({
1258
+ id: KSUID.randomSync().string,
1259
+ title: "Post 2",
1260
+ tags: ["Tag 2", "Tag 3"],
1261
+ authorId: person.id,
1262
+ });
1263
+ const p3 = await postAPI.create({
1264
+ id: KSUID.randomSync().string,
1265
+ title: "Post 2",
1266
+ tags: [],
1267
+ authorId: person.id,
1268
+ });
1269
+ const p4 = await postAPI.create({
1270
+ id: KSUID.randomSync().string,
1271
+ title: "Post 2",
1272
+ tags: null,
1273
+ authorId: person.id,
1274
+ });
1275
+ const p5 = await postAPI.create({
1276
+ id: KSUID.randomSync().string,
1277
+ title: "Post 2",
1278
+ tags: ["Tag 3"],
1279
+ authorId: person.id,
1280
+ });
1281
+
1282
+ const rows = await postAPI.findMany({
1283
+ where: {
1284
+ tags: {
1285
+ any: {
1286
+ equals: "Tag 2",
1287
+ },
1288
+ },
1289
+ },
1290
+ });
1291
+
1292
+ expect(rows.length).toEqual(2);
1293
+ expect(rows.map((x) => x.id).sort()).toEqual([p1.id, p2.id].sort());
1294
+ });
1295
+
1296
+ test("ModelAPI.findMany - array any not equals", async () => {
1297
+ const person = await personAPI.create({
1298
+ id: KSUID.randomSync().string,
1299
+ name: "Jake",
1300
+ favouriteNumber: 8,
1301
+ date: new Date("2021-12-31"),
1302
+ });
1303
+ const p1 = await postAPI.create({
1304
+ id: KSUID.randomSync().string,
1305
+ title: "Post 1",
1306
+ tags: ["Tag 1", "Tag 2"],
1307
+ authorId: person.id,
1308
+ });
1309
+ const p2 = await postAPI.create({
1310
+ id: KSUID.randomSync().string,
1311
+ title: "Post 2",
1312
+ tags: ["Tag 2", "Tag 3"],
1313
+ authorId: person.id,
1314
+ });
1315
+ const p3 = await postAPI.create({
1316
+ id: KSUID.randomSync().string,
1317
+ title: "Post 2",
1318
+ tags: [],
1319
+ authorId: person.id,
1320
+ });
1321
+ const p4 = await postAPI.create({
1322
+ id: KSUID.randomSync().string,
1323
+ title: "Post 2",
1324
+ tags: null,
1325
+ authorId: person.id,
1326
+ });
1327
+ const p5 = await postAPI.create({
1328
+ id: KSUID.randomSync().string,
1329
+ title: "Post 2",
1330
+ tags: ["Tag 3"],
1331
+ authorId: person.id,
1332
+ });
1333
+
1334
+ const rows = await postAPI.findMany({
1335
+ where: {
1336
+ tags: {
1337
+ any: {
1338
+ notEquals: "Tag 3",
1339
+ },
1340
+ },
1341
+ },
1342
+ });
1343
+
1344
+ expect(rows.length).toEqual(2);
1345
+ expect(rows.map((x) => x.id).sort()).toEqual([p1.id, p3.id].sort());
1346
+ });
1347
+
1348
+ test("ModelAPI.findMany - array all equals", async () => {
1349
+ const person = await personAPI.create({
1350
+ id: KSUID.randomSync().string,
1351
+ name: "Jake",
1352
+ favouriteNumber: 8,
1353
+ date: new Date("2021-12-31"),
1354
+ });
1355
+ const p1 = await postAPI.create({
1356
+ id: KSUID.randomSync().string,
1357
+ title: "Post 1",
1358
+ tags: ["Tag 1", "Tag 2"],
1359
+ authorId: person.id,
1360
+ });
1361
+ const p2 = await postAPI.create({
1362
+ id: KSUID.randomSync().string,
1363
+ title: "Post 2",
1364
+ tags: ["Tag 2", "Tag 3"],
1365
+ authorId: person.id,
1366
+ });
1367
+ const p3 = await postAPI.create({
1368
+ id: KSUID.randomSync().string,
1369
+ title: "Post 2",
1370
+ tags: [],
1371
+ authorId: person.id,
1372
+ });
1373
+ const p4 = await postAPI.create({
1374
+ id: KSUID.randomSync().string,
1375
+ title: "Post 2",
1376
+ tags: null,
1377
+ authorId: person.id,
1378
+ });
1379
+ const p5 = await postAPI.create({
1380
+ id: KSUID.randomSync().string,
1381
+ title: "Post 2",
1382
+ tags: ["Tag 2", "Tag 2"],
1383
+ authorId: person.id,
1384
+ });
1385
+
1386
+ const rows = await postAPI.findMany({
1387
+ where: {
1388
+ tags: {
1389
+ all: {
1390
+ equals: "Tag 2",
1391
+ },
1392
+ },
1393
+ },
1394
+ });
1395
+
1396
+ expect(rows.length).toEqual(2);
1397
+ expect(rows.map((x) => x.id).sort()).toEqual([p3.id, p5.id].sort());
1398
+ });
1399
+
1400
+ test("ModelAPI.findMany - array all not equals", async () => {
1401
+ const person = await personAPI.create({
1402
+ id: KSUID.randomSync().string,
1403
+ name: "Jake",
1404
+ favouriteNumber: 8,
1405
+ date: new Date("2021-12-31"),
1406
+ });
1407
+ const p1 = await postAPI.create({
1408
+ id: KSUID.randomSync().string,
1409
+ title: "Post 1",
1410
+ tags: ["Tag 1", "Tag 2"],
1411
+ authorId: person.id,
1412
+ });
1413
+ const p2 = await postAPI.create({
1414
+ id: KSUID.randomSync().string,
1415
+ title: "Post 2",
1416
+ tags: ["Tag 2", "Tag 3"],
1417
+ authorId: person.id,
1418
+ });
1419
+ const p3 = await postAPI.create({
1420
+ id: KSUID.randomSync().string,
1421
+ title: "Post 2",
1422
+ tags: [],
1423
+ authorId: person.id,
1424
+ });
1425
+ const p4 = await postAPI.create({
1426
+ id: KSUID.randomSync().string,
1427
+ title: "Post 2",
1428
+ tags: null,
1429
+ authorId: person.id,
1430
+ });
1431
+ const p5 = await postAPI.create({
1432
+ id: KSUID.randomSync().string,
1433
+ title: "Post 2",
1434
+ tags: ["Tag 2", "Tag 2"],
1435
+ authorId: person.id,
1436
+ });
1437
+
1438
+ const rows = await postAPI.findMany({
1439
+ where: {
1440
+ tags: {
1441
+ all: {
1442
+ notEquals: "Tag 2",
1443
+ },
1444
+ },
1445
+ },
1446
+ });
1447
+
1448
+ expect(rows.length).toEqual(2);
1449
+ expect(rows.map((x) => x.id).sort()).toEqual([p1.id, p2.id].sort());
1450
+ });
1102
1451
  });
@@ -1,11 +1,11 @@
1
- const { sql } = require("kysely");
1
+ const { sql, Kysely } = require("kysely");
2
2
  const { snakeCase } = require("./casing");
3
3
 
4
4
  const opMapping = {
5
5
  startsWith: { op: "like", value: (v) => `${v}%` },
6
6
  endsWith: { op: "like", value: (v) => `%${v}` },
7
7
  contains: { op: "like", value: (v) => `%${v}%` },
8
- oneOf: { op: "in" },
8
+ oneOf: { op: "=", value: (v) => sql`ANY(${v})` },
9
9
  greaterThan: { op: ">" },
10
10
  greaterThanOrEquals: { op: ">=" },
11
11
  lessThan: { op: "<" },
@@ -16,6 +16,32 @@ const opMapping = {
16
16
  onOrAfter: { op: ">=" },
17
17
  equals: { op: sql`is not distinct from` },
18
18
  notEquals: { op: sql`is distinct from` },
19
+ any: {
20
+ isArrayQuery: true,
21
+ greaterThan: { op: ">" },
22
+ greaterThanOrEquals: { op: ">=" },
23
+ lessThan: { op: "<" },
24
+ lessThanOrEquals: { op: "<=" },
25
+ before: { op: "<" },
26
+ onOrBefore: { op: "<=" },
27
+ after: { op: ">" },
28
+ onOrAfter: { op: ">=" },
29
+ equals: { op: "=" },
30
+ notEquals: { op: "=", value: (v) => sql`NOT ${v}` },
31
+ },
32
+ all: {
33
+ isArrayQuery: true,
34
+ greaterThan: { op: ">" },
35
+ greaterThanOrEquals: { op: ">=" },
36
+ lessThan: { op: "<" },
37
+ lessThanOrEquals: { op: "<=" },
38
+ before: { op: "<" },
39
+ onOrBefore: { op: "<=" },
40
+ after: { op: ">" },
41
+ onOrAfter: { op: ">=" },
42
+ equals: { op: "=" },
43
+ notEquals: { op: "=", value: (v) => sql`NOT ${v}` },
44
+ },
19
45
  };
20
46
 
21
47
  /**
@@ -44,7 +70,7 @@ function applyWhereConditions(context, qb, where = {}) {
44
70
  const fieldName = `${context.tableAlias()}.${snakeCase(key)}`;
45
71
 
46
72
  if (Object.prototype.toString.call(v) !== "[object Object]") {
47
- qb = qb.where(fieldName, "=", v);
73
+ qb = qb.where(fieldName, sql`is not distinct from`, sql`${v}`);
48
74
  continue;
49
75
  }
50
76
 
@@ -54,11 +80,23 @@ function applyWhereConditions(context, qb, where = {}) {
54
80
  throw new Error(`invalid where condition: ${op}`);
55
81
  }
56
82
 
57
- qb = qb.where(
58
- fieldName,
59
- mapping.op,
60
- mapping.value ? mapping.value(v[op]) : v[op]
61
- );
83
+ if (mapping.isArrayQuery) {
84
+ for (const arrayOp of Object.keys(v[op])) {
85
+ qb = qb.where(
86
+ mapping[arrayOp].value
87
+ ? mapping[arrayOp].value(v[op][arrayOp])
88
+ : sql`${v[op][arrayOp]}`,
89
+ mapping[arrayOp].op,
90
+ sql`${sql(op)}(${sql.ref(fieldName)})`
91
+ );
92
+ }
93
+ } else {
94
+ qb = qb.where(
95
+ fieldName,
96
+ mapping.op,
97
+ mapping.value ? mapping.value(v[op]) : sql`${v[op]}`
98
+ );
99
+ }
62
100
  }
63
101
  }
64
102
 
package/src/index.d.ts CHANGED
@@ -27,7 +27,6 @@ export type NumberWhereCondition = {
27
27
  notEquals?: number | null;
28
28
  };
29
29
 
30
- // Date database API
31
30
  export type DateWhereCondition = {
32
31
  equals?: Date | string | null;
33
32
  before?: Date | string | null;
@@ -36,7 +35,6 @@ export type DateWhereCondition = {
36
35
  onOrAfter?: Date | string | null;
37
36
  };
38
37
 
39
- // Date query input
40
38
  export type DateQueryInput = {
41
39
  equals?: string | null;
42
40
  before?: string | null;
@@ -45,12 +43,67 @@ export type DateQueryInput = {
45
43
  onOrAfter?: string | null;
46
44
  };
47
45
 
48
- // Timestamp query input
49
46
  export type TimestampQueryInput = {
50
47
  before: string | null;
51
48
  after: string | null;
52
49
  };
53
50
 
51
+ export type StringArrayWhereCondition = {
52
+ equals?: string[] | null;
53
+ notEquals?: string[] | null;
54
+ any?: StringArrayQueryWhereCondition | null;
55
+ all?: StringArrayQueryWhereCondition | null;
56
+ };
57
+
58
+ export type StringArrayQueryWhereCondition = {
59
+ equals?: string | null;
60
+ notEquals?: string | null;
61
+ };
62
+
63
+ export type NumberArrayWhereCondition = {
64
+ equals?: number[] | null;
65
+ notEquals?: number[] | null;
66
+ any?: NumberArrayQueryWhereCondition | null;
67
+ all?: NumberArrayQueryWhereCondition | null;
68
+ };
69
+
70
+ export type NumberArrayQueryWhereCondition = {
71
+ greaterThan?: number | null;
72
+ greaterThanOrEquals?: number | null;
73
+ lessThan?: number | null;
74
+ lessThanOrEquals?: number | null;
75
+ equals?: number | null;
76
+ notEquals?: number | null;
77
+ };
78
+
79
+ export type BooleanArrayWhereCondition = {
80
+ equals?: bool[] | null;
81
+ notEquals?: bool[] | null;
82
+ any?: BooleanArrayQueryWhereCondition | null;
83
+ all?: BooleanArrayQueryWhereCondition | null;
84
+ };
85
+
86
+ export type BooleanArrayQueryWhereCondition = {
87
+ equals?: bool | null;
88
+ notEquals?: bool | null;
89
+ };
90
+
91
+ export type DateArrayWhereCondition = {
92
+ equals?: Date[] | null;
93
+ notEquals?: Date[] | null;
94
+ any?: DateArrayQueryWhereCondition | null;
95
+ all?: DateArrayQueryWhereCondition | null;
96
+ };
97
+
98
+ export type DateArrayQueryWhereCondition = {
99
+ greaterThan?: Date | null;
100
+ greaterThanOrEquals?: Date | null;
101
+ lessThan?: Date | null;
102
+ lessThanOrEquals?: number | null;
103
+ equals?: Date | null;
104
+ notEquals?: Date | null;
105
+ };
106
+
54
107
  export type ContextAPI = {
55
108
  headers: RequestHeaders;
56
109
  response: Response;