improve performance of adding users and groups
This commit is contained in:
parent
0e3b1fc46f
commit
72a67adcf4
|
@ -144,8 +144,10 @@
|
||||||
on:mousedown={onClick}
|
on:mousedown={onClick}
|
||||||
>
|
>
|
||||||
<span class="spectrum-Picker-label">
|
<span class="spectrum-Picker-label">
|
||||||
|
<div>
|
||||||
{fieldText}
|
{fieldText}
|
||||||
</span>
|
</div></span
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
||||||
focusable="false"
|
focusable="false"
|
||||||
|
|
|
@ -12,25 +12,36 @@
|
||||||
import { Constants } from "@budibase/frontend-core"
|
import { Constants } from "@budibase/frontend-core"
|
||||||
|
|
||||||
export let showOnboardingTypeModal
|
export let showOnboardingTypeModal
|
||||||
|
const password = Math.random().toString(36).substring(2, 22)
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let disabled
|
let disabled
|
||||||
|
let userGroups = []
|
||||||
|
|
||||||
$: userData = [{ email: "", role: "", groups: [], error: null }]
|
$: userData = [
|
||||||
|
{
|
||||||
|
email: "",
|
||||||
|
role: "appUser",
|
||||||
|
password,
|
||||||
|
forceResetPassword: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
function addNewInput() {
|
function addNewInput() {
|
||||||
userData = [...userData, { email: "", role: "" }]
|
userData = [
|
||||||
}
|
...userData,
|
||||||
|
{
|
||||||
function setValue(e) {
|
email: "",
|
||||||
userData.groups = e.detail
|
role: "appUser",
|
||||||
|
password: Math.random().toString(36).substring(2, 22),
|
||||||
|
forceResetPassword: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent
|
<ModalContent
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
showOnboardingTypeModal()
|
showOnboardingTypeModal()
|
||||||
dispatch("change", userData)
|
dispatch("change", { users: userData, groups: userGroups })
|
||||||
}}
|
}}
|
||||||
size="M"
|
size="M"
|
||||||
title="Add new user"
|
title="Add new user"
|
||||||
|
@ -57,12 +68,12 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<Multiselect
|
<Multiselect
|
||||||
|
bind:value={userGroups}
|
||||||
placeholder="Select User Groups"
|
placeholder="Select User Groups"
|
||||||
on:change={e => setValue(e)}
|
|
||||||
label="User Groups"
|
label="User Groups"
|
||||||
options={$groups}
|
options={$groups}
|
||||||
getOptionLabel={option => option.name}
|
getOptionLabel={option => option.name}
|
||||||
getOptionValue={option => option.name}
|
getOptionValue={option => option._id}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { Avatar } from "@budibase/bbui"
|
import { Avatar } from "@budibase/bbui"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
console.log(value)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="align">
|
<div class="align">
|
||||||
|
|
|
@ -2,6 +2,16 @@
|
||||||
import { Body, ModalContent, Table, Icon } from "@budibase/bbui"
|
import { Body, ModalContent, Table, Icon } from "@budibase/bbui"
|
||||||
import PasswordCopyRenderer from "./PasswordCopyRenderer.svelte"
|
import PasswordCopyRenderer from "./PasswordCopyRenderer.svelte"
|
||||||
|
|
||||||
|
export let userData
|
||||||
|
|
||||||
|
$: mappedData = userData.map(user => {
|
||||||
|
return {
|
||||||
|
email: user.email,
|
||||||
|
password: user.password,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$: console.log(mappedData)
|
||||||
const schema = {
|
const schema = {
|
||||||
email: {},
|
email: {},
|
||||||
password: {},
|
password: {},
|
||||||
|
@ -33,9 +43,7 @@
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
{schema}
|
{schema}
|
||||||
data={[
|
data={mappedData}
|
||||||
{ email: "test", password: Math.random().toString(36).slice(2, 20) },
|
|
||||||
]}
|
|
||||||
allowEditColumns={false}
|
allowEditColumns={false}
|
||||||
allowEditRows={false}
|
allowEditRows={false}
|
||||||
allowSelectRows={false}
|
allowSelectRows={false}
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte"
|
import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte"
|
||||||
import PasswordModal from "./_components/PasswordModal.svelte"
|
import PasswordModal from "./_components/PasswordModal.svelte"
|
||||||
import ImportUsersModal from "./_components/ImportUsersModal.svelte"
|
import ImportUsersModal from "./_components/ImportUsersModal.svelte"
|
||||||
import analytics, { Events } from "analytics"
|
|
||||||
import { createPaginationStore } from "helpers/pagination"
|
import { createPaginationStore } from "helpers/pagination"
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
|
@ -74,6 +73,7 @@
|
||||||
let pageInfo = createPaginationStore()
|
let pageInfo = createPaginationStore()
|
||||||
let prevEmail = undefined,
|
let prevEmail = undefined,
|
||||||
searchEmail = undefined
|
searchEmail = undefined
|
||||||
|
|
||||||
$: page = $pageInfo.page
|
$: page = $pageInfo.page
|
||||||
$: fetchUsers(page, searchEmail)
|
$: fetchUsers(page, searchEmail)
|
||||||
|
|
||||||
|
@ -110,42 +110,30 @@
|
||||||
admin: true,
|
admin: true,
|
||||||
})
|
})
|
||||||
notifications.success(res.message)
|
notifications.success(res.message)
|
||||||
analytics.captureEvent(Events.USER.INVITE, { type: "Email onboarding" })
|
|
||||||
inviteConfirmationModal.show()
|
inviteConfirmationModal.show()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
notifications.error("Error inviting user")
|
notifications.error("Error inviting user")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
async function createUser() {
|
async function createUser() {
|
||||||
try {
|
try {
|
||||||
await users.create({
|
await users.create(userData)
|
||||||
email: $email,
|
|
||||||
password,
|
|
||||||
builder,
|
|
||||||
admin,
|
|
||||||
forceResetPassword: true,
|
|
||||||
})
|
|
||||||
notifications.success("Successfully created user")
|
notifications.success("Successfully created user")
|
||||||
|
await groups.actions.init()
|
||||||
|
passwordModal.show()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
notifications.error("Error creating user")
|
notifications.error("Error creating user")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
async function chooseCreationType(onboardingType) {
|
async function chooseCreationType(onboardingType) {
|
||||||
if (onboardingType === "emailOnboarding") {
|
if (onboardingType === "emailOnboarding") {
|
||||||
createUserFlow()
|
createUserFlow()
|
||||||
} else {
|
} else {
|
||||||
await users.create({
|
await createUser()
|
||||||
email: "auser5@test.com",
|
|
||||||
password: Math.random().toString(36).slice(2, 20),
|
|
||||||
builder: true,
|
|
||||||
admin: true,
|
|
||||||
forceResetPassword: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
passwordModal.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +254,7 @@
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={passwordModal}>
|
<Modal bind:this={passwordModal}>
|
||||||
<PasswordModal />
|
<PasswordModal userData={userData.users} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={importUsersModal}>
|
<Modal bind:this={importUsersModal}>
|
||||||
|
|
|
@ -37,28 +37,35 @@ export function createUsersStore() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function create({
|
async function create(data) {
|
||||||
email,
|
let mappedUsers = data.users.map(user => {
|
||||||
password,
|
console.log(user)
|
||||||
admin,
|
|
||||||
builder,
|
|
||||||
forceResetPassword,
|
|
||||||
}) {
|
|
||||||
const body = {
|
const body = {
|
||||||
email,
|
email: user.email,
|
||||||
password,
|
password: user.password,
|
||||||
roles: {},
|
roles: {},
|
||||||
}
|
}
|
||||||
if (forceResetPassword) {
|
if (user.forceResetPassword) {
|
||||||
body.forceResetPassword = forceResetPassword
|
body.forceResetPassword = user.forceResetPassword
|
||||||
}
|
}
|
||||||
if (builder) {
|
|
||||||
|
switch (user.role) {
|
||||||
|
case "appUser":
|
||||||
|
body.builder = { global: false }
|
||||||
|
body.admin = { global: false }
|
||||||
|
break
|
||||||
|
case "developer":
|
||||||
body.builder = { global: true }
|
body.builder = { global: true }
|
||||||
}
|
break
|
||||||
if (admin) {
|
case "admin":
|
||||||
body.admin = { global: true }
|
body.admin = { global: true }
|
||||||
|
break
|
||||||
}
|
}
|
||||||
await API.saveUser(body)
|
|
||||||
|
return body
|
||||||
|
})
|
||||||
|
await API.saveUsers({ users: mappedUsers, groups: data.groups })
|
||||||
|
|
||||||
// re-search from first page
|
// re-search from first page
|
||||||
await search()
|
await search()
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,20 @@ export const buildUserEndpoints = API => ({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates multiple users.
|
||||||
|
* @param users the array of user objects to create
|
||||||
|
*/
|
||||||
|
saveUsers: async ({ users, groups }) => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/global/users/bulkSave",
|
||||||
|
body: {
|
||||||
|
users,
|
||||||
|
groups,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a user from the curernt tenant.
|
* Deletes a user from the curernt tenant.
|
||||||
* @param userId the ID of the user to delete
|
* @param userId the ID of the user to delete
|
||||||
|
|
|
@ -166,13 +166,19 @@ export async function preview(ctx: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function execute(ctx: any, opts = { rowsOnly: false }) {
|
async function execute(
|
||||||
|
ctx: any,
|
||||||
|
opts = { rowsOnly: false, isAutomation: false }
|
||||||
|
) {
|
||||||
const db = getAppDB()
|
const db = getAppDB()
|
||||||
|
|
||||||
const query = await db.get(ctx.params.queryId)
|
const query = await db.get(ctx.params.queryId)
|
||||||
const datasource = await db.get(query.datasourceId)
|
const datasource = await db.get(query.datasourceId)
|
||||||
|
|
||||||
const authConfigCtx: any = getAuthConfig(ctx)
|
let authConfigCtx: any = {}
|
||||||
|
if (!opts.isAutomation) {
|
||||||
|
authConfigCtx = getAuthConfig(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
const enrichedParameters = ctx.request.body.parameters || {}
|
const enrichedParameters = ctx.request.body.parameters || {}
|
||||||
// make sure parameters are fully enriched with defaults
|
// make sure parameters are fully enriched with defaults
|
||||||
|
@ -214,11 +220,11 @@ async function execute(ctx: any, opts = { rowsOnly: false }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function executeV1(ctx: any) {
|
export async function executeV1(ctx: any) {
|
||||||
return execute(ctx, { rowsOnly: true })
|
return execute(ctx, { rowsOnly: true, isAutomation: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function executeV2(ctx: any) {
|
export async function executeV2(ctx: any, isAutomation?: any) {
|
||||||
return execute(ctx, { rowsOnly: false })
|
return execute(ctx, { rowsOnly: false, isAutomation: isAutomation })
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeDynamicVariables = async (queryId: any) => {
|
const removeDynamicVariables = async (queryId: any) => {
|
||||||
|
|
|
@ -72,14 +72,18 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await queryController.executeV2(ctx)
|
await queryController.executeV2(ctx, true)
|
||||||
|
|
||||||
const { data, ...rest } = ctx.body
|
const { data, ...rest } = ctx.body
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: data,
|
response: data,
|
||||||
info: rest,
|
info: rest,
|
||||||
success: true,
|
success: true,
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
info: {},
|
info: {},
|
||||||
|
|
|
@ -22,6 +22,48 @@ export const save = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const bulkSave = async (ctx: any) => {
|
||||||
|
let { users: newUsers, groups } = ctx.request.body
|
||||||
|
let usersToSave: any[] = []
|
||||||
|
let groupsToSave: any[] = []
|
||||||
|
const db = tenancy.getGlobalDB()
|
||||||
|
|
||||||
|
newUsers.forEach((user: any) => {
|
||||||
|
usersToSave.push(
|
||||||
|
users.save(user, {
|
||||||
|
hashPassword: false,
|
||||||
|
requirePassword: user.requirePassword,
|
||||||
|
bulkCreate: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
if (groups.length) {
|
||||||
|
groups.forEach(async (groupId: string) => {
|
||||||
|
let oldGroup = await db.get(groupId)
|
||||||
|
groupsToSave.push(oldGroup)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const allUsers = await Promise.all(usersToSave)
|
||||||
|
let response = await db.bulkDocs(allUsers)
|
||||||
|
|
||||||
|
// delete passwords and add to group
|
||||||
|
allUsers.forEach(user => {
|
||||||
|
delete user.password
|
||||||
|
})
|
||||||
|
|
||||||
|
groupsToSave.forEach(async group => {
|
||||||
|
group.users = [...group.users, ...allUsers]
|
||||||
|
await db.put(group)
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.body = response
|
||||||
|
} catch (err: any) {
|
||||||
|
ctx.throw(err.status || 400, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const parseBooleanParam = (param: any) => {
|
const parseBooleanParam = (param: any) => {
|
||||||
return !(param && param === "false")
|
return !(param && param === "false")
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,13 @@ router
|
||||||
users.buildUserSaveValidation(),
|
users.buildUserSaveValidation(),
|
||||||
controller.save
|
controller.save
|
||||||
)
|
)
|
||||||
|
.post(
|
||||||
|
"/api/global/users/bulkSave",
|
||||||
|
adminOnly,
|
||||||
|
users.buildUserBulkSaveValidation(),
|
||||||
|
controller.bulkSave
|
||||||
|
)
|
||||||
|
|
||||||
.get("/api/global/users", builderOrAdmin, controller.fetch)
|
.get("/api/global/users", builderOrAdmin, controller.fetch)
|
||||||
.post("/api/global/users/search", builderOrAdmin, controller.search)
|
.post("/api/global/users/search", builderOrAdmin, controller.search)
|
||||||
.delete("/api/global/users/:id", adminOnly, controller.destroy)
|
.delete("/api/global/users/:id", adminOnly, controller.destroy)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import joiValidator from "../../../middleware/joi-validator"
|
import joiValidator from "../../../middleware/joi-validator"
|
||||||
import Joi from "joi"
|
import Joi from "joi"
|
||||||
|
|
||||||
export const buildUserSaveValidation = (isSelf = false) => {
|
|
||||||
let schema: any = {
|
let schema: any = {
|
||||||
email: Joi.string().allow(null, ""),
|
email: Joi.string().allow(null, ""),
|
||||||
password: Joi.string().allow(null, ""),
|
password: Joi.string().allow(null, ""),
|
||||||
|
@ -17,6 +16,8 @@ export const buildUserSaveValidation = (isSelf = false) => {
|
||||||
// maps appId -> roleId for the user
|
// maps appId -> roleId for the user
|
||||||
roles: Joi.object().pattern(/.*/, Joi.string()).required().unknown(true),
|
roles: Joi.object().pattern(/.*/, Joi.string()).required().unknown(true),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const buildUserSaveValidation = (isSelf = false) => {
|
||||||
if (!isSelf) {
|
if (!isSelf) {
|
||||||
schema = {
|
schema = {
|
||||||
...schema,
|
...schema,
|
||||||
|
@ -26,3 +27,19 @@ export const buildUserSaveValidation = (isSelf = false) => {
|
||||||
}
|
}
|
||||||
return joiValidator.body(Joi.object(schema).required().unknown(true))
|
return joiValidator.body(Joi.object(schema).required().unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const buildUserBulkSaveValidation = (isSelf = false) => {
|
||||||
|
if (!isSelf) {
|
||||||
|
schema = {
|
||||||
|
...schema,
|
||||||
|
_id: Joi.string(),
|
||||||
|
_rev: Joi.string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bulkSaveSchema = {
|
||||||
|
groups: Joi.array().optional(),
|
||||||
|
users: Joi.array().items(Joi.object(schema).required().unknown(true)),
|
||||||
|
}
|
||||||
|
|
||||||
|
return joiValidator.body(Joi.object(bulkSaveSchema).required().unknown(true))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue