Cache roles in store and add role selector for screens

This commit is contained in:
Andrew Kingston 2020-12-09 15:35:57 +00:00
parent 5882f76c9a
commit 4dd0d5b016
8 changed files with 70 additions and 92 deletions

View File

@ -6,6 +6,7 @@ const INITIAL_BACKEND_UI_STATE = {
tables: [], tables: [],
views: [], views: [],
users: [], users: [],
roles: [],
selectedDatabase: {}, selectedDatabase: {},
selectedTable: {}, selectedTable: {},
draftTable: {}, draftTable: {},
@ -177,6 +178,26 @@ export const getBackendUiStore = () => {
return state return state
}), }),
}, },
roles: {
fetch: async () => {
const response = await api.get("/api/roles")
const roles = await response.json()
store.update(state => {
state.roles = roles
return state
})
},
delete: async role => {
const response = await api.delete(`/api/roles/${role._id}/${role._rev}`)
await store.actions.roles.fetch()
return response
},
save: async role => {
const response = await api.post("/api/roles", role)
await store.actions.roles.fetch()
return response
},
},
} }
return store return store

View File

@ -1,42 +1,10 @@
<!-- Module scoped cache of saved role options -->
<script context="module">
import builderApi from "builderStore/api"
let cachedRoles
async function getRoles(force = false) {
if (cachedRoles && !force) {
return await cachedRoles
}
cachedRoles = new Promise(resolve => {
builderApi
.get("/api/roles")
.then(response => response.json())
.then(resolve)
})
return await cachedRoles
}
</script>
<script> <script>
import { backendUiStore } from "builderStore"
export let roleId export let roleId
let roleName $: role = $backendUiStore.roles.find(role => role._id === roleId)
$: getRole() $: roleName = role?.name ?? "Unknown role"
async function getRole() {
// Try to find a matching role
let roles = await getRoles()
let role = roles.find(role => role._id === roleId)
// If we didn't find a matching role, try updating the cached results
if (!role) {
let roles = await getRoles(true)
let role = roles.find(role => role._id === roleId)
}
role = roles.find(role => role._id === roleId)
roleName = role?.name ?? "Unknown role"
}
</script> </script>
<div>{roleName}</div> <div>{roleName}</div>

View File

@ -1,18 +1,14 @@
<script> <script>
import { onMount } from "svelte"
import { backendUiStore } from "builderStore" import { backendUiStore } from "builderStore"
import { notifier } from "builderStore/store/notifications" import { notifier } from "builderStore/store/notifications"
import RowFieldControl from "../RowFieldControl.svelte" import RowFieldControl from "../RowFieldControl.svelte"
import * as backendApi from "../api" import * as backendApi from "../api"
import builderApi from "builderStore/api"
import { ModalContent, Select } from "@budibase/bbui" import { ModalContent, Select } from "@budibase/bbui"
import ErrorsBox from "components/common/ErrorsBox.svelte" import ErrorsBox from "components/common/ErrorsBox.svelte"
export let row = {} export let row = {}
let errors = [] let errors = []
let roles = []
let rolesLoaded = false
$: creating = row?._id == null $: creating = row?._id == null
$: table = row.tableId $: table = row.tableId
@ -56,14 +52,6 @@
notifier.success("User saved successfully.") notifier.success("User saved successfully.")
backendUiStore.actions.rows.save(rowResponse) backendUiStore.actions.rows.save(rowResponse)
} }
const fetchRoles = async () => {
const rolesResponse = await builderApi.get("/api/roles")
roles = await rolesResponse.json()
rolesLoaded = true
}
onMount(fetchRoles)
</script> </script>
<ModalContent <ModalContent
@ -80,19 +68,17 @@
bind:value={row.password} /> bind:value={row.password} />
<!-- Defer rendering this select until roles load, otherwise the initial <!-- Defer rendering this select until roles load, otherwise the initial
selection is always undefined --> selection is always undefined -->
{#if rolesLoaded} <Select
<Select thin
thin secondary
secondary label="Role"
label="Role" data-cy="roleId-select"
data-cy="roleId-select" bind:value={row.roleId}>
bind:value={row.roleId}> <option value="">Choose an option</option>
<option value="">Choose an option</option> {#each $backendUiStore.roles as role}
{#each roles as role} <option value={role._id}>{role.name}</option>
<option value={role._id}>{role.name}</option> {/each}
{/each} </Select>
</Select>
{/if}
{#each customSchemaKeys as [key, meta]} {#each customSchemaKeys as [key, meta]}
<RowFieldControl {meta} bind:value={row[key]} {creating} /> <RowFieldControl {meta} bind:value={row[key]} {creating} />
{/each} {/each}

View File

@ -4,27 +4,26 @@
import api from "builderStore/api" import api from "builderStore/api"
import { notifier } from "builderStore/store/notifications" import { notifier } from "builderStore/store/notifications"
import ErrorsBox from "components/common/ErrorsBox.svelte" import ErrorsBox from "components/common/ErrorsBox.svelte"
import { backendUiStore } from "builderStore"
let roles = []
let permissions = [] let permissions = []
let selectedRole = {} let selectedRole = {}
let errors = [] let errors = []
$: selectedRoleId = selectedRole._id $: selectedRoleId = selectedRole._id
$: otherRoles = roles.filter(role => role._id !== selectedRoleId) $: otherRoles = $backendUiStore.roles.filter(
role => role._id !== selectedRoleId
)
$: isCreating = selectedRoleId == null || selectedRoleId === "" $: isCreating = selectedRoleId == null || selectedRoleId === ""
// Loads available roles and permissions from the server const fetchPermissions = async () => {
const fetchRoles = async () => {
const rolesResponse = await api.get("/api/roles")
roles = await rolesResponse.json()
const permissionsResponse = await api.get("/api/permissions") const permissionsResponse = await api.get("/api/permissions")
permissions = await permissionsResponse.json() permissions = await permissionsResponse.json()
} }
// Changes the seleced role // Changes the selected role
const changeRole = event => { const changeRole = event => {
const id = event?.target?.value const id = event?.target?.value
const role = roles.find(role => role._id === id) const role = $backendUiStore.roles.find(role => role._id === id)
if (role) { if (role) {
selectedRole = { selectedRole = {
...role, ...role,
@ -61,7 +60,7 @@
} }
// Save/create the role // Save/create the role
const response = await api.post("/api/roles", selectedRole) const response = await backendUiStore.actions.roles.save(selectedRole)
if (response.status === 200) { if (response.status === 200) {
notifier.success("Role saved successfully.") notifier.success("Role saved successfully.")
} else { } else {
@ -72,11 +71,8 @@
// Deletes the selected role // Deletes the selected role
const deleteRole = async () => { const deleteRole = async () => {
const response = await api.delete( const response = await backendUiStore.actions.roles.delete(selectedRole)
`/api/roles/${selectedRole._id}/${selectedRole._rev}`
)
if (response.status === 200) { if (response.status === 200) {
await fetchRoles()
changeRole() changeRole()
notifier.success("Role deleted successfully.") notifier.success("Role deleted successfully.")
} else { } else {
@ -84,7 +80,7 @@
} }
} }
onMount(fetchRoles) onMount(fetchPermissions)
</script> </script>
<ModalContent <ModalContent
@ -101,7 +97,7 @@
value={selectedRoleId} value={selectedRoleId}
on:change={changeRole}> on:change={changeRole}>
<option value="">Create new role</option> <option value="">Create new role</option>
{#each roles as role} {#each $backendUiStore.roles as role}
<option value={role._id}>{role.name}</option> <option value={role._id}>{role.name}</option>
{/each} {/each}
</Select> </Select>

View File

@ -15,7 +15,6 @@
let templateIndex let templateIndex
let draftScreen let draftScreen
let createLink = true let createLink = true
let roles = []
let roleId = "BASIC" let roleId = "BASIC"
$: templates = getTemplates($store, $backendUiStore.tables) $: templates = getTemplates($store, $backendUiStore.tables)
@ -30,11 +29,6 @@
} }
} }
const fetchRoles = async () => {
const response = await api.get("/api/roles")
roles = await response.json()
}
const templateChanged = newTemplateIndex => { const templateChanged = newTemplateIndex => {
if (newTemplateIndex === undefined) return if (newTemplateIndex === undefined) return
const template = templates[newTemplateIndex] const template = templates[newTemplateIndex]
@ -96,8 +90,6 @@
route = "/" + event.target.value route = "/" + event.target.value
} }
} }
onMount(fetchRoles)
</script> </script>
<ModalContent title="New Screen" confirmText="Create Screen" onConfirm={save}> <ModalContent title="New Screen" confirmText="Create Screen" onConfirm={save}>
@ -118,12 +110,10 @@
error={routeError} error={routeError}
bind:value={route} bind:value={route}
on:change={routeChanged} /> on:change={routeChanged} />
{#if roles.length} <Select label="Access" bind:value={roleId} secondary>
<Select label="Access" bind:value={roleId} secondary> {#each $backendUiStore as role}
{#each roles as role} <option value={role._id}>{role.name}</option>
<option value={role._id}>{role.name}</option> {/each}
{/each} </Select>
</Select>
{/if}
<Toggle text="Create link in navigation bar" bind:checked={createLink} /> <Toggle text="Create link in navigation bar" bind:checked={createLink} />
</ModalContent> </ModalContent>

View File

@ -0,0 +1,15 @@
<script>
import { Select } from "@budibase/bbui"
import { backendUiStore } from "builderStore"
export let value
let roles = []
</script>
<Select bind:value extraThin secondary on:change>
<option value="">Choose an option</option>
{#each $backendUiStore.roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>

View File

@ -4,6 +4,7 @@
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import PropertyControl from "./PropertyControl.svelte" import PropertyControl from "./PropertyControl.svelte"
import LayoutSelect from "./LayoutSelect.svelte" import LayoutSelect from "./LayoutSelect.svelte"
import RoleSelect from "./RoleSelect.svelte"
import Input from "./PropertyPanelControls/Input.svelte" import Input from "./PropertyPanelControls/Input.svelte"
import { excludeProps } from "./propertyCategories.js" import { excludeProps } from "./propertyCategories.js"
import { store, allScreens, currentAsset } from "builderStore" import { store, allScreens, currentAsset } from "builderStore"
@ -36,8 +37,8 @@
const screenDefinition = [ const screenDefinition = [
{ key: "description", label: "Description", control: Input }, { key: "description", label: "Description", control: Input },
{ key: "routing.route", label: "Route", control: Input }, { key: "routing.route", label: "Route", control: Input },
{ key: "routing.roleId", label: "Access", control: RoleSelect },
{ key: "layoutId", label: "Layout", control: LayoutSelect }, { key: "layoutId", label: "Layout", control: LayoutSelect },
{ key: "routing.roleId", label: "Role", control: Input },
] ]
const layoutDefinition = [{ key: "title", label: "Title", control: Input }] const layoutDefinition = [{ key: "title", label: "Title", control: Input }]

View File

@ -20,6 +20,7 @@
backendUiStore.actions.reset() backendUiStore.actions.reset()
await store.actions.initialise(pkg) await store.actions.initialise(pkg)
await automationStore.actions.fetch() await automationStore.actions.fetch()
await backendUiStore.actions.roles.fetch()
return pkg return pkg
} else { } else {
throw new Error(pkg) throw new Error(pkg)