Auth config forms

This commit is contained in:
Rory Powell 2021-12-11 09:59:09 +00:00
parent 431a22aaf0
commit 77d17e143d
5 changed files with 246 additions and 69 deletions

View File

@ -21,6 +21,7 @@
export let showSecondaryButton = false
export let secondaryButtonText = undefined
export let secondaryAction = undefined
export let secondaryButtonWarning = false
const { hide, cancel } = getContext(Context.Modal)
let loading = false
@ -88,8 +89,11 @@
{#if showSecondaryButton && secondaryButtonText && secondaryAction}
<div class="secondary-action">
<Button group secondary on:click={secondary}
>{secondaryButtonText}</Button
<Button
group
secondary
warning={secondaryButtonWarning}
on:click={secondary}>{secondaryButtonText}</Button
>
</div>
{/if}

View File

@ -41,7 +41,7 @@
<Body size="S">
Create an authentication config that can be shared with queries.
</Body>
<RestAuthenticationBuilder bind:authConfigs={datasource.config.authConfigs} />
<RestAuthenticationBuilder bind:configs={datasource.config.authConfigs} />
<style>
.section-header {

View File

@ -3,11 +3,11 @@
export let value
const renderAuthType = () => {
const renderAuthType = value => {
return AUTH_TYPE_LABELS.filter(type => type.value === value).map(
type => type.label
)
}
</script>
{renderAuthType()}
{renderAuthType(value)}

View File

@ -1,22 +1,12 @@
<script>
import {
Table,
Modal,
ModalContent,
Layout,
ActionButton,
Select,
Body,
Input,
} from "@budibase/bbui"
import { Table, Modal, Layout, ActionButton } from "@budibase/bbui"
import AuthTypeRenderer from "./AuthTypeRenderer.svelte"
import { AUTH_TYPE_LABELS, AUTH_TYPES } from "./authTypes"
import RestAuthenticationModal from "./RestAuthenticationModal.svelte"
export let authConfigs = []
export let configs = []
let currentConfig = null
let modal
let currentConfig
let isNew = false
const schema = {
name: "",
@ -24,73 +14,44 @@
}
const openConfigModal = config => {
if (!config) {
currentConfig = {
config: {},
}
isNew = true
} else {
currentConfig = { ...config }
isNew = false
}
currentConfig = config
modal.show()
}
const onConfirm = () => {
if (isNew) {
authConfigs.push(currentConfig)
} else {
authConfigs = authConfigs.map(c => {
const onConfirm = config => {
if (currentConfig) {
// TODO: Update with _id
configs = configs.map(c => {
// replace the current config with the new one
if (c.name === currentConfig.name) {
return currentConfig
return config
}
return c
})
} else {
configs.push(config)
configs = [...configs]
}
}
const onCancel = () => {
currentConfig = {}
const onDelete = () => {
// TODO: Update with _id
configs = configs.filter(c => {
return c.name !== currentConfig.name
})
}
</script>
<Modal bind:this={modal}>
<ModalContent
title={isNew ? "Add Authentication" : "Update Authentication"}
{onConfirm}
{onCancel}
confirmText={isNew ? "Add" : "Update"}
cancelText="Cancel"
size="M"
>
<Layout gap="S">
<Body size="S">
The authorization header will be automatically generated when you
sendthe request.
</Body>
<Select
label="Type"
bind:value={currentConfig.type}
options={AUTH_TYPE_LABELS}
on:change={({ detail }) => (currentConfig.type = detail)}
/>
{#if currentConfig.type === AUTH_TYPES.BASIC}
<Input label="Username" bind:value={currentConfig.config.username} />
<Input label="Password" bind:value={currentConfig.config.password} />
{/if}
{#if currentConfig.type === AUTH_TYPES.BEARER}
<Input label="Token" bind:value={currentConfig.config.token} />
{/if}
</Layout>
</ModalContent>
<RestAuthenticationModal {configs} {currentConfig} {onConfirm} {onDelete} />
</Modal>
<Layout gap="S" noPadding>
{#if authConfigs && authConfigs.length > 0}
{#if configs && configs.length > 0}
<Table
on:click={({ detail }) => openConfigModal(detail)}
{schema}
data={authConfigs}
data={configs}
allowEditColumns={false}
allowEditRows={false}
allowSelectRows={false}
@ -103,6 +64,3 @@
>
</div>
</Layout>
<style>
</style>

View File

@ -0,0 +1,215 @@
<script>
import { onMount } from "svelte"
import { ModalContent, Layout, Select, Body, Input } from "@budibase/bbui"
import { AUTH_TYPE_LABELS, AUTH_TYPES } from "./authTypes"
export let configs
export let currentConfig
export let onConfirm
export let onDelete
let form = {
basic: {},
bearer: {},
}
let errors = {
basic: {},
bearer: {},
}
let blurred = {
basic: {},
bearer: {},
}
let hasErrors = false
let hasChanged = false
onMount(() => {
if (currentConfig) {
deconstructConfig()
}
})
/**
* map the current config's data into the form by type
*/
const deconstructConfig = () => {
form.name = currentConfig.name
form.type = currentConfig.type
if (currentConfig.type === AUTH_TYPES.BASIC) {
form.basic = {
...currentConfig.config,
}
} else if (currentConfig.type === AUTH_TYPES.BEARER) {
form.bearer = {
...currentConfig.config,
}
}
}
/**
* map the form into a new config to save by type
*/
const constructConfig = () => {
const newConfig = {
name: form.name,
type: form.type,
}
if (form.type === AUTH_TYPES.BASIC) {
newConfig.config = {
...form.basic,
}
} else if (form.type === AUTH_TYPES.BEARER) {
newConfig.config = {
...form.bearer,
}
}
return newConfig
}
/**
* compare the existing config with the new config to see if there are any changes
*/
const checkChanged = () => {
if (currentConfig) {
hasChanged =
JSON.stringify(currentConfig) !== JSON.stringify(constructConfig())
} else {
hasChanged = true
}
}
const checkErrors = () => {
hasErrors = false
// NAME
const nameError = () => {
// Unique name
if (form.name) {
errors.name =
// check for duplicate excluding the current config
configs.find(
c => c.name === form.name && c.name !== currentConfig?.name
) !== undefined
? "Name must be unique"
: null
}
// Name required
else {
errors.name = "Name is required"
}
return !!errors.name
}
// TYPE
const typeError = () => {
errors.type = form.type ? null : "Type is required"
return !!errors.type
}
// BASIC AUTH
const basicAuthErrors = () => {
errors.basic.username = form.basic.username
? null
: "Username is required"
errors.basic.password = form.basic.password
? null
: "Password is required"
return !!(errors.basic.username || errors.basic.password || commonError)
}
// BEARER TOKEN
const bearerTokenErrors = () => {
errors.bearer.token = form.bearer.token ? null : "Token is required"
return !!(errors.bearer.token || commonError)
}
const commonError = nameError() || typeError()
if (form.type === AUTH_TYPES.BASIC) {
hasErrors = basicAuthErrors() || commonError
} else if (form.type === AUTH_TYPES.BEARER) {
hasErrors = bearerTokenErrors() || commonError
} else {
hasErrors = !!commonError
}
}
const onFieldChange = () => {
checkErrors()
checkChanged()
}
const onConfirmInternal = () => {
onConfirm(constructConfig())
}
</script>
<ModalContent
title={currentConfig ? "Update Authentication" : "Add Authentication"}
onConfirm={onConfirmInternal}
confirmText={currentConfig ? "Update" : "Add"}
disabled={hasErrors || !hasChanged}
cancelText={"Cancel"}
warning={true}
size="M"
showSecondaryButton={!!currentConfig}
secondaryButtonText={"Delete"}
secondaryAction={onDelete}
secondaryButtonWarning={true}
>
<Layout gap="S">
<Body size="S">
The authorization header will be automatically generated when you send the
request.
</Body>
<Input
label="Name"
bind:value={form.name}
on:change={onFieldChange}
on:blur={() => (blurred.name = true)}
error={blurred.name ? errors.name : null}
/>
<Select
label="Type"
bind:value={form.type}
on:change={onFieldChange}
options={AUTH_TYPE_LABELS}
on:blur={() => (blurred.type = true)}
error={blurred.type ? errors.type : null}
/>
{#if form.type === AUTH_TYPES.BASIC}
<Input
label="Username"
bind:value={form.basic.username}
on:change={onFieldChange}
on:blur={() => (blurred.basic.username = true)}
error={blurred.basic.username ? errors.basic.username : null}
/>
<Input
label="Password"
bind:value={form.basic.password}
on:change={onFieldChange}
on:blur={() => (blurred.basic.password = true)}
error={blurred.basic.password ? errors.basic.password : null}
/>
{/if}
{#if form.type === AUTH_TYPES.BEARER}
<Input
label="Token"
bind:value={form.bearer.token}
on:change={onFieldChange}
on:blur={() => (blurred.bearer.token = true)}
error={blurred.bearer.token ? errors.bearer.token : null}
/>
{/if}
</Layout>
</ModalContent>
<style>
</style>