@teamkeel/functions-runtime 0.313.7 → 0.314.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/src/ModelAPI.js CHANGED
@@ -4,6 +4,7 @@ const { QueryContext } = require("./QueryContext");
4
4
  const { applyWhereConditions } = require("./applyWhereConditions");
5
5
  const { applyJoins } = require("./applyJoins");
6
6
  const { camelCaseObject, snakeCaseObject } = require("./casing");
7
+ const tracing = require("./tracing");
7
8
 
8
9
  /**
9
10
  * RelationshipConfig is a simple representation of a model field that
@@ -48,7 +49,7 @@ class ModelAPI {
48
49
  async create(values) {
49
50
  try {
50
51
  const defaults = this._defaultValues();
51
- const row = await this._db
52
+ const query = this._db
52
53
  .insertInto(this._tableName)
53
54
  .values(
54
55
  snakeCaseObject({
@@ -56,8 +57,15 @@ class ModelAPI {
56
57
  ...values,
57
58
  })
58
59
  )
59
- .returningAll()
60
- .executeTakeFirstOrThrow();
60
+ .returningAll();
61
+ const sql = query.compile().sql;
62
+ const row = await tracing.withSpan(
63
+ `${this._tableName}.create`,
64
+ (span) => {
65
+ span.setAttribute("sql", sql);
66
+ return query.executeTakeFirstOrThrow();
67
+ }
68
+ );
61
69
 
62
70
  return camelCaseObject(row);
63
71
  } catch (e) {
@@ -76,7 +84,11 @@ class ModelAPI {
76
84
  builder = applyJoins(context, builder, where);
77
85
  builder = applyWhereConditions(context, builder, where);
78
86
 
79
- const row = await builder.executeTakeFirst();
87
+ const sql = builder.compile().sql;
88
+ const row = await tracing.withSpan(`${this._tableName}.findOne`, (span) => {
89
+ span.setAttribute("sql", sql);
90
+ return builder.executeTakeFirst();
91
+ });
80
92
  if (!row) {
81
93
  return null;
82
94
  }
@@ -94,8 +106,16 @@ class ModelAPI {
94
106
 
95
107
  builder = applyJoins(context, builder, where);
96
108
  builder = applyWhereConditions(context, builder, where);
97
-
98
- const rows = await builder.orderBy("id").execute();
109
+ const query = builder.orderBy("id");
110
+
111
+ const sql = query.compile().sql;
112
+ const rows = await tracing.withSpan(
113
+ `${this._tableName}.findMany`,
114
+ (span) => {
115
+ span.setAttribute("sql", sql);
116
+ return builder.execute();
117
+ }
118
+ );
99
119
  return rows.map((x) => camelCaseObject(x));
100
120
  }
101
121
 
@@ -110,7 +130,14 @@ class ModelAPI {
110
130
  builder = applyWhereConditions(context, builder, where);
111
131
 
112
132
  try {
113
- const row = await builder.executeTakeFirstOrThrow();
133
+ const sql = builder.compile().sql;
134
+ const row = await tracing.withSpan(
135
+ `${this._tableName}.update`,
136
+ (span) => {
137
+ span.setAttribute("sql", sql);
138
+ return builder.executeTakeFirstOrThrow();
139
+ }
140
+ );
114
141
 
115
142
  return camelCaseObject(row);
116
143
  } catch (e) {
@@ -127,7 +154,14 @@ class ModelAPI {
127
154
  builder = applyWhereConditions(context, builder, where);
128
155
 
129
156
  try {
130
- const row = await builder.executeTakeFirstOrThrow();
157
+ const sql = builder.compile().sql;
158
+ const row = await tracing.withSpan(
159
+ `${this._tableName}.delete`,
160
+ (span) => {
161
+ span.setAttribute("sql", sql);
162
+ return builder.executeTakeFirstOrThrow();
163
+ }
164
+ );
131
165
 
132
166
  return row.id;
133
167
  } catch (e) {
@@ -1,6 +1,7 @@
1
1
  const { applyWhereConditions } = require("./applyWhereConditions");
2
2
  const { applyJoins } = require("./applyJoins");
3
3
  const { camelCaseObject } = require("./casing");
4
+ const tracing = require("./tracing");
4
5
 
5
6
  class QueryBuilder {
6
7
  /**
@@ -34,7 +35,12 @@ class QueryBuilder {
34
35
  }
35
36
 
36
37
  async findMany() {
37
- const rows = await this._db.orderBy("id").execute();
38
+ const query = this._db.orderBy("id");
39
+ const sql = query.compile().sql;
40
+ const rows = await tracing.withSpan(`query`, (span) => {
41
+ span.setAttribute("sql", sql);
42
+ return query.execute();
43
+ });
38
44
  return rows.map((x) => camelCaseObject(x));
39
45
  }
40
46
  }
package/src/tracing.js ADDED
@@ -0,0 +1,30 @@
1
+ const opentelemetry = require("@opentelemetry/api");
2
+
3
+ const serviceName = "customerCustomFunctions";
4
+
5
+ const tracer = opentelemetry.trace.getTracer(serviceName);
6
+
7
+ function withSpan(name, fn) {
8
+ return tracer.startActiveSpan(name, async (span) => {
9
+ try {
10
+ // await the thing (this means we can use try/catch)
11
+ return await fn(span);
12
+ } catch (err) {
13
+ // record any errors
14
+ span.recordException(err);
15
+ span.setStatus({
16
+ code: opentelemetry.SpanStatusCode.ERROR,
17
+ message: err.message,
18
+ });
19
+ // re-throw the error
20
+ throw err;
21
+ } finally {
22
+ // make sure the span is ended
23
+ span.end();
24
+ }
25
+ });
26
+ }
27
+
28
+ module.exports = {
29
+ withSpan,
30
+ };
@@ -0,0 +1,56 @@
1
+ import { expect, test, beforeEach } from "vitest";
2
+ import tracing from "./tracing";
3
+ import { NodeTracerProvider, Span } from "@opentelemetry/sdk-trace-node";
4
+
5
+ let spanEvents = [];
6
+ const provider = new NodeTracerProvider({});
7
+ provider.addSpanProcessor({
8
+ forceFlush() {
9
+ return Promise.resolve();
10
+ },
11
+ onStart(span, parentContext) {
12
+ spanEvents.push({ event: "onStart", span, parentContext });
13
+ },
14
+ onEnd(span) {
15
+ spanEvents.push({ event: "onEnd", span });
16
+ },
17
+ shutdown() {
18
+ return Promise.resolve();
19
+ },
20
+ });
21
+ provider.register();
22
+
23
+ beforeEach(() => {
24
+ spanEvents = [];
25
+ });
26
+
27
+ test("withSpan span time", async () => {
28
+ const waitTimeMillis = 100;
29
+ await tracing.withSpan("name", async () => {
30
+ await new Promise((resolve) => setTimeout(resolve, waitTimeMillis));
31
+ });
32
+
33
+ expect(spanEvents.map((e) => e.event)).toEqual(["onStart", "onEnd"]);
34
+ const spanDuration = spanEvents.pop().span._duration.pop();
35
+ const waitTimeNanos = waitTimeMillis * 1000 * 1000;
36
+ expect(spanDuration).toBeGreaterThan(waitTimeNanos);
37
+ });
38
+
39
+ test("withSpan on error", async () => {
40
+ try {
41
+ await tracing.withSpan("name", async () => {
42
+ throw "err";
43
+ });
44
+ // previous line should have an error thrown
45
+ expect(true).toEqual(false);
46
+ } catch (e) {
47
+ expect(e).toEqual("err");
48
+ expect(spanEvents.map((e) => e.event)).toEqual(["onStart", "onEnd"]);
49
+ const lastSpanEvents = spanEvents.pop().span.events;
50
+ expect(lastSpanEvents).length(1);
51
+ expect(lastSpanEvents[0].name).toEqual("exception");
52
+ expect(lastSpanEvents[0].attributes).toEqual({
53
+ "exception.message": "err",
54
+ });
55
+ }
56
+ });