@teamkeel/functions-runtime 0.373.0 → 0.374.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,10 +1,10 @@
1
1
  {
2
2
  "name": "@teamkeel/functions-runtime",
3
- "version": "0.373.0",
3
+ "version": "0.374.0",
4
4
  "description": "Internal package used by @teamkeel/sdk",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
- "test": "DEBUG=true vitest run --reporter verbose --threads false",
7
+ "test": "vitest run --reporter verbose --threads false",
8
8
  "format": "npx prettier --write src/**/*.js"
9
9
  },
10
10
  "keywords": [],
package/src/ModelAPI.js CHANGED
@@ -1,3 +1,4 @@
1
+ const { sql } = require("kysely");
1
2
  const { useDatabase } = require("./database");
2
3
  const { QueryBuilder } = require("./QueryBuilder");
3
4
  const { QueryContext } = require("./QueryContext");
@@ -50,26 +51,10 @@ class ModelAPI {
50
51
 
51
52
  async create(values) {
52
53
  const name = tracing.spanNameForModelAPI(this._modelName, "create");
53
- const db = useDatabase();
54
54
 
55
- return tracing.withSpan(name, async (span) => {
56
- try {
57
- const query = db
58
- .insertInto(this._tableName)
59
- .values(
60
- snakeCaseObject({
61
- ...values,
62
- })
63
- )
64
- .returningAll();
65
-
66
- span.setAttribute("sql", query.compile().sql);
67
- const row = await query.executeTakeFirstOrThrow();
68
-
69
- return camelCaseObject(row);
70
- } catch (e) {
71
- throw new DatabaseError(e);
72
- }
55
+ return tracing.withSpan(name, () => {
56
+ const db = useDatabase();
57
+ return create(db, this._tableName, this._tableConfigMap, values);
73
58
  });
74
59
  }
75
60
 
@@ -218,6 +203,111 @@ class ModelAPI {
218
203
  }
219
204
  }
220
205
 
206
+ async function create(conn, tableName, tableConfigs, values) {
207
+ try {
208
+ let query = conn.insertInto(tableName);
209
+
210
+ const keys = values ? Object.keys(values) : [];
211
+ const tableConfig = tableConfigs[tableName] || {};
212
+ const hasManyRecords = [];
213
+
214
+ if (keys.length === 0) {
215
+ // See https://github.com/kysely-org/kysely/issues/685#issuecomment-1711240534
216
+ query = query.expression(sql`default values`);
217
+ } else {
218
+ const row = {};
219
+ for (const key of keys) {
220
+ const value = values[key];
221
+ const columnConfig = tableConfig[key];
222
+
223
+ if (!columnConfig) {
224
+ row[key] = value;
225
+ continue;
226
+ }
227
+
228
+ switch (columnConfig.relationshipType) {
229
+ case "belongsTo":
230
+ if (!isPlainObject(value)) {
231
+ throw new Error(
232
+ `non-object provided for field ${key} of ${tableName}`
233
+ );
234
+ }
235
+
236
+ if (isReferencingExistingRecord(value)) {
237
+ row[columnConfig.foreignKey] = value.id;
238
+ break;
239
+ }
240
+
241
+ const created = await create(
242
+ conn,
243
+ columnConfig.referencesTable,
244
+ tableConfigs,
245
+ value
246
+ );
247
+ row[columnConfig.foreignKey] = created.id;
248
+ break;
249
+
250
+ case "hasMany":
251
+ if (!Array.isArray(value)) {
252
+ throw new Error(
253
+ `non-array provided for has-many field ${key} of ${tableName}`
254
+ );
255
+ }
256
+ for (const v of value) {
257
+ hasManyRecords.push({
258
+ key,
259
+ value: v,
260
+ columnConfig,
261
+ });
262
+ }
263
+ break;
264
+ default:
265
+ throw new Error(
266
+ `unsupported relationship type - ${tableName}.${key} (${columnConfig.relationshipType})`
267
+ );
268
+ }
269
+ }
270
+
271
+ query = query.values(row);
272
+ }
273
+
274
+ const created = await query.returningAll().executeTakeFirstOrThrow();
275
+
276
+ await Promise.all(
277
+ hasManyRecords.map(async ({ key, value, columnConfig }) => {
278
+ if (!isPlainObject(value)) {
279
+ throw new Error(
280
+ `non-object provided for field ${key} of ${tableName}`
281
+ );
282
+ }
283
+
284
+ if (isReferencingExistingRecord(value)) {
285
+ throw new Error(
286
+ `nested update as part of create not supported for ${key} of ${tableConfig}`
287
+ );
288
+ }
289
+
290
+ await create(conn, columnConfig.referencesTable, tableConfigs, {
291
+ ...value,
292
+ [columnConfig.foreignKey]: created.id,
293
+ });
294
+ })
295
+ );
296
+
297
+ return created;
298
+ } catch (e) {
299
+ throw new DatabaseError(e);
300
+ }
301
+ }
302
+
303
+ function isPlainObject(obj) {
304
+ return Object.prototype.toString.call(obj) === "[object Object]";
305
+ }
306
+
307
+ function isReferencingExistingRecord(value) {
308
+ return Object.keys(value).length === 1 && value.id;
309
+ }
310
+
221
311
  module.exports = {
222
312
  ModelAPI,
223
313
  DatabaseError,
package/src/tracing.js CHANGED
@@ -119,6 +119,14 @@ function init() {
119
119
  patchConsoleLog();
120
120
  }
121
121
 
122
+ async function forceFlush() {
123
+ // The "delegate" is the actual provider set by the functions-runtime package
124
+ const provider = opentelemetry.trace.getTracerProvider().getDelegate();
125
+ if (provider && provider.forceFlush) {
126
+ await provider.forceFlush();
127
+ }
128
+ }
129
+
122
130
  function getTracer() {
123
131
  return opentelemetry.trace.getTracer("functions");
124
132
  }
@@ -131,5 +139,6 @@ module.exports = {
131
139
  getTracer,
132
140
  withSpan,
133
141
  init,
142
+ forceFlush,
134
143
  spanNameForModelAPI,
135
144
  };
@@ -33,7 +33,10 @@ test("withSpan span time", async () => {
33
33
 
34
34
  expect(spanEvents.map((e) => e.event)).toEqual(["onStart", "onEnd"]);
35
35
  const spanDuration = spanEvents.pop().span._duration.pop();
36
- const waitTimeNanos = waitTimeMillis * 1000 * 1000;
36
+
37
+ // The '- 1' here is because sometimes the test fails due to the span duration
38
+ // being something like 99.87ms. As long as it's at least 99ms we're happy
39
+ const waitTimeNanos = (waitTimeMillis - 1) * 1000 * 1000;
37
40
  expect(spanDuration).toBeGreaterThan(waitTimeNanos);
38
41
  });
39
42