@budibase/backend-core 2.30.3 → 2.30.4
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/dist/index.js +113 -89
- package/dist/index.js.map +3 -3
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +4 -4
- package/dist/plugins.js +1 -0
- package/dist/plugins.js.map +2 -2
- package/dist/plugins.js.meta.json +1 -1
- package/dist/src/context/mainContext.d.ts +2 -0
- package/dist/src/context/mainContext.js +19 -0
- package/dist/src/context/mainContext.js.map +1 -1
- package/dist/src/context/types.d.ts +3 -0
- package/dist/src/features/index.d.ts +3 -4
- package/dist/src/features/index.js +15 -10
- package/dist/src/features/index.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/package.json +4 -4
- package/src/context/mainContext.ts +19 -0
- package/src/context/tests/index.spec.ts +1 -1
- package/src/context/types.ts +3 -0
- package/src/features/index.ts +24 -12
- package/src/features/tests/features.spec.ts +6 -4
- package/src/redis/tests/redis.spec.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/backend-core",
|
|
3
|
-
"version": "2.30.
|
|
3
|
+
"version": "2.30.4",
|
|
4
4
|
"description": "Budibase backend core libraries used in server and worker",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@budibase/nano": "10.1.5",
|
|
25
25
|
"@budibase/pouchdb-replication-stream": "1.2.11",
|
|
26
|
-
"@budibase/shared-core": "2.30.
|
|
27
|
-
"@budibase/types": "2.30.
|
|
26
|
+
"@budibase/shared-core": "2.30.4",
|
|
27
|
+
"@budibase/types": "2.30.4",
|
|
28
28
|
"aws-cloudfront-sign": "3.0.2",
|
|
29
29
|
"aws-sdk": "2.1030.0",
|
|
30
30
|
"bcrypt": "5.1.0",
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
|
-
"gitHead": "
|
|
98
|
+
"gitHead": "31ecd970317f72024de072a54e4d6759333e2b80"
|
|
99
99
|
}
|
|
@@ -375,3 +375,22 @@ export function getCurrentContext(): ContextMap | undefined {
|
|
|
375
375
|
return undefined
|
|
376
376
|
}
|
|
377
377
|
}
|
|
378
|
+
|
|
379
|
+
export function getFeatureFlags<T extends Record<string, any>>(
|
|
380
|
+
key: string
|
|
381
|
+
): T | undefined {
|
|
382
|
+
const context = getCurrentContext()
|
|
383
|
+
if (!context) {
|
|
384
|
+
return undefined
|
|
385
|
+
}
|
|
386
|
+
return context.featureFlagCache?.[key] as T
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function setFeatureFlags(key: string, value: Record<string, any>) {
|
|
390
|
+
const context = getCurrentContext()
|
|
391
|
+
if (!context) {
|
|
392
|
+
return
|
|
393
|
+
}
|
|
394
|
+
context.featureFlagCache ??= {}
|
|
395
|
+
context.featureFlagCache[key] = value
|
|
396
|
+
}
|
|
@@ -2,7 +2,7 @@ import { testEnv } from "../../../tests/extra"
|
|
|
2
2
|
import * as context from "../"
|
|
3
3
|
import { DEFAULT_TENANT_ID } from "../../constants"
|
|
4
4
|
import { structures } from "../../../tests"
|
|
5
|
-
import
|
|
5
|
+
import * as db from "../../db"
|
|
6
6
|
import Context from "../Context"
|
|
7
7
|
import { ContextMap } from "../types"
|
|
8
8
|
import { IdentityType } from "@budibase/types"
|
package/src/context/types.ts
CHANGED
package/src/features/index.ts
CHANGED
|
@@ -18,6 +18,10 @@ export function init(opts?: PostHogOptions) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export function shutdown() {
|
|
22
|
+
posthog?.shutdown()
|
|
23
|
+
}
|
|
24
|
+
|
|
21
25
|
export abstract class Flag<T> {
|
|
22
26
|
static boolean(defaultValue: boolean): Flag<boolean> {
|
|
23
27
|
return new BooleanFlag(defaultValue)
|
|
@@ -87,7 +91,14 @@ class NumberFlag extends Flag<number> {
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
90
|
-
|
|
94
|
+
// This is used to safely cache flags sets in the current request context.
|
|
95
|
+
// Because multiple sets could theoretically exist, we don't want the cache of
|
|
96
|
+
// one to leak into another.
|
|
97
|
+
private readonly setId: string
|
|
98
|
+
|
|
99
|
+
constructor(private readonly flagSchema: T) {
|
|
100
|
+
this.setId = crypto.randomUUID()
|
|
101
|
+
}
|
|
91
102
|
|
|
92
103
|
defaults(): FlagValues<T> {
|
|
93
104
|
return Object.keys(this.flagSchema).reduce((acc, key) => {
|
|
@@ -119,6 +130,12 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
|
119
130
|
|
|
120
131
|
async fetch(ctx?: UserCtx): Promise<FlagValues<T>> {
|
|
121
132
|
return await tracer.trace("features.fetch", async span => {
|
|
133
|
+
const cachedFlags = context.getFeatureFlags<FlagValues<T>>(this.setId)
|
|
134
|
+
if (cachedFlags) {
|
|
135
|
+
span?.addTags({ fromCache: true })
|
|
136
|
+
return cachedFlags
|
|
137
|
+
}
|
|
138
|
+
|
|
122
139
|
const tags: Record<string, any> = {}
|
|
123
140
|
const flagValues = this.defaults()
|
|
124
141
|
const currentTenantId = context.getTenantId()
|
|
@@ -142,8 +159,9 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
|
142
159
|
specificallySetFalse.add(feature)
|
|
143
160
|
}
|
|
144
161
|
|
|
162
|
+
// ignore unknown flags
|
|
145
163
|
if (!this.isFlagName(feature)) {
|
|
146
|
-
|
|
164
|
+
continue
|
|
147
165
|
}
|
|
148
166
|
|
|
149
167
|
if (typeof flagValues[feature] !== "boolean") {
|
|
@@ -152,7 +170,7 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
|
152
170
|
|
|
153
171
|
// @ts-expect-error - TS does not like you writing into a generic type,
|
|
154
172
|
// but we know that it's okay in this case because it's just an object.
|
|
155
|
-
flagValues[feature] = value
|
|
173
|
+
flagValues[feature as keyof FlagValues] = value
|
|
156
174
|
tags[`flags.${feature}.source`] = "environment"
|
|
157
175
|
}
|
|
158
176
|
}
|
|
@@ -187,10 +205,7 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
|
187
205
|
tags[`identity.tenantId`] = identity?.tenantId
|
|
188
206
|
tags[`identity._id`] = identity?._id
|
|
189
207
|
|
|
190
|
-
|
|
191
|
-
// and test environments.
|
|
192
|
-
const usePosthog = env.isTest() || env.isQA()
|
|
193
|
-
if (usePosthog && posthog && identity?.type === IdentityType.USER) {
|
|
208
|
+
if (posthog && identity?.type === IdentityType.USER) {
|
|
194
209
|
tags[`readFromPostHog`] = true
|
|
195
210
|
|
|
196
211
|
const personProperties: Record<string, string> = {}
|
|
@@ -204,7 +219,6 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
|
204
219
|
personProperties,
|
|
205
220
|
}
|
|
206
221
|
)
|
|
207
|
-
console.log("posthog flags", JSON.stringify(posthogFlags))
|
|
208
222
|
|
|
209
223
|
for (const [name, value] of Object.entries(posthogFlags.featureFlags)) {
|
|
210
224
|
if (!this.isFlagName(name)) {
|
|
@@ -236,6 +250,7 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
|
236
250
|
}
|
|
237
251
|
}
|
|
238
252
|
|
|
253
|
+
context.setFeatureFlags(this.setId, flagValues)
|
|
239
254
|
for (const [key, value] of Object.entries(flagValues)) {
|
|
240
255
|
tags[`flags.${key}.value`] = value
|
|
241
256
|
}
|
|
@@ -251,8 +266,5 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
|
|
|
251
266
|
// All of the machinery in this file is to make sure that flags have their
|
|
252
267
|
// default values set correctly and their types flow through the system.
|
|
253
268
|
export const flags = new FlagSet({
|
|
254
|
-
|
|
255
|
-
GOOGLE_SHEETS: Flag.boolean(false),
|
|
256
|
-
USER_GROUPS: Flag.boolean(false),
|
|
257
|
-
ONBOARDING_TOUR: Flag.boolean(false),
|
|
269
|
+
DEFAULT_VALUES: Flag.boolean(false),
|
|
258
270
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IdentityContext, IdentityType, UserCtx } from "@budibase/types"
|
|
2
|
-
import { Flag, FlagSet, FlagValues, init } from "../"
|
|
3
|
-
import
|
|
2
|
+
import { Flag, FlagSet, FlagValues, init, shutdown } from "../"
|
|
3
|
+
import * as context from "../../context"
|
|
4
4
|
import environment, { withEnv } from "../../environment"
|
|
5
5
|
import nodeFetch from "node-fetch"
|
|
6
6
|
import nock from "nock"
|
|
@@ -67,9 +67,9 @@ describe("feature flags", () => {
|
|
|
67
67
|
expected: flags.defaults(),
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
|
-
it: "should
|
|
70
|
+
it: "should ignore unknown feature flags",
|
|
71
71
|
environmentFlags: "default:TEST_BOOLEAN,default:FOO",
|
|
72
|
-
|
|
72
|
+
expected: { TEST_BOOLEAN: true },
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
it: "should be able to read boolean flags from PostHog",
|
|
@@ -197,6 +197,8 @@ describe("feature flags", () => {
|
|
|
197
197
|
throw new Error("No expected value")
|
|
198
198
|
}
|
|
199
199
|
})
|
|
200
|
+
|
|
201
|
+
shutdown()
|
|
200
202
|
})
|
|
201
203
|
}
|
|
202
204
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GenericContainer, StartedTestContainer } from "testcontainers"
|
|
2
2
|
import { generator, structures } from "../../../tests"
|
|
3
3
|
import RedisWrapper, { closeAll } from "../redis"
|
|
4
|
-
import
|
|
4
|
+
import env from "../../environment"
|
|
5
5
|
import { randomUUID } from "crypto"
|
|
6
6
|
|
|
7
7
|
jest.setTimeout(30000)
|