@budibase/backend-core 3.2.4 → 3.2.6
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.map +1 -1
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +11 -4
- package/dist/plugins.js.meta.json +1 -1
- package/package.json +11 -4
- package/src/accounts/accounts.ts +0 -82
- package/src/accounts/api.ts +0 -59
- package/src/accounts/index.ts +0 -1
- package/src/auth/auth.ts +0 -210
- package/src/auth/index.ts +0 -1
- package/src/auth/tests/auth.spec.ts +0 -14
- package/src/blacklist/blacklist.ts +0 -54
- package/src/blacklist/index.ts +0 -1
- package/src/blacklist/tests/blacklist.spec.ts +0 -46
- package/src/cache/appMetadata.ts +0 -88
- package/src/cache/base/index.ts +0 -150
- package/src/cache/docWritethrough.ts +0 -105
- package/src/cache/generic.ts +0 -33
- package/src/cache/index.ts +0 -8
- package/src/cache/invite.ts +0 -86
- package/src/cache/passwordReset.ts +0 -49
- package/src/cache/tests/docWritethrough.spec.ts +0 -296
- package/src/cache/tests/user.spec.ts +0 -145
- package/src/cache/tests/writethrough.spec.ts +0 -139
- package/src/cache/user.ts +0 -154
- package/src/cache/writethrough.ts +0 -133
- package/src/configs/configs.ts +0 -263
- package/src/configs/index.ts +0 -1
- package/src/configs/tests/configs.spec.ts +0 -184
- package/src/constants/db.ts +0 -75
- package/src/constants/index.ts +0 -2
- package/src/constants/misc.ts +0 -36
- package/src/context/Context.ts +0 -14
- package/src/context/identity.ts +0 -58
- package/src/context/index.ts +0 -3
- package/src/context/mainContext.ts +0 -422
- package/src/context/tests/index.spec.ts +0 -255
- package/src/context/types.ts +0 -26
- package/src/db/Replication.ts +0 -94
- package/src/db/couch/DatabaseImpl.ts +0 -511
- package/src/db/couch/connections.ts +0 -89
- package/src/db/couch/index.ts +0 -4
- package/src/db/couch/pouchDB.ts +0 -97
- package/src/db/couch/pouchDump.ts +0 -0
- package/src/db/couch/tests/DatabaseImpl.spec.ts +0 -118
- package/src/db/couch/utils.ts +0 -55
- package/src/db/db.ts +0 -34
- package/src/db/errors.ts +0 -14
- package/src/db/index.ts +0 -12
- package/src/db/instrumentation.ts +0 -199
- package/src/db/lucene.ts +0 -721
- package/src/db/searchIndexes/index.ts +0 -1
- package/src/db/searchIndexes/searchIndexes.ts +0 -62
- package/src/db/tests/DatabaseImpl.spec.ts +0 -55
- package/src/db/tests/connections.spec.ts +0 -22
- package/src/db/tests/index.spec.ts +0 -32
- package/src/db/tests/lucene.spec.ts +0 -400
- package/src/db/tests/pouch.spec.js +0 -62
- package/src/db/tests/utils.spec.ts +0 -63
- package/src/db/utils.ts +0 -208
- package/src/db/views.ts +0 -245
- package/src/docIds/conversions.ts +0 -60
- package/src/docIds/ids.ts +0 -126
- package/src/docIds/index.ts +0 -2
- package/src/docIds/newid.ts +0 -5
- package/src/docIds/params.ts +0 -189
- package/src/docUpdates/index.ts +0 -24
- package/src/environment.ts +0 -293
- package/src/errors/errors.ts +0 -119
- package/src/errors/index.ts +0 -1
- package/src/events/analytics.ts +0 -6
- package/src/events/asyncEvents/index.ts +0 -2
- package/src/events/asyncEvents/publisher.ts +0 -12
- package/src/events/asyncEvents/queue.ts +0 -22
- package/src/events/backfill.ts +0 -183
- package/src/events/documentId.ts +0 -56
- package/src/events/events.ts +0 -47
- package/src/events/identification.ts +0 -311
- package/src/events/index.ts +0 -15
- package/src/events/processors/AnalyticsProcessor.ts +0 -64
- package/src/events/processors/AuditLogsProcessor.ts +0 -92
- package/src/events/processors/LoggingProcessor.ts +0 -36
- package/src/events/processors/Processors.ts +0 -52
- package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -38
- package/src/events/processors/index.ts +0 -19
- package/src/events/processors/posthog/PosthogProcessor.ts +0 -118
- package/src/events/processors/posthog/index.ts +0 -3
- package/src/events/processors/posthog/rateLimiting.ts +0 -106
- package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -164
- package/src/events/processors/types.ts +0 -1
- package/src/events/publishers/account.ts +0 -41
- package/src/events/publishers/ai.ts +0 -21
- package/src/events/publishers/app.ts +0 -168
- package/src/events/publishers/auditLog.ts +0 -26
- package/src/events/publishers/auth.ts +0 -73
- package/src/events/publishers/automation.ts +0 -110
- package/src/events/publishers/backfill.ts +0 -74
- package/src/events/publishers/backup.ts +0 -42
- package/src/events/publishers/datasource.ts +0 -48
- package/src/events/publishers/email.ts +0 -17
- package/src/events/publishers/environmentVariable.ts +0 -38
- package/src/events/publishers/group.ts +0 -99
- package/src/events/publishers/index.ts +0 -25
- package/src/events/publishers/installation.ts +0 -38
- package/src/events/publishers/layout.ts +0 -26
- package/src/events/publishers/license.ts +0 -84
- package/src/events/publishers/org.ts +0 -37
- package/src/events/publishers/plugin.ts +0 -47
- package/src/events/publishers/query.ts +0 -89
- package/src/events/publishers/role.ts +0 -62
- package/src/events/publishers/rows.ts +0 -29
- package/src/events/publishers/screen.ts +0 -36
- package/src/events/publishers/serve.ts +0 -43
- package/src/events/publishers/table.ts +0 -70
- package/src/events/publishers/user.ts +0 -202
- package/src/events/publishers/view.ts +0 -107
- package/src/features/features.ts +0 -277
- package/src/features/index.ts +0 -2
- package/src/features/tests/features.spec.ts +0 -267
- package/src/features/tests/utils.ts +0 -64
- package/src/helpers.ts +0 -9
- package/src/index.ts +0 -59
- package/src/installation.ts +0 -115
- package/src/logging/alerts.ts +0 -26
- package/src/logging/correlation/correlation.ts +0 -15
- package/src/logging/correlation/index.ts +0 -1
- package/src/logging/correlation/middleware.ts +0 -18
- package/src/logging/index.ts +0 -4
- package/src/logging/pino/logger.ts +0 -239
- package/src/logging/pino/middleware.ts +0 -48
- package/src/logging/system.ts +0 -81
- package/src/logging/tests/system.spec.ts +0 -61
- package/src/middleware/adminOnly.ts +0 -9
- package/src/middleware/auditLog.ts +0 -6
- package/src/middleware/authenticated.ts +0 -247
- package/src/middleware/builderOnly.ts +0 -21
- package/src/middleware/builderOrAdmin.ts +0 -21
- package/src/middleware/contentSecurityPolicy.ts +0 -113
- package/src/middleware/csrf.ts +0 -81
- package/src/middleware/errorHandling.ts +0 -43
- package/src/middleware/index.ts +0 -24
- package/src/middleware/internalApi.ts +0 -23
- package/src/middleware/ip.ts +0 -12
- package/src/middleware/joi-validator.ts +0 -58
- package/src/middleware/matchers.ts +0 -39
- package/src/middleware/passport/datasource/google.ts +0 -102
- package/src/middleware/passport/local.ts +0 -54
- package/src/middleware/passport/sso/google.ts +0 -77
- package/src/middleware/passport/sso/oidc.ts +0 -152
- package/src/middleware/passport/sso/sso.ts +0 -138
- package/src/middleware/passport/sso/tests/google.spec.ts +0 -68
- package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -144
- package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
- package/src/middleware/passport/utils.ts +0 -38
- package/src/middleware/querystringToBody.ts +0 -28
- package/src/middleware/tenancy.ts +0 -36
- package/src/middleware/tests/builder.spec.ts +0 -181
- package/src/middleware/tests/contentSecurityPolicy.spec.ts +0 -75
- package/src/middleware/tests/matchers.spec.ts +0 -100
- package/src/migrations/definitions.ts +0 -40
- package/src/migrations/index.ts +0 -2
- package/src/migrations/migrations.ts +0 -186
- package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
- package/src/migrations/tests/migrations.spec.ts +0 -64
- package/src/objectStore/buckets/app.ts +0 -53
- package/src/objectStore/buckets/global.ts +0 -29
- package/src/objectStore/buckets/index.ts +0 -3
- package/src/objectStore/buckets/plugins.ts +0 -71
- package/src/objectStore/buckets/tests/app.spec.ts +0 -161
- package/src/objectStore/buckets/tests/global.spec.ts +0 -74
- package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
- package/src/objectStore/cloudfront.ts +0 -41
- package/src/objectStore/index.ts +0 -3
- package/src/objectStore/objectStore.ts +0 -585
- package/src/objectStore/utils.ts +0 -113
- package/src/platform/index.ts +0 -3
- package/src/platform/platformDb.ts +0 -6
- package/src/platform/tenants.ts +0 -101
- package/src/platform/tests/tenants.spec.ts +0 -26
- package/src/platform/users.ts +0 -129
- package/src/plugin/index.ts +0 -1
- package/src/plugin/tests/validation.spec.ts +0 -209
- package/src/plugin/utils.ts +0 -175
- package/src/queue/constants.ts +0 -8
- package/src/queue/inMemoryQueue.ts +0 -189
- package/src/queue/index.ts +0 -2
- package/src/queue/listeners.ts +0 -199
- package/src/queue/queue.ts +0 -84
- package/src/redis/index.ts +0 -6
- package/src/redis/init.ts +0 -118
- package/src/redis/redis.ts +0 -358
- package/src/redis/redlockImpl.ts +0 -155
- package/src/redis/tests/redis.spec.ts +0 -207
- package/src/redis/tests/redlockImpl.spec.ts +0 -105
- package/src/redis/utils.ts +0 -128
- package/src/security/auth.ts +0 -24
- package/src/security/encryption.ts +0 -185
- package/src/security/index.ts +0 -1
- package/src/security/permissions.ts +0 -166
- package/src/security/roles.ts +0 -655
- package/src/security/secrets.ts +0 -20
- package/src/security/sessions.ts +0 -123
- package/src/security/tests/auth.spec.ts +0 -45
- package/src/security/tests/encryption.spec.ts +0 -31
- package/src/security/tests/permissions.spec.ts +0 -146
- package/src/security/tests/secrets.spec.ts +0 -35
- package/src/security/tests/sessions.spec.ts +0 -12
- package/src/sql/designDoc.ts +0 -17
- package/src/sql/index.ts +0 -5
- package/src/sql/sql.ts +0 -1854
- package/src/sql/sqlTable.ts +0 -319
- package/src/sql/utils.ts +0 -193
- package/src/tenancy/db.ts +0 -6
- package/src/tenancy/index.ts +0 -2
- package/src/tenancy/tenancy.ts +0 -148
- package/src/tenancy/tests/tenancy.spec.ts +0 -184
- package/src/timers/index.ts +0 -1
- package/src/timers/timers.ts +0 -22
- package/src/users/db.ts +0 -582
- package/src/users/events.ts +0 -176
- package/src/users/index.ts +0 -4
- package/src/users/lookup.ts +0 -99
- package/src/users/test/db.spec.ts +0 -188
- package/src/users/test/utils.spec.ts +0 -67
- package/src/users/users.ts +0 -353
- package/src/users/utils.ts +0 -81
- package/src/utils/Duration.ts +0 -56
- package/src/utils/hashing.ts +0 -15
- package/src/utils/index.ts +0 -4
- package/src/utils/stringUtils.ts +0 -8
- package/src/utils/tests/Duration.spec.ts +0 -19
- package/src/utils/tests/utils.spec.ts +0 -204
- package/src/utils/utils.ts +0 -249
- package/tests/core/logging.ts +0 -34
- package/tests/core/users/users.spec.js +0 -53
- package/tests/core/utilities/index.ts +0 -7
- package/tests/core/utilities/jestUtils.ts +0 -33
- package/tests/core/utilities/mocks/alerts.ts +0 -4
- package/tests/core/utilities/mocks/date.ts +0 -3
- package/tests/core/utilities/mocks/events.ts +0 -132
- package/tests/core/utilities/mocks/index.ts +0 -9
- package/tests/core/utilities/mocks/licenses.ts +0 -119
- package/tests/core/utilities/queue.ts +0 -9
- package/tests/core/utilities/structures/Chance.ts +0 -20
- package/tests/core/utilities/structures/accounts.ts +0 -80
- package/tests/core/utilities/structures/apps.ts +0 -21
- package/tests/core/utilities/structures/common.ts +0 -7
- package/tests/core/utilities/structures/db.ts +0 -12
- package/tests/core/utilities/structures/documents/index.ts +0 -1
- package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
- package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
- package/tests/core/utilities/structures/generator.ts +0 -3
- package/tests/core/utilities/structures/index.ts +0 -15
- package/tests/core/utilities/structures/koa.ts +0 -16
- package/tests/core/utilities/structures/licenses.ts +0 -190
- package/tests/core/utilities/structures/plugins.ts +0 -19
- package/tests/core/utilities/structures/quotas.ts +0 -72
- package/tests/core/utilities/structures/scim.ts +0 -80
- package/tests/core/utilities/structures/sso.ts +0 -118
- package/tests/core/utilities/structures/tenants.ts +0 -5
- package/tests/core/utilities/structures/userGroups.ts +0 -10
- package/tests/core/utilities/structures/users.ts +0 -89
- package/tests/core/utilities/testContainerUtils.ts +0 -165
- package/tests/core/utilities/utils/index.ts +0 -2
- package/tests/core/utilities/utils/queue.ts +0 -27
- package/tests/core/utilities/utils/time.ts +0 -3
- package/tests/extra/DBTestConfiguration.ts +0 -36
- package/tests/extra/index.ts +0 -2
- package/tests/extra/testEnv.ts +0 -95
- package/tests/index.ts +0 -2
- package/tests/jestEnv.ts +0 -10
- package/tests/jestSetup.ts +0 -36
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { getTenantId, isMultiTenant } from "../../context"
|
|
2
|
-
import * as configs from "../../configs"
|
|
3
|
-
import { ConfigType, GoogleInnerConfig } from "@budibase/types"
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Utility to handle authentication errors.
|
|
7
|
-
*
|
|
8
|
-
* @param done The passport callback.
|
|
9
|
-
* @param message Message that will be returned in the response body
|
|
10
|
-
* @param err (Optional) error that will be logged
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export function authError(done: Function, message: string, err?: any) {
|
|
14
|
-
return done(
|
|
15
|
-
err,
|
|
16
|
-
null, // never return a user
|
|
17
|
-
{ message: message }
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function ssoCallbackUrl(
|
|
22
|
-
type: ConfigType,
|
|
23
|
-
config?: GoogleInnerConfig
|
|
24
|
-
) {
|
|
25
|
-
// incase there is a callback URL from before
|
|
26
|
-
if (config && (config as GoogleInnerConfig).callbackURL) {
|
|
27
|
-
return (config as GoogleInnerConfig).callbackURL as string
|
|
28
|
-
}
|
|
29
|
-
const settingsConfig = await configs.getSettingsConfig()
|
|
30
|
-
|
|
31
|
-
let callbackUrl = `/api/global/auth`
|
|
32
|
-
if (isMultiTenant()) {
|
|
33
|
-
callbackUrl += `/${getTenantId()}`
|
|
34
|
-
}
|
|
35
|
-
callbackUrl += `/${type}/callback`
|
|
36
|
-
|
|
37
|
-
return `${settingsConfig.platformUrl}${callbackUrl}`
|
|
38
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Ctx } from "@budibase/types"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Expects a standard "query" query string property which is the JSON body
|
|
5
|
-
* of the request, which has to be sent via query string due to the requirement
|
|
6
|
-
* of making an endpoint a GET request e.g. downloading a file stream.
|
|
7
|
-
*/
|
|
8
|
-
export default function (ctx: Ctx, next: any) {
|
|
9
|
-
const queryString = ctx.request.query?.query as string | undefined
|
|
10
|
-
if (ctx.request.method.toLowerCase() !== "get") {
|
|
11
|
-
ctx.throw(
|
|
12
|
-
500,
|
|
13
|
-
"Query to download middleware can only be used for get requests."
|
|
14
|
-
)
|
|
15
|
-
}
|
|
16
|
-
if (!queryString) {
|
|
17
|
-
return next()
|
|
18
|
-
}
|
|
19
|
-
const decoded = decodeURIComponent(queryString)
|
|
20
|
-
let json
|
|
21
|
-
try {
|
|
22
|
-
json = JSON.parse(decoded)
|
|
23
|
-
} catch (err) {
|
|
24
|
-
return next()
|
|
25
|
-
}
|
|
26
|
-
ctx.request.body = json
|
|
27
|
-
return next()
|
|
28
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { doInTenant } from "../context"
|
|
2
|
-
import { getTenantIDFromCtx } from "../tenancy"
|
|
3
|
-
import { buildMatcherRegex, matches } from "./matchers"
|
|
4
|
-
import { Header } from "../constants"
|
|
5
|
-
import {
|
|
6
|
-
BBContext,
|
|
7
|
-
EndpointMatcher,
|
|
8
|
-
GetTenantIdOptions,
|
|
9
|
-
TenantResolutionStrategy,
|
|
10
|
-
} from "@budibase/types"
|
|
11
|
-
|
|
12
|
-
export default function (
|
|
13
|
-
allowQueryStringPatterns: EndpointMatcher[],
|
|
14
|
-
noTenancyPatterns: EndpointMatcher[],
|
|
15
|
-
opts: { noTenancyRequired?: boolean } = { noTenancyRequired: false }
|
|
16
|
-
) {
|
|
17
|
-
const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns)
|
|
18
|
-
const noTenancyOptions = buildMatcherRegex(noTenancyPatterns)
|
|
19
|
-
|
|
20
|
-
return async function (ctx: BBContext | any, next: any) {
|
|
21
|
-
const allowNoTenant =
|
|
22
|
-
opts.noTenancyRequired || !!matches(ctx, noTenancyOptions)
|
|
23
|
-
const tenantOpts: GetTenantIdOptions = {
|
|
24
|
-
allowNoTenant,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const allowQs = !!matches(ctx, allowQsOptions)
|
|
28
|
-
if (!allowQs) {
|
|
29
|
-
tenantOpts.excludeStrategies = [TenantResolutionStrategy.QUERY]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const tenantId = getTenantIDFromCtx(ctx, tenantOpts)
|
|
33
|
-
ctx.set(Header.TENANT_ID, tenantId as string)
|
|
34
|
-
return doInTenant(tenantId, next)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import adminOnly from "../adminOnly"
|
|
2
|
-
import builderOnly from "../builderOnly"
|
|
3
|
-
import builderOrAdmin from "../builderOrAdmin"
|
|
4
|
-
import { structures } from "../../../tests"
|
|
5
|
-
import { ContextUser, ServiceType } from "@budibase/types"
|
|
6
|
-
import { doInAppContext } from "../../context"
|
|
7
|
-
import env from "../../environment"
|
|
8
|
-
|
|
9
|
-
env._set("SERVICE_TYPE", ServiceType.APPS)
|
|
10
|
-
|
|
11
|
-
const appId = "app_aaa"
|
|
12
|
-
const basicUser = structures.users.user()
|
|
13
|
-
const adminUser = structures.users.adminUser()
|
|
14
|
-
const adminOnlyUser = structures.users.adminOnlyUser()
|
|
15
|
-
const builderUser = structures.users.builderUser()
|
|
16
|
-
const appBuilderUser = structures.users.appBuilderUser(appId)
|
|
17
|
-
|
|
18
|
-
function buildUserCtx(user: ContextUser) {
|
|
19
|
-
return {
|
|
20
|
-
internal: false,
|
|
21
|
-
user,
|
|
22
|
-
throw: jest.fn(),
|
|
23
|
-
} as any
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function passed(throwFn: jest.Func, nextFn: jest.Func) {
|
|
27
|
-
expect(throwFn).not.toHaveBeenCalled()
|
|
28
|
-
expect(nextFn).toHaveBeenCalled()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function threw(throwFn: jest.Func) {
|
|
32
|
-
// cant check next, the throw function doesn't actually throw - so it still continues
|
|
33
|
-
expect(throwFn).toHaveBeenCalled()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
describe("adminOnly middleware", () => {
|
|
37
|
-
it("should allow admin user", () => {
|
|
38
|
-
const ctx = buildUserCtx(adminUser),
|
|
39
|
-
next = jest.fn()
|
|
40
|
-
adminOnly(ctx, next)
|
|
41
|
-
passed(ctx.throw, next)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it("should not allow basic user", () => {
|
|
45
|
-
const ctx = buildUserCtx(basicUser),
|
|
46
|
-
next = jest.fn()
|
|
47
|
-
adminOnly(ctx, next)
|
|
48
|
-
threw(ctx.throw)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it("should not allow builder user", () => {
|
|
52
|
-
const ctx = buildUserCtx(builderUser),
|
|
53
|
-
next = jest.fn()
|
|
54
|
-
adminOnly(ctx, next)
|
|
55
|
-
threw(ctx.throw)
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
describe("builderOnly middleware", () => {
|
|
60
|
-
it("should allow builder user", () => {
|
|
61
|
-
const ctx = buildUserCtx(builderUser),
|
|
62
|
-
next = jest.fn()
|
|
63
|
-
builderOnly(ctx, next)
|
|
64
|
-
passed(ctx.throw, next)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it("should allow app builder user", () => {
|
|
68
|
-
const ctx = buildUserCtx(appBuilderUser),
|
|
69
|
-
next = jest.fn()
|
|
70
|
-
doInAppContext(appId, () => {
|
|
71
|
-
builderOnly(ctx, next)
|
|
72
|
-
})
|
|
73
|
-
passed(ctx.throw, next)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it("should allow admin and builder user", () => {
|
|
77
|
-
const ctx = buildUserCtx(adminUser),
|
|
78
|
-
next = jest.fn()
|
|
79
|
-
builderOnly(ctx, next)
|
|
80
|
-
passed(ctx.throw, next)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it("should not allow admin user", () => {
|
|
84
|
-
const ctx = buildUserCtx(adminOnlyUser),
|
|
85
|
-
next = jest.fn()
|
|
86
|
-
builderOnly(ctx, next)
|
|
87
|
-
threw(ctx.throw)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it("should not allow app builder user to different app", () => {
|
|
91
|
-
const ctx = buildUserCtx(appBuilderUser),
|
|
92
|
-
next = jest.fn()
|
|
93
|
-
doInAppContext("app_bbb", () => {
|
|
94
|
-
builderOnly(ctx, next)
|
|
95
|
-
})
|
|
96
|
-
threw(ctx.throw)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it("should not allow basic user", () => {
|
|
100
|
-
const ctx = buildUserCtx(basicUser),
|
|
101
|
-
next = jest.fn()
|
|
102
|
-
builderOnly(ctx, next)
|
|
103
|
-
threw(ctx.throw)
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
describe("builderOrAdmin middleware", () => {
|
|
108
|
-
it("should allow builder user", () => {
|
|
109
|
-
const ctx = buildUserCtx(builderUser),
|
|
110
|
-
next = jest.fn()
|
|
111
|
-
builderOrAdmin(ctx, next)
|
|
112
|
-
passed(ctx.throw, next)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it("should allow builder and admin user", () => {
|
|
116
|
-
const ctx = buildUserCtx(adminUser),
|
|
117
|
-
next = jest.fn()
|
|
118
|
-
builderOrAdmin(ctx, next)
|
|
119
|
-
passed(ctx.throw, next)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it("should allow admin user", () => {
|
|
123
|
-
const ctx = buildUserCtx(adminOnlyUser),
|
|
124
|
-
next = jest.fn()
|
|
125
|
-
builderOrAdmin(ctx, next)
|
|
126
|
-
passed(ctx.throw, next)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it("should allow app builder user", () => {
|
|
130
|
-
const ctx = buildUserCtx(appBuilderUser),
|
|
131
|
-
next = jest.fn()
|
|
132
|
-
doInAppContext(appId, () => {
|
|
133
|
-
builderOrAdmin(ctx, next)
|
|
134
|
-
})
|
|
135
|
-
passed(ctx.throw, next)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it("should not allow basic user", () => {
|
|
139
|
-
const ctx = buildUserCtx(basicUser),
|
|
140
|
-
next = jest.fn()
|
|
141
|
-
builderOrAdmin(ctx, next)
|
|
142
|
-
threw(ctx.throw)
|
|
143
|
-
})
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
describe("check service difference", () => {
|
|
147
|
-
it("should not allow without app ID in apps", () => {
|
|
148
|
-
env._set("SERVICE_TYPE", ServiceType.APPS)
|
|
149
|
-
const appId = "app_a"
|
|
150
|
-
const ctx = buildUserCtx({
|
|
151
|
-
...basicUser,
|
|
152
|
-
builder: {
|
|
153
|
-
apps: [appId],
|
|
154
|
-
},
|
|
155
|
-
})
|
|
156
|
-
const next = jest.fn()
|
|
157
|
-
doInAppContext(appId, () => {
|
|
158
|
-
builderOnly(ctx, next)
|
|
159
|
-
})
|
|
160
|
-
passed(ctx.throw, next)
|
|
161
|
-
doInAppContext("app_b", () => {
|
|
162
|
-
builderOnly(ctx, next)
|
|
163
|
-
})
|
|
164
|
-
threw(ctx.throw)
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it("should allow without app ID in worker", () => {
|
|
168
|
-
env._set("SERVICE_TYPE", ServiceType.WORKER)
|
|
169
|
-
const ctx = buildUserCtx({
|
|
170
|
-
...basicUser,
|
|
171
|
-
builder: {
|
|
172
|
-
apps: ["app_a"],
|
|
173
|
-
},
|
|
174
|
-
})
|
|
175
|
-
const next = jest.fn()
|
|
176
|
-
doInAppContext("app_b", () => {
|
|
177
|
-
builderOnly(ctx, next)
|
|
178
|
-
})
|
|
179
|
-
passed(ctx.throw, next)
|
|
180
|
-
})
|
|
181
|
-
})
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import crypto from "crypto"
|
|
2
|
-
import contentSecurityPolicy from "../contentSecurityPolicy"
|
|
3
|
-
|
|
4
|
-
jest.mock("crypto", () => ({
|
|
5
|
-
randomBytes: jest.fn(),
|
|
6
|
-
randomUUID: jest.fn(),
|
|
7
|
-
}))
|
|
8
|
-
|
|
9
|
-
describe("contentSecurityPolicy middleware", () => {
|
|
10
|
-
let ctx: any
|
|
11
|
-
let next: any
|
|
12
|
-
const mockNonce = "mocked/nonce"
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
ctx = {
|
|
16
|
-
state: {},
|
|
17
|
-
set: jest.fn(),
|
|
18
|
-
}
|
|
19
|
-
next = jest.fn()
|
|
20
|
-
// @ts-ignore
|
|
21
|
-
crypto.randomBytes.mockReturnValue(Buffer.from(mockNonce, "base64"))
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
jest.clearAllMocks()
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it("should generate a nonce and set it in the script-src directive", async () => {
|
|
29
|
-
await contentSecurityPolicy(ctx, next)
|
|
30
|
-
|
|
31
|
-
expect(ctx.state.nonce).toBe(mockNonce)
|
|
32
|
-
expect(ctx.set).toHaveBeenCalledWith(
|
|
33
|
-
"Content-Security-Policy",
|
|
34
|
-
expect.stringContaining(
|
|
35
|
-
`script-src 'self' 'unsafe-eval' https://*.budibase.net https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io https://d2l5prqdbvm3op.cloudfront.net https://us-assets.i.posthog.com 'nonce-${mockNonce}'`
|
|
36
|
-
)
|
|
37
|
-
)
|
|
38
|
-
expect(next).toHaveBeenCalled()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it("should include all CSP directives in the header", async () => {
|
|
42
|
-
await contentSecurityPolicy(ctx, next)
|
|
43
|
-
|
|
44
|
-
const cspHeader = ctx.set.mock.calls[0][1]
|
|
45
|
-
expect(cspHeader).toContain("default-src 'self'")
|
|
46
|
-
expect(cspHeader).toContain("script-src 'self' 'unsafe-eval'")
|
|
47
|
-
expect(cspHeader).toContain("style-src 'self' 'unsafe-inline'")
|
|
48
|
-
expect(cspHeader).toContain("object-src 'none'")
|
|
49
|
-
expect(cspHeader).toContain("base-uri 'self'")
|
|
50
|
-
expect(cspHeader).toContain("connect-src 'self'")
|
|
51
|
-
expect(cspHeader).toContain("font-src 'self'")
|
|
52
|
-
expect(cspHeader).toContain("frame-src 'self'")
|
|
53
|
-
expect(cspHeader).toContain("img-src http: https: data: blob:")
|
|
54
|
-
expect(cspHeader).toContain("manifest-src 'self'")
|
|
55
|
-
expect(cspHeader).toContain("media-src 'self'")
|
|
56
|
-
expect(cspHeader).toContain("worker-src blob:")
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it("should handle errors and log an error message", async () => {
|
|
60
|
-
const consoleSpy = jest.spyOn(console, "error").mockImplementation()
|
|
61
|
-
const error = new Error("Test error")
|
|
62
|
-
// @ts-ignore
|
|
63
|
-
crypto.randomBytes.mockImplementation(() => {
|
|
64
|
-
throw error
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
await contentSecurityPolicy(ctx, next)
|
|
68
|
-
|
|
69
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
70
|
-
`Error occurred in Content-Security-Policy middleware: ${error}`
|
|
71
|
-
)
|
|
72
|
-
expect(next).not.toHaveBeenCalled()
|
|
73
|
-
consoleSpy.mockRestore()
|
|
74
|
-
})
|
|
75
|
-
})
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import * as matchers from "../matchers"
|
|
2
|
-
import { structures } from "../../../tests"
|
|
3
|
-
|
|
4
|
-
describe("matchers", () => {
|
|
5
|
-
it("matches by path and method", () => {
|
|
6
|
-
const pattern = [
|
|
7
|
-
{
|
|
8
|
-
route: "/api/tests",
|
|
9
|
-
method: "POST",
|
|
10
|
-
},
|
|
11
|
-
]
|
|
12
|
-
const ctx = structures.koa.newContext()
|
|
13
|
-
ctx.request.url = "/api/tests"
|
|
14
|
-
ctx.request.method = "POST"
|
|
15
|
-
|
|
16
|
-
const built = matchers.buildMatcherRegex(pattern)
|
|
17
|
-
|
|
18
|
-
expect(!!matchers.matches(ctx, built)).toBe(true)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it("wildcards path", () => {
|
|
22
|
-
const pattern = [
|
|
23
|
-
{
|
|
24
|
-
route: "/api/tests",
|
|
25
|
-
method: "POST",
|
|
26
|
-
},
|
|
27
|
-
]
|
|
28
|
-
const ctx = structures.koa.newContext()
|
|
29
|
-
ctx.request.url = "/api/tests/id/something/else"
|
|
30
|
-
ctx.request.method = "POST"
|
|
31
|
-
|
|
32
|
-
const built = matchers.buildMatcherRegex(pattern)
|
|
33
|
-
|
|
34
|
-
expect(!!matchers.matches(ctx, built)).toBe(true)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it("matches with param", () => {
|
|
38
|
-
const pattern = [
|
|
39
|
-
{
|
|
40
|
-
route: "/api/tests/:testId",
|
|
41
|
-
method: "GET",
|
|
42
|
-
},
|
|
43
|
-
]
|
|
44
|
-
const ctx = structures.koa.newContext()
|
|
45
|
-
ctx.request.url = "/api/tests/id"
|
|
46
|
-
ctx.request.method = "GET"
|
|
47
|
-
|
|
48
|
-
const built = matchers.buildMatcherRegex(pattern)
|
|
49
|
-
|
|
50
|
-
expect(!!matchers.matches(ctx, built)).toBe(true)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it("doesn't match by path", () => {
|
|
54
|
-
const pattern = [
|
|
55
|
-
{
|
|
56
|
-
route: "/api/tests",
|
|
57
|
-
method: "POST",
|
|
58
|
-
},
|
|
59
|
-
]
|
|
60
|
-
const ctx = structures.koa.newContext()
|
|
61
|
-
ctx.request.url = "/api/unknown"
|
|
62
|
-
ctx.request.method = "POST"
|
|
63
|
-
|
|
64
|
-
const built = matchers.buildMatcherRegex(pattern)
|
|
65
|
-
|
|
66
|
-
expect(!!matchers.matches(ctx, built)).toBe(false)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it("doesn't match by method", () => {
|
|
70
|
-
const pattern = [
|
|
71
|
-
{
|
|
72
|
-
route: "/api/tests",
|
|
73
|
-
method: "POST",
|
|
74
|
-
},
|
|
75
|
-
]
|
|
76
|
-
const ctx = structures.koa.newContext()
|
|
77
|
-
ctx.request.url = "/api/tests"
|
|
78
|
-
ctx.request.method = "GET"
|
|
79
|
-
|
|
80
|
-
const built = matchers.buildMatcherRegex(pattern)
|
|
81
|
-
|
|
82
|
-
expect(!!matchers.matches(ctx, built)).toBe(false)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it("matches by path and wildcard method", () => {
|
|
86
|
-
const pattern = [
|
|
87
|
-
{
|
|
88
|
-
route: "/api/tests",
|
|
89
|
-
method: "ALL",
|
|
90
|
-
},
|
|
91
|
-
]
|
|
92
|
-
const ctx = structures.koa.newContext()
|
|
93
|
-
ctx.request.url = "/api/tests"
|
|
94
|
-
ctx.request.method = "GET"
|
|
95
|
-
|
|
96
|
-
const built = matchers.buildMatcherRegex(pattern)
|
|
97
|
-
|
|
98
|
-
expect(!!matchers.matches(ctx, built)).toBe(true)
|
|
99
|
-
})
|
|
100
|
-
})
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MigrationType,
|
|
3
|
-
MigrationName,
|
|
4
|
-
MigrationDefinition,
|
|
5
|
-
} from "@budibase/types"
|
|
6
|
-
|
|
7
|
-
export const DEFINITIONS: MigrationDefinition[] = [
|
|
8
|
-
{
|
|
9
|
-
type: MigrationType.GLOBAL,
|
|
10
|
-
name: MigrationName.USER_EMAIL_VIEW_CASING,
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
type: MigrationType.GLOBAL,
|
|
14
|
-
name: MigrationName.SYNC_QUOTAS,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
type: MigrationType.APP,
|
|
18
|
-
name: MigrationName.APP_URLS,
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
type: MigrationType.APP,
|
|
22
|
-
name: MigrationName.EVENT_APP_BACKFILL,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
type: MigrationType.APP,
|
|
26
|
-
name: MigrationName.TABLE_SETTINGS_LINKS_TO_ACTIONS,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
type: MigrationType.GLOBAL,
|
|
30
|
-
name: MigrationName.EVENT_GLOBAL_BACKFILL,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
type: MigrationType.INSTALLATION,
|
|
34
|
-
name: MigrationName.EVENT_INSTALLATION_BACKFILL,
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
type: MigrationType.GLOBAL,
|
|
38
|
-
name: MigrationName.GLOBAL_INFO_SYNC_USERS,
|
|
39
|
-
},
|
|
40
|
-
]
|
package/src/migrations/index.ts
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_TENANT_ID } from "../constants"
|
|
2
|
-
import {
|
|
3
|
-
DocumentType,
|
|
4
|
-
StaticDatabases,
|
|
5
|
-
getAllApps,
|
|
6
|
-
getGlobalDBName,
|
|
7
|
-
getDB,
|
|
8
|
-
} from "../db"
|
|
9
|
-
import environment from "../environment"
|
|
10
|
-
import * as platform from "../platform"
|
|
11
|
-
import * as context from "../context"
|
|
12
|
-
import { DEFINITIONS } from "."
|
|
13
|
-
import {
|
|
14
|
-
Migration,
|
|
15
|
-
MigrationOptions,
|
|
16
|
-
MigrationType,
|
|
17
|
-
MigrationNoOpOptions,
|
|
18
|
-
App,
|
|
19
|
-
} from "@budibase/types"
|
|
20
|
-
|
|
21
|
-
export const getMigrationsDoc = async (db: any) => {
|
|
22
|
-
// get the migrations doc
|
|
23
|
-
try {
|
|
24
|
-
return await db.get(DocumentType.MIGRATIONS)
|
|
25
|
-
} catch (err: any) {
|
|
26
|
-
if (err.status && err.status === 404) {
|
|
27
|
-
return { _id: DocumentType.MIGRATIONS }
|
|
28
|
-
} else {
|
|
29
|
-
throw err
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const backPopulateMigrations = async (opts: MigrationNoOpOptions) => {
|
|
35
|
-
// filter migrations to the type and populate a no-op migration
|
|
36
|
-
const migrations: Migration[] = DEFINITIONS.filter(
|
|
37
|
-
def => def.type === opts.type
|
|
38
|
-
).map(d => ({ ...d, fn: () => {} }))
|
|
39
|
-
await runMigrations(migrations, { noOp: opts })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const runMigration = async (
|
|
43
|
-
migration: Migration,
|
|
44
|
-
options: MigrationOptions = {}
|
|
45
|
-
) => {
|
|
46
|
-
const migrationType = migration.type
|
|
47
|
-
const migrationName = migration.name
|
|
48
|
-
const silent = migration.silent
|
|
49
|
-
|
|
50
|
-
const log = (message: string) => {
|
|
51
|
-
if (!silent) {
|
|
52
|
-
console.log(message)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// get the db to store the migration in
|
|
57
|
-
let dbNames: string[]
|
|
58
|
-
if (migrationType === MigrationType.GLOBAL) {
|
|
59
|
-
dbNames = [getGlobalDBName()]
|
|
60
|
-
} else if (migrationType === MigrationType.APP) {
|
|
61
|
-
if (options.noOp) {
|
|
62
|
-
if (!options.noOp.appId) {
|
|
63
|
-
throw new Error("appId is required for noOp app migration")
|
|
64
|
-
}
|
|
65
|
-
dbNames = [options.noOp.appId]
|
|
66
|
-
} else {
|
|
67
|
-
const apps = (await getAllApps(migration.appOpts)) as App[]
|
|
68
|
-
dbNames = apps.map(app => app.appId)
|
|
69
|
-
}
|
|
70
|
-
} else if (migrationType === MigrationType.INSTALLATION) {
|
|
71
|
-
dbNames = [StaticDatabases.PLATFORM_INFO.name]
|
|
72
|
-
} else {
|
|
73
|
-
throw new Error(`Unrecognised migration type [${migrationType}]`)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const length = dbNames.length
|
|
77
|
-
let count = 0
|
|
78
|
-
|
|
79
|
-
// run the migration against each db
|
|
80
|
-
for (const dbName of dbNames) {
|
|
81
|
-
count++
|
|
82
|
-
const lengthStatement = length > 1 ? `[${count}/${length}]` : ""
|
|
83
|
-
|
|
84
|
-
const db = getDB(dbName)
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const doc = await getMigrationsDoc(db)
|
|
88
|
-
|
|
89
|
-
// the migration has already been run
|
|
90
|
-
if (doc[migrationName]) {
|
|
91
|
-
// check for force
|
|
92
|
-
if (
|
|
93
|
-
options.force &&
|
|
94
|
-
options.force[migrationType] &&
|
|
95
|
-
options.force[migrationType].includes(migrationName)
|
|
96
|
-
) {
|
|
97
|
-
log(`[Migration: ${migrationName}] [DB: ${dbName}] Forcing`)
|
|
98
|
-
} else {
|
|
99
|
-
// no force, exit
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// check if the migration is not a no-op
|
|
105
|
-
if (!options.noOp) {
|
|
106
|
-
log(
|
|
107
|
-
`[Migration: ${migrationName}] [DB: ${dbName}] Running ${lengthStatement}`
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
if (migration.preventRetry) {
|
|
111
|
-
// eagerly set the completion date
|
|
112
|
-
// so that we never run this migration twice even upon failure
|
|
113
|
-
doc[migrationName] = Date.now()
|
|
114
|
-
const response = await db.put(doc)
|
|
115
|
-
doc._rev = response.rev
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// run the migration
|
|
119
|
-
if (migrationType === MigrationType.APP) {
|
|
120
|
-
await context.doInAppContext(db.name, async () => {
|
|
121
|
-
await migration.fn(db)
|
|
122
|
-
})
|
|
123
|
-
} else {
|
|
124
|
-
await migration.fn(db)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
log(`[Migration: ${migrationName}] [DB: ${dbName}] Complete`)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// mark as complete
|
|
131
|
-
doc[migrationName] = Date.now()
|
|
132
|
-
await db.put(doc)
|
|
133
|
-
} catch (err) {
|
|
134
|
-
console.error(
|
|
135
|
-
`[Migration: ${migrationName}] [DB: ${dbName}] Error: `,
|
|
136
|
-
err
|
|
137
|
-
)
|
|
138
|
-
throw err
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export const runMigrations = async (
|
|
144
|
-
migrations: Migration[],
|
|
145
|
-
options: MigrationOptions = {}
|
|
146
|
-
) => {
|
|
147
|
-
let tenantIds
|
|
148
|
-
|
|
149
|
-
if (environment.MULTI_TENANCY) {
|
|
150
|
-
if (options.noOp) {
|
|
151
|
-
tenantIds = [options.noOp.tenantId]
|
|
152
|
-
} else if (!options.tenantIds || !options.tenantIds.length) {
|
|
153
|
-
// run for all tenants
|
|
154
|
-
tenantIds = await platform.tenants.getTenantIds()
|
|
155
|
-
} else {
|
|
156
|
-
tenantIds = options.tenantIds
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
// single tenancy
|
|
160
|
-
tenantIds = [DEFAULT_TENANT_ID]
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (tenantIds.length > 1) {
|
|
164
|
-
console.log(`Checking migrations for ${tenantIds.length} tenants`)
|
|
165
|
-
} else {
|
|
166
|
-
console.log("Checking migrations")
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
let count = 0
|
|
170
|
-
// for all tenants
|
|
171
|
-
for (const tenantId of tenantIds) {
|
|
172
|
-
count++
|
|
173
|
-
if (tenantIds.length > 1) {
|
|
174
|
-
console.log(`Progress [${count}/${tenantIds.length}]`)
|
|
175
|
-
}
|
|
176
|
-
// for all migrations
|
|
177
|
-
for (const migration of migrations) {
|
|
178
|
-
// run the migration
|
|
179
|
-
await context.doInTenant(
|
|
180
|
-
tenantId,
|
|
181
|
-
async () => await runMigration(migration, options)
|
|
182
|
-
)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
console.log("Migrations complete")
|
|
186
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`migrations should match snapshot 1`] = `
|
|
4
|
-
{
|
|
5
|
-
"_id": "migrations",
|
|
6
|
-
"_rev": "1-2f64479842a0513aa8b97f356b0b9127",
|
|
7
|
-
"createdAt": "2020-01-01T00:00:00.000Z",
|
|
8
|
-
"test": 1577836800000,
|
|
9
|
-
"updatedAt": "2020-01-01T00:00:00.000Z",
|
|
10
|
-
}
|
|
11
|
-
`;
|