Merge branch 'master' into fix/automation-layout-styling
This commit is contained in:
commit
4da8a3899f
|
@ -26,7 +26,7 @@ export async function fetchSelf(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const appId = context.getAppId()
|
const appId = context.getAppId()
|
||||||
let user: ContextUser = await getFullUser(ctx, userId)
|
let user: ContextUser = await getFullUser(userId)
|
||||||
// this shouldn't be returned by the app self
|
// this shouldn't be returned by the app self
|
||||||
delete user.roles
|
delete user.roles
|
||||||
// forward the csrf token from the session
|
// forward the csrf token from the session
|
||||||
|
|
|
@ -2,7 +2,6 @@ import * as linkRows from "../../../db/linkedRows"
|
||||||
import {
|
import {
|
||||||
generateRowID,
|
generateRowID,
|
||||||
getMultiIDParams,
|
getMultiIDParams,
|
||||||
getTableIDFromRowID,
|
|
||||||
InternalTables,
|
InternalTables,
|
||||||
} from "../../../db/utils"
|
} from "../../../db/utils"
|
||||||
import * as userController from "../user"
|
import * as userController from "../user"
|
||||||
|
@ -89,7 +88,7 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
if (isUserTable) {
|
if (isUserTable) {
|
||||||
// the row has been updated, need to put it into the ctx
|
// the row has been updated, need to put it into the ctx
|
||||||
ctx.request.body = row as any
|
ctx.request.body = row as any
|
||||||
await userController.updateMetadata(ctx)
|
await userController.updateMetadata(ctx as any)
|
||||||
return { row: ctx.body as Row, table }
|
return { row: ctx.body as Row, table }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,26 @@
|
||||||
import { generateUserFlagID, InternalTables } from "../../db/utils"
|
import { generateUserFlagID, InternalTables } from "../../db/utils"
|
||||||
import { getFullUser } from "../../utilities/users"
|
import { getFullUser } from "../../utilities/users"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { Ctx, UserCtx } from "@budibase/types"
|
import {
|
||||||
|
ContextUserMetadata,
|
||||||
|
Ctx,
|
||||||
|
FetchUserMetadataResponse,
|
||||||
|
FindUserMetadataResponse,
|
||||||
|
Flags,
|
||||||
|
SetFlagRequest,
|
||||||
|
UserCtx,
|
||||||
|
UserMetadata,
|
||||||
|
} from "@budibase/types"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
|
import { DocumentInsertResponse } from "@budibase/nano"
|
||||||
|
|
||||||
export async function fetchMetadata(ctx: Ctx) {
|
export async function fetchMetadata(ctx: Ctx<void, FetchUserMetadataResponse>) {
|
||||||
ctx.body = await sdk.users.fetchMetadata()
|
ctx.body = await sdk.users.fetchMetadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateSelfMetadata(ctx: UserCtx) {
|
export async function updateSelfMetadata(
|
||||||
|
ctx: UserCtx<UserMetadata, DocumentInsertResponse>
|
||||||
|
) {
|
||||||
// overwrite the ID with current users
|
// overwrite the ID with current users
|
||||||
ctx.request.body._id = ctx.user?._id
|
ctx.request.body._id = ctx.user?._id
|
||||||
// make sure no stale rev
|
// make sure no stale rev
|
||||||
|
@ -18,19 +30,21 @@ export async function updateSelfMetadata(ctx: UserCtx) {
|
||||||
await updateMetadata(ctx)
|
await updateMetadata(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateMetadata(ctx: UserCtx) {
|
export async function updateMetadata(
|
||||||
|
ctx: UserCtx<UserMetadata, DocumentInsertResponse>
|
||||||
|
) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const user = ctx.request.body
|
const user = ctx.request.body
|
||||||
// this isn't applicable to the user
|
const metadata: ContextUserMetadata = {
|
||||||
delete user.roles
|
|
||||||
const metadata = {
|
|
||||||
tableId: InternalTables.USER_METADATA,
|
tableId: InternalTables.USER_METADATA,
|
||||||
...user,
|
...user,
|
||||||
}
|
}
|
||||||
|
// this isn't applicable to the user
|
||||||
|
delete metadata.roles
|
||||||
ctx.body = await db.put(metadata)
|
ctx.body = await db.put(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function destroyMetadata(ctx: UserCtx) {
|
export async function destroyMetadata(ctx: UserCtx<void, { message: string }>) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
try {
|
try {
|
||||||
const dbUser = await sdk.users.get(ctx.params.id)
|
const dbUser = await sdk.users.get(ctx.params.id)
|
||||||
|
@ -43,11 +57,15 @@ export async function destroyMetadata(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findMetadata(ctx: UserCtx) {
|
export async function findMetadata(
|
||||||
ctx.body = await getFullUser(ctx, ctx.params.id)
|
ctx: UserCtx<void, FindUserMetadataResponse>
|
||||||
|
) {
|
||||||
|
ctx.body = await getFullUser(ctx.params.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setFlag(ctx: UserCtx) {
|
export async function setFlag(
|
||||||
|
ctx: UserCtx<SetFlagRequest, { message: string }>
|
||||||
|
) {
|
||||||
const userId = ctx.user?._id
|
const userId = ctx.user?._id
|
||||||
const { flag, value } = ctx.request.body
|
const { flag, value } = ctx.request.body
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
|
@ -55,9 +73,9 @@ export async function setFlag(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
const flagDocId = generateUserFlagID(userId!)
|
const flagDocId = generateUserFlagID(userId!)
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let doc
|
let doc: Flags
|
||||||
try {
|
try {
|
||||||
doc = await db.get<any>(flagDocId)
|
doc = await db.get<Flags>(flagDocId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
doc = { _id: flagDocId }
|
doc = { _id: flagDocId }
|
||||||
}
|
}
|
||||||
|
@ -66,13 +84,13 @@ export async function setFlag(ctx: UserCtx) {
|
||||||
ctx.body = { message: "Flag set successfully" }
|
ctx.body = { message: "Flag set successfully" }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFlags(ctx: UserCtx) {
|
export async function getFlags(ctx: UserCtx<void, Flags>) {
|
||||||
const userId = ctx.user?._id
|
const userId = ctx.user?._id
|
||||||
const docId = generateUserFlagID(userId!)
|
const docId = generateUserFlagID(userId!)
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let doc
|
let doc: Flags
|
||||||
try {
|
try {
|
||||||
doc = await db.get(docId)
|
doc = await db.get<Flags>(docId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
doc = { _id: docId }
|
doc = { _id: docId }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
const { roles, utils } = require("@budibase/backend-core")
|
|
||||||
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
|
|
||||||
const setup = require("./utilities")
|
|
||||||
const { BUILTIN_ROLE_IDS } = roles
|
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
|
||||||
|
|
||||||
jest.mock("../../../utilities/workerRequests", () => ({
|
|
||||||
getGlobalUsers: jest.fn(() => {
|
|
||||||
return {}
|
|
||||||
}),
|
|
||||||
getGlobalSelf: jest.fn(() => {
|
|
||||||
return {}
|
|
||||||
}),
|
|
||||||
deleteGlobalUser: jest.fn(),
|
|
||||||
}))
|
|
||||||
|
|
||||||
describe("/users", () => {
|
|
||||||
let request = setup.getRequest()
|
|
||||||
let config = setup.getConfig()
|
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await config.init()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("fetch", () => {
|
|
||||||
it("returns a list of users from an instance db", async () => {
|
|
||||||
await config.createUser({ id: "uuidx" })
|
|
||||||
await config.createUser({ id: "uuidy" })
|
|
||||||
const res = await request
|
|
||||||
.get(`/api/users/metadata`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
expect(res.body.length).toBe(3)
|
|
||||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidx`)).toBeDefined()
|
|
||||||
expect(res.body.find(u => u._id === `ro_ta_users_us_uuidy`)).toBeDefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
|
||||||
await config.createUser()
|
|
||||||
await checkPermissionsEndpoint({
|
|
||||||
config,
|
|
||||||
request,
|
|
||||||
method: "GET",
|
|
||||||
url: `/api/users/metadata`,
|
|
||||||
passRole: BUILTIN_ROLE_IDS.ADMIN,
|
|
||||||
failRole: BUILTIN_ROLE_IDS.PUBLIC,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("update", () => {
|
|
||||||
it("should be able to update the user", async () => {
|
|
||||||
const user = await config.createUser({ id: `us_update${utils.newid()}` })
|
|
||||||
user.roleId = BUILTIN_ROLE_IDS.BASIC
|
|
||||||
delete user._rev
|
|
||||||
const res = await request
|
|
||||||
.put(`/api/users/metadata`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send(user)
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body.ok).toEqual(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to update the user multiple times", async () => {
|
|
||||||
const user = await config.createUser()
|
|
||||||
delete user._rev
|
|
||||||
|
|
||||||
const res1 = await request
|
|
||||||
.put(`/api/users/metadata`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ ...user, roleId: BUILTIN_ROLE_IDS.BASIC })
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
|
|
||||||
const res = await request
|
|
||||||
.put(`/api/users/metadata`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ ...user, _rev: res1.body.rev, roleId: BUILTIN_ROLE_IDS.POWER })
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
|
|
||||||
expect(res.body.ok).toEqual(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should require the _rev field for multiple updates", async () => {
|
|
||||||
const user = await config.createUser()
|
|
||||||
delete user._rev
|
|
||||||
|
|
||||||
await request
|
|
||||||
.put(`/api/users/metadata`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ ...user, roleId: BUILTIN_ROLE_IDS.BASIC })
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
|
|
||||||
await request
|
|
||||||
.put(`/api/users/metadata`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ ...user, roleId: BUILTIN_ROLE_IDS.POWER })
|
|
||||||
.expect(409)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("destroy", () => {
|
|
||||||
it("should be able to delete the user", async () => {
|
|
||||||
const user = await config.createUser()
|
|
||||||
const res = await request
|
|
||||||
.delete(`/api/users/metadata/${user._id}`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body.message).toBeDefined()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("find", () => {
|
|
||||||
it("should be able to find the user", async () => {
|
|
||||||
const user = await config.createUser()
|
|
||||||
const res = await request
|
|
||||||
.get(`/api/users/metadata/${user._id}`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body._id).toEqual(user._id)
|
|
||||||
expect(res.body.roleId).toEqual(BUILTIN_ROLE_IDS.ADMIN)
|
|
||||||
expect(res.body.tableId).toBeDefined()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("setFlag", () => {
|
|
||||||
it("should throw an error if a flag is not provided", async () => {
|
|
||||||
await config.createUser()
|
|
||||||
const res = await request
|
|
||||||
.post(`/api/users/flags`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ value: "test" })
|
|
||||||
.expect(400)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body.message).toEqual(
|
|
||||||
"Must supply a 'flag' field in request body."
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to set a flag on the user", async () => {
|
|
||||||
await config.createUser()
|
|
||||||
const res = await request
|
|
||||||
.post(`/api/users/flags`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ value: "test", flag: "test" })
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body.message).toEqual("Flag set successfully")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("getFlags", () => {
|
|
||||||
it("should get flags for a specific user", async () => {
|
|
||||||
let flagData = { value: "test", flag: "test" }
|
|
||||||
await config.createUser()
|
|
||||||
await request
|
|
||||||
.post(`/api/users/flags`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send(flagData)
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
|
|
||||||
const res = await request
|
|
||||||
.get(`/api/users/flags`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body[flagData.value]).toEqual(flagData.flag)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("setFlag", () => {
|
|
||||||
it("should throw an error if a flag is not provided", async () => {
|
|
||||||
await config.createUser()
|
|
||||||
const res = await request
|
|
||||||
.post(`/api/users/flags`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ value: "test" })
|
|
||||||
.expect(400)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body.message).toEqual(
|
|
||||||
"Must supply a 'flag' field in request body."
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to set a flag on the user", async () => {
|
|
||||||
await config.createUser()
|
|
||||||
const res = await request
|
|
||||||
.post(`/api/users/flags`)
|
|
||||||
.set(config.defaultHeaders())
|
|
||||||
.send({ value: "test", flag: "test" })
|
|
||||||
.expect(200)
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
expect(res.body.message).toEqual("Flag set successfully")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
import { roles, utils } from "@budibase/backend-core"
|
||||||
|
import { checkPermissionsEndpoint } from "./utilities/TestFunctions"
|
||||||
|
import * as setup from "./utilities"
|
||||||
|
import { UserMetadata } from "@budibase/types"
|
||||||
|
|
||||||
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
jest.mock("../../../utilities/workerRequests", () => ({
|
||||||
|
getGlobalUsers: jest.fn(() => {
|
||||||
|
return {}
|
||||||
|
}),
|
||||||
|
getGlobalSelf: jest.fn(() => {
|
||||||
|
return {}
|
||||||
|
}),
|
||||||
|
deleteGlobalUser: jest.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe("/users", () => {
|
||||||
|
let request = setup.getRequest()
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("fetch", () => {
|
||||||
|
it("returns a list of users from an instance db", async () => {
|
||||||
|
await config.createUser({ id: "uuidx" })
|
||||||
|
await config.createUser({ id: "uuidy" })
|
||||||
|
|
||||||
|
const res = await config.api.user.fetch()
|
||||||
|
expect(res.length).toBe(3)
|
||||||
|
|
||||||
|
const ids = res.map(u => u._id)
|
||||||
|
expect(ids).toContain(`ro_ta_users_us_uuidx`)
|
||||||
|
expect(ids).toContain(`ro_ta_users_us_uuidy`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
await config.createUser()
|
||||||
|
await checkPermissionsEndpoint({
|
||||||
|
config,
|
||||||
|
request,
|
||||||
|
method: "GET",
|
||||||
|
url: `/api/users/metadata`,
|
||||||
|
passRole: roles.BUILTIN_ROLE_IDS.ADMIN,
|
||||||
|
failRole: roles.BUILTIN_ROLE_IDS.PUBLIC,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("update", () => {
|
||||||
|
it("should be able to update the user", async () => {
|
||||||
|
const user: UserMetadata = await config.createUser({
|
||||||
|
id: `us_update${utils.newid()}`,
|
||||||
|
})
|
||||||
|
user.roleId = roles.BUILTIN_ROLE_IDS.BASIC
|
||||||
|
delete user._rev
|
||||||
|
const res = await config.api.user.update(user)
|
||||||
|
expect(res.ok).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to update the user multiple times", async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
delete user._rev
|
||||||
|
|
||||||
|
const res1 = await config.api.user.update({
|
||||||
|
...user,
|
||||||
|
roleId: roles.BUILTIN_ROLE_IDS.BASIC,
|
||||||
|
})
|
||||||
|
const res2 = await config.api.user.update({
|
||||||
|
...user,
|
||||||
|
_rev: res1.rev,
|
||||||
|
roleId: roles.BUILTIN_ROLE_IDS.POWER,
|
||||||
|
})
|
||||||
|
expect(res2.ok).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should require the _rev field for multiple updates", async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
delete user._rev
|
||||||
|
|
||||||
|
await config.api.user.update({
|
||||||
|
...user,
|
||||||
|
roleId: roles.BUILTIN_ROLE_IDS.BASIC,
|
||||||
|
})
|
||||||
|
await config.api.user.update(
|
||||||
|
{ ...user, roleId: roles.BUILTIN_ROLE_IDS.POWER },
|
||||||
|
{ expectStatus: 409 }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("destroy", () => {
|
||||||
|
it("should be able to delete the user", async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
const res = await config.api.user.destroy(user._id!)
|
||||||
|
expect(res.message).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("find", () => {
|
||||||
|
it("should be able to find the user", async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
const res = await config.api.user.find(user._id!)
|
||||||
|
expect(res._id).toEqual(user._id)
|
||||||
|
expect(res.roleId).toEqual(roles.BUILTIN_ROLE_IDS.ADMIN)
|
||||||
|
expect(res.tableId).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("setFlag", () => {
|
||||||
|
it("should throw an error if a flag is not provided", async () => {
|
||||||
|
await config.createUser()
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/users/flags`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.send({ value: "test" })
|
||||||
|
.expect(400)
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
expect(res.body.message).toEqual(
|
||||||
|
"Must supply a 'flag' field in request body."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to set a flag on the user", async () => {
|
||||||
|
await config.createUser()
|
||||||
|
const res = await config.api.user.setFlag("test", true)
|
||||||
|
expect(res.message).toEqual("Flag set successfully")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("getFlags", () => {
|
||||||
|
it("should get flags for a specific user", async () => {
|
||||||
|
await config.createUser()
|
||||||
|
await config.api.user.setFlag("test", "test")
|
||||||
|
|
||||||
|
const res = await config.api.user.getFlags()
|
||||||
|
expect(res.test).toEqual("test")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -264,7 +264,7 @@ class TestConfiguration {
|
||||||
admin = false,
|
admin = false,
|
||||||
email = this.defaultUserValues.email,
|
email = this.defaultUserValues.email,
|
||||||
roles,
|
roles,
|
||||||
}: any = {}) {
|
}: any = {}): Promise<User> {
|
||||||
const db = tenancy.getTenantDB(this.getTenantId())
|
const db = tenancy.getTenantDB(this.getTenantId())
|
||||||
let existing
|
let existing
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { ScreenAPI } from "./screen"
|
||||||
import { ApplicationAPI } from "./application"
|
import { ApplicationAPI } from "./application"
|
||||||
import { BackupAPI } from "./backup"
|
import { BackupAPI } from "./backup"
|
||||||
import { AttachmentAPI } from "./attachment"
|
import { AttachmentAPI } from "./attachment"
|
||||||
|
import { UserAPI } from "./user"
|
||||||
|
|
||||||
export default class API {
|
export default class API {
|
||||||
table: TableAPI
|
table: TableAPI
|
||||||
|
@ -21,6 +22,7 @@ export default class API {
|
||||||
application: ApplicationAPI
|
application: ApplicationAPI
|
||||||
backup: BackupAPI
|
backup: BackupAPI
|
||||||
attachment: AttachmentAPI
|
attachment: AttachmentAPI
|
||||||
|
user: UserAPI
|
||||||
|
|
||||||
constructor(config: TestConfiguration) {
|
constructor(config: TestConfiguration) {
|
||||||
this.table = new TableAPI(config)
|
this.table = new TableAPI(config)
|
||||||
|
@ -33,5 +35,6 @@ export default class API {
|
||||||
this.application = new ApplicationAPI(config)
|
this.application = new ApplicationAPI(config)
|
||||||
this.backup = new BackupAPI(config)
|
this.backup = new BackupAPI(config)
|
||||||
this.attachment = new AttachmentAPI(config)
|
this.attachment = new AttachmentAPI(config)
|
||||||
|
this.user = new UserAPI(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
import {
|
||||||
|
FetchUserMetadataResponse,
|
||||||
|
FindUserMetadataResponse,
|
||||||
|
Flags,
|
||||||
|
UserMetadata,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import TestConfiguration from "../TestConfiguration"
|
||||||
|
import { TestAPI } from "./base"
|
||||||
|
import { DocumentInsertResponse } from "@budibase/nano"
|
||||||
|
|
||||||
|
export class UserAPI extends TestAPI {
|
||||||
|
constructor(config: TestConfiguration) {
|
||||||
|
super(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch = async (
|
||||||
|
{ expectStatus } = { expectStatus: 200 }
|
||||||
|
): Promise<FetchUserMetadataResponse> => {
|
||||||
|
const res = await this.request
|
||||||
|
.get(`/api/users/metadata`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
|
find = async (
|
||||||
|
id: string,
|
||||||
|
{ expectStatus } = { expectStatus: 200 }
|
||||||
|
): Promise<FindUserMetadataResponse> => {
|
||||||
|
const res = await this.request
|
||||||
|
.get(`/api/users/metadata/${id}`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
|
update = async (
|
||||||
|
user: UserMetadata,
|
||||||
|
{ expectStatus } = { expectStatus: 200 }
|
||||||
|
): Promise<DocumentInsertResponse> => {
|
||||||
|
const res = await this.request
|
||||||
|
.put(`/api/users/metadata`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.send(user)
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body as DocumentInsertResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelf = async (
|
||||||
|
user: UserMetadata,
|
||||||
|
{ expectStatus } = { expectStatus: 200 }
|
||||||
|
): Promise<DocumentInsertResponse> => {
|
||||||
|
const res = await this.request
|
||||||
|
.post(`/api/users/metadata/self`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.send(user)
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body as DocumentInsertResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy = async (
|
||||||
|
id: string,
|
||||||
|
{ expectStatus } = { expectStatus: 200 }
|
||||||
|
): Promise<{ message: string }> => {
|
||||||
|
const res = await this.request
|
||||||
|
.delete(`/api/users/metadata/${id}`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body as { message: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
setFlag = async (
|
||||||
|
flag: string,
|
||||||
|
value: any,
|
||||||
|
{ expectStatus } = { expectStatus: 200 }
|
||||||
|
): Promise<{ message: string }> => {
|
||||||
|
const res = await this.request
|
||||||
|
.post(`/api/users/flags`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.send({ flag, value })
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body as { message: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
getFlags = async (
|
||||||
|
{ expectStatus } = { expectStatus: 200 }
|
||||||
|
): Promise<Flags> => {
|
||||||
|
const res = await this.request
|
||||||
|
.get(`/api/users/flags`)
|
||||||
|
.set(this.config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
|
||||||
|
if (res.status !== expectStatus) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected status ${expectStatus} but got ${
|
||||||
|
res.status
|
||||||
|
} with body ${JSON.stringify(res.body)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.body as Flags
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
import { InternalTables } from "../db/utils"
|
import { InternalTables } from "../db/utils"
|
||||||
import { getGlobalUser } from "./global"
|
import { getGlobalUser } from "./global"
|
||||||
import { context, roles } from "@budibase/backend-core"
|
import { context, roles } from "@budibase/backend-core"
|
||||||
import { UserCtx } from "@budibase/types"
|
import { ContextUserMetadata, UserCtx, UserMetadata } from "@budibase/types"
|
||||||
|
|
||||||
export async function getFullUser(ctx: UserCtx, userId: string) {
|
export async function getFullUser(
|
||||||
|
userId: string
|
||||||
|
): Promise<ContextUserMetadata> {
|
||||||
const global = await getGlobalUser(userId)
|
const global = await getGlobalUser(userId)
|
||||||
let metadata: any = {}
|
let metadata: UserMetadata | undefined = undefined
|
||||||
|
|
||||||
// always prefer the user metadata _id and _rev
|
// always prefer the user metadata _id and _rev
|
||||||
delete global._id
|
delete global._id
|
||||||
|
@ -14,11 +16,11 @@ export async function getFullUser(ctx: UserCtx, userId: string) {
|
||||||
try {
|
try {
|
||||||
// this will throw an error if the db doesn't exist, or there is no appId
|
// this will throw an error if the db doesn't exist, or there is no appId
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
metadata = await db.get(userId)
|
metadata = await db.get<UserMetadata>(userId)
|
||||||
|
delete metadata.csrfToken
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// it is fine if there is no user metadata yet
|
// it is fine if there is no user metadata yet
|
||||||
}
|
}
|
||||||
delete metadata.csrfToken
|
|
||||||
return {
|
return {
|
||||||
...metadata,
|
...metadata,
|
||||||
...global,
|
...global,
|
||||||
|
|
|
@ -6,3 +6,4 @@ export * from "./rows"
|
||||||
export * from "./table"
|
export * from "./table"
|
||||||
export * from "./permission"
|
export * from "./permission"
|
||||||
export * from "./attachment"
|
export * from "./attachment"
|
||||||
|
export * from "./user"
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { ContextUserMetadata } from "../../../"
|
||||||
|
|
||||||
|
export type FetchUserMetadataResponse = ContextUserMetadata[]
|
||||||
|
export type FindUserMetadataResponse = ContextUserMetadata
|
||||||
|
|
||||||
|
export interface SetFlagRequest {
|
||||||
|
flag: string
|
||||||
|
value: any
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { Document } from "../../"
|
||||||
|
|
||||||
|
export interface Flags extends Document {
|
||||||
|
[key: string]: any
|
||||||
|
}
|
|
@ -1,2 +1,3 @@
|
||||||
export * from "./account"
|
export * from "./account"
|
||||||
export * from "./user"
|
export * from "./user"
|
||||||
|
export * from "./flag"
|
||||||
|
|
Loading…
Reference in New Issue