refactor: function settings

This commit is contained in:
Arman
2023-05-22 16:57:17 +02:00
parent 30f0a3dd84
commit 4492059a2b
10 changed files with 599 additions and 511 deletions
@@ -1,520 +1,25 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import {
Box,
CardGrid,
DropList,
DropListItem,
Empty,
Output,
PaginationInline,
Secret
} from '$lib/components';
import Heading from '$lib/components/heading.svelte';
import { Roles } from '$lib/components/permissions';
import { Dependencies } from '$lib/constants';
import { Button, Form, FormList, InputCron, InputNumber, InputText } from '$lib/elements/forms';
import { symmetricDifference } from '$lib/helpers/array';
import { toLocaleDateTime } from '$lib/helpers/date';
import { Container } from '$lib/layout';
import { app } from '$lib/stores/app';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
import { onMount } from 'svelte';
import Variable from '../../createVariable.svelte';
import { execute, func } from '../store';
import UploadVariables from './uploadVariables.svelte';
import {
Table,
TableBody,
TableCell,
TableCellHead,
TableHeader,
TableRow
} from '$lib/elements/table';
import type { PageData } from './$types';
import Delete from './delete.svelte';
import UpdateEvents from './updateEvents.svelte';
import ExecuteFunction from './executeFunction.svelte';
import UpdateName from './updateName.svelte';
import UpdatePermissions from './updatePermissions.svelte';
import UpdateSchedule from './updateSchedule.svelte';
import UpdateVariables from './updateVariables.svelte';
import UpdateTimeout from './updateTimeout.svelte';
import DangerZone from './dangerZone.svelte';
export let data: PageData;
const functionId = $page.params.function;
let showDelete = false;
let selectedVar: Models.Variable = null;
let showVariablesUpload = false;
let showVariablesModal = false;
let showVariablesDropdown = [];
let timeout: number = null;
let functionName: string = null;
let functionSchedule: string = null;
let permissions: string[] = [];
let arePermsDisabled = true;
let offset = 0;
onMount(async () => {
timeout ??= $func.timeout;
functionName ??= $func.name;
functionSchedule ??= $func.schedule;
permissions = $func.execute;
});
async function updateName() {
try {
await sdk.forProject.functions.update(
functionId,
functionName,
$func.execute || undefined,
$func.events || undefined,
$func.schedule || undefined,
$func.timeout || undefined,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
message: 'Name has been updated',
type: 'success'
});
trackEvent(Submit.FunctionUpdateName);
} catch (error) {
addNotification({
message: error.message,
type: 'error'
});
trackError(error, Submit.FunctionUpdateName);
}
}
async function updatePermissions() {
try {
await sdk.forProject.functions.update(
functionId,
$func.name,
permissions,
$func.events || undefined,
$func.schedule || undefined,
$func.timeout || undefined,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
message: 'Permissions have been updated',
type: 'success'
});
trackEvent(Submit.FunctionUpdatePermissions);
} catch (error) {
addNotification({
message: error.message,
type: 'error'
});
trackError(error, Submit.FunctionUpdatePermissions);
}
}
async function updateSchedule() {
try {
await sdk.forProject.functions.update(
functionId,
$func.name,
$func.execute || undefined,
$func.events || undefined,
functionSchedule,
$func.timeout || undefined,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
type: 'success',
message: 'Cron Schedule has been updated'
});
trackEvent(Submit.FunctionUpdateSchedule);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.FunctionUpdateSchedule);
}
}
async function updateTimeout() {
try {
await sdk.forProject.functions.update(
functionId,
$func.name,
$func.execute || undefined,
$func.events || undefined,
$func.schedule || undefined,
timeout,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
type: 'success',
message: 'Timeout has been updated'
});
trackEvent(Submit.FunctionUpdateTimeout);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.FunctionUpdateTimeout);
}
}
async function handleVariableCreated(event: CustomEvent<Models.Variable>) {
const variable = event.detail;
try {
await sdk.forProject.functions.createVariable(functionId, variable.key, variable.value);
await invalidate(Dependencies.VARIABLES);
showVariablesModal = false;
addNotification({
type: 'success',
message: `${$func.name} variables have been updated`
});
trackEvent(Submit.VariableCreate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.VariableCreate);
}
}
async function handleVariableUpdated(event: CustomEvent<Models.Variable>) {
const variable = event.detail;
try {
await sdk.forProject.functions.updateVariable(
functionId,
variable.$id,
variable.key,
variable.value
);
await invalidate(Dependencies.VARIABLES);
selectedVar = null;
showVariablesModal = false;
addNotification({
type: 'success',
message: `${$func.name} variables have been updated`
});
trackEvent(Submit.VariableUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.VariableUpdate);
}
}
async function handleVariableDeleted(variable: Models.Variable) {
try {
await sdk.forProject.functions.deleteVariable(variable.functionId, variable.$id);
await invalidate(Dependencies.VARIABLES);
addNotification({
type: 'success',
message: `Variable has been deleted`
});
trackEvent(Submit.VariableDelete);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.VariableDelete);
}
}
function downloadVariables() {
if (data.variables.total) {
let content = data.variables.variables
.map((variable) => `${variable.key}=${variable.value}`)
.join('\n');
const file = new File([content], '.env', {
type: 'application/x-envoy'
});
const link = document.createElement('a');
const url = URL.createObjectURL(file);
link.href = url;
link.download = file.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}
}
$: if (permissions) {
if (symmetricDifference(permissions, $func.execute).length) {
arePermsDisabled = false;
} else arePermsDisabled = true;
}
export let data;
</script>
<Container>
<CardGrid>
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
<div class="avatar is-medium">
<img
src={`${base}/icons/${$app.themeInUse}/color/${
$func.runtime.split('-')[0]
}.svg`}
alt="technology" />
</div>
<div>
<Heading tag="h6" size="7">{$func.name}</Heading>
<p class="text u-capitalize">{$func.runtime}</p>
</div>
</div>
<svelte:fragment slot="aside">
<div class="u-flex u-main-space-between">
<div>
<p>Function ID: {$func.$id}</p>
<p>Created at: {toLocaleDateTime($func.$createdAt)}</p>
<p>Updated at: {toLocaleDateTime($func.$updatedAt)}</p>
</div>
</div>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button secondary on:click={() => ($execute = $func)}>Execute now</Button>
</svelte:fragment>
</CardGrid>
<Form onSubmit={updateName}>
<CardGrid>
<Heading tag="h6" size="7">Name</Heading>
<svelte:fragment slot="aside">
<ul>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autocomplete={false}
bind:value={functionName} />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={functionName === $func.name || !functionName} submit>
Update
</Button>
</svelte:fragment>
</CardGrid>
</Form>
<Form onSubmit={updatePermissions}>
<CardGrid>
<Heading tag="h6" size="7">Execute Access</Heading>
<p>
Choose who can execute this function using the client API. For more information,
check out the <a
href="https://appwrite.io/docs/permissions"
target="_blank"
rel="noopener noreferrer"
class="link">
Permissions Guide
</a>.
</p>
<svelte:fragment slot="aside">
<Roles bind:roles={permissions} />
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={arePermsDisabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<ExecuteFunction />
<UpdateName />
<UpdatePermissions />
<UpdateEvents />
<Form onSubmit={updateSchedule}>
<CardGrid>
<Heading tag="h6" size="7">Schedule</Heading>
<p>
Set a Cron schedule to trigger your function. Leave blank for no schedule. <a
href="https://en.wikipedia.org/wiki/Cron"
target="_blank"
rel="noopener noreferrer"
class="link">
More details on Cron syntax here.</a>
</p>
<svelte:fragment slot="aside">
<FormList>
<InputCron
bind:value={functionSchedule}
label="Schedule (Cron Syntax)"
id="schedule" />
</FormList>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={$func.schedule === functionSchedule} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<CardGrid>
<Heading tag="h6" size="7">Variables</Heading>
<p>Set the variables (or secret keys) that will be passed to your function at runtime.</p>
<svelte:fragment slot="aside">
<div class="u-flex u-margin-inline-start-auto u-gap-16">
<Button
secondary
event="download_env"
disabled={!data.variables.total}
on:click={downloadVariables}>
<span class="icon-download" />
<span class="text">Download .env file</span>
</Button>
<Button secondary on:click={() => (showVariablesUpload = true)}>
<span class="icon-upload" />
<span class="text">Import .env file</span>
</Button>
</div>
{@const limit = 10}
{@const sum = data.variables.total}
{#if sum}
<div class="u-flex u-flex-vertical u-gap-16">
<Table noMargin noStyles>
<TableHeader>
<TableCellHead>Key</TableCellHead>
<TableCellHead width={180}>Value</TableCellHead>
<TableCellHead width={30} />
</TableHeader>
<TableBody>
{#each data.variables.variables.slice(offset, offset + limit) as variable, i}
<TableRow>
<TableCell title="key">
<Output value={variable.key} hideCopyIcon>
{variable.key}
</Output>
</TableCell>
<TableCell showOverflow title="value">
<Secret copyEvent="variable" value={variable.value} />
</TableCell>
<TableCell showOverflow title="options">
<DropList
bind:show={showVariablesDropdown[i]}
placement="bottom-start"
noArrow>
<Button
text
round
ariaLabel="more options"
on:click={() =>
(showVariablesDropdown[i] =
!showVariablesDropdown[i])}>
<span
class="icon-dots-horizontal"
aria-hidden="true" />
</Button>
<svelte:fragment slot="list">
<DropListItem
icon="pencil"
on:click={() => {
selectedVar = variable;
showVariablesDropdown[i] = false;
showVariablesModal = true;
}}>
Edit
</DropListItem>
<DropListItem
icon="trash"
on:click={async () => {
handleVariableDeleted(variable);
showVariablesDropdown[i] = false;
}}>
Delete
</DropListItem>
</svelte:fragment>
</DropList>
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
<Button text noMargin on:click={() => (showVariablesModal = true)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text">Create variable</span>
</Button>
<div class="u-flex u-main-space-between">
<p class="text">Total variables: {sum}</p>
<PaginationInline {sum} {limit} bind:offset hidePages />
</div>
</div>
{:else}
<Empty on:click={() => (showVariablesModal = !showVariablesModal)}>
Create a variable to get started
</Empty>
{/if}
</svelte:fragment>
</CardGrid>
<Form onSubmit={updateTimeout}>
<CardGrid>
<Heading tag="h6" size="7">Timeout</Heading>
<p>
Limit the execution time of your function. Maximum value is 900 seconds (15
minutes).
</p>
<svelte:fragment slot="aside">
<FormList>
<InputNumber
min={1}
max={900}
id="time"
label="Time (in seconds)"
bind:value={timeout} />
</FormList>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={$func.timeout === timeout || timeout < 1} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<CardGrid danger>
<Heading tag="h6" size="7">Delete Function</Heading>
<p>
The function will be permanently deleted, including all deployments associated with it.
This action is irreversible.
</p>
<svelte:fragment slot="aside">
<Box>
<svelte:fragment slot="title">
<h6 class="u-bold u-trim-1">{$func.name}</h6>
</svelte:fragment>
<p>Last Updated: {toLocaleDateTime($func.$updatedAt)}</p>
</Box>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button secondary on:click={() => (showDelete = true)}>Delete</Button>
</svelte:fragment>
</CardGrid>
<UpdateSchedule />
<UpdateVariables variableList={data.variables} />
<UpdateTimeout />
<DangerZone />
</Container>
<Delete bind:showDelete />
{#if showVariablesModal}
<Variable
bind:selectedVar
bind:showCreate={showVariablesModal}
on:created={handleVariableCreated}
on:updated={handleVariableUpdated} />
{/if}
{#if showVariablesUpload}
<UploadVariables bind:show={showVariablesUpload} />
{/if}
@@ -0,0 +1,33 @@
<script lang="ts">
import { Box, CardGrid } from '$lib/components';
import Heading from '$lib/components/heading.svelte';
import { Button } from '$lib/elements/forms';
import { toLocaleDateTime } from '$lib/helpers/date';
import Delete from './deleteModal.svelte';
import { func } from '../store';
let showDelete = false;
</script>
<CardGrid danger>
<Heading tag="h6" size="7">Delete Function</Heading>
<p>
The function will be permanently deleted, including all deployments associated with it. This
action is irreversible.
</p>
<svelte:fragment slot="aside">
<Box>
<svelte:fragment slot="title">
<h6 class="u-bold u-trim-1">{$func.name}</h6>
</svelte:fragment>
<p>Last Updated: {toLocaleDateTime($func.$updatedAt)}</p>
</Box>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button secondary on:click={() => (showDelete = true)}>Delete</Button>
</svelte:fragment>
</CardGrid>
<Delete bind:showDelete />
@@ -0,0 +1,36 @@
<script lang="ts">
import { base } from '$app/paths';
import { CardGrid, Heading } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { toLocaleDateTime } from '$lib/helpers/date';
import { app } from '$lib/stores/app';
import { execute, func } from '../store';
</script>
<CardGrid>
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
<div class="avatar is-medium">
<img
src={`${base}/icons/${$app.themeInUse}/color/${$func.runtime.split('-')[0]}.svg`}
alt="technology" />
</div>
<div>
<Heading tag="h6" size="7">{$func.name}</Heading>
<p class="text u-capitalize">{$func.runtime}</p>
</div>
</div>
<svelte:fragment slot="aside">
<div class="u-flex u-main-space-between">
<div>
<p>Function ID: {$func.$id}</p>
<p>Created at: {toLocaleDateTime($func.$createdAt)}</p>
<p>Updated at: {toLocaleDateTime($func.$updatedAt)}</p>
</div>
</div>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button secondary on:click={() => ($execute = $func)}>Execute now</Button>
</svelte:fragment>
</CardGrid>
@@ -0,0 +1,66 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, InputText } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { func } from '../store';
const functionId = $page.params.function;
let functionName: string = null;
onMount(async () => {
functionName ??= $func.name;
});
async function updateName() {
try {
await sdk.forProject.functions.update(
functionId,
functionName,
$func.execute || undefined,
$func.events || undefined,
$func.schedule || undefined,
$func.timeout || undefined,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
message: 'Name has been updated',
type: 'success'
});
trackEvent(Submit.FunctionUpdateName);
} catch (error) {
addNotification({
message: error.message,
type: 'error'
});
trackError(error, Submit.FunctionUpdateName);
}
}
</script>
<Form onSubmit={updateName}>
<CardGrid>
<Heading tag="h6" size="7">Name</Heading>
<svelte:fragment slot="aside">
<ul>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autocomplete={false}
bind:value={functionName} />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={functionName === $func.name || !functionName} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -0,0 +1,78 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { func } from '../store';
import { Roles } from '$lib/components/permissions';
import { symmetricDifference } from '$lib/helpers/array';
const functionId = $page.params.function;
let arePermsDisabled = true;
let permissions: string[] = [];
onMount(async () => {
permissions = $func.execute;
});
async function updatePermissions() {
try {
await sdk.forProject.functions.update(
functionId,
$func.name,
permissions,
$func.events || undefined,
$func.schedule || undefined,
$func.timeout || undefined,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
message: 'Permissions have been updated',
type: 'success'
});
trackEvent(Submit.FunctionUpdatePermissions);
} catch (error) {
addNotification({
message: error.message,
type: 'error'
});
trackError(error, Submit.FunctionUpdatePermissions);
}
}
$: if (permissions) {
if (symmetricDifference(permissions, $func.execute).length) {
arePermsDisabled = false;
} else arePermsDisabled = true;
}
</script>
<Form onSubmit={updatePermissions}>
<CardGrid>
<Heading tag="h6" size="7">Execute Access</Heading>
<p>
Choose who can execute this function using the client API. For more information, check
out the <a
href="https://appwrite.io/docs/permissions"
target="_blank"
rel="noopener noreferrer"
class="link">
Permissions Guide
</a>.
</p>
<svelte:fragment slot="aside">
<Roles bind:roles={permissions} />
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={arePermsDisabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -0,0 +1,71 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, FormList, InputCron } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { func } from '../store';
const functionId = $page.params.function;
let functionSchedule: string = null;
onMount(async () => {
functionSchedule ??= $func.schedule;
});
async function updateSchedule() {
try {
await sdk.forProject.functions.update(
functionId,
$func.name,
$func.execute || undefined,
$func.events || undefined,
functionSchedule,
$func.timeout || undefined,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
type: 'success',
message: 'Cron Schedule has been updated'
});
trackEvent(Submit.FunctionUpdateSchedule);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.FunctionUpdateSchedule);
}
}
</script>
<Form onSubmit={updateSchedule}>
<CardGrid>
<Heading tag="h6" size="7">Schedule</Heading>
<p>
Set a Cron schedule to trigger your function. Leave blank for no schedule. <a
href="https://en.wikipedia.org/wiki/Cron"
target="_blank"
rel="noopener noreferrer"
class="link">
More details on Cron syntax here.</a>
</p>
<svelte:fragment slot="aside">
<FormList>
<InputCron
bind:value={functionSchedule}
label="Schedule (Cron Syntax)"
id="schedule" />
</FormList>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={$func.schedule === functionSchedule} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -0,0 +1,66 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Form, FormList, InputNumber } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { func } from '../store';
const functionId = $page.params.function;
let timeout: number = null;
onMount(async () => {
timeout ??= $func.timeout;
});
async function updateTimeout() {
try {
await sdk.forProject.functions.update(
functionId,
$func.name,
$func.execute || undefined,
$func.events || undefined,
$func.schedule || undefined,
timeout,
$func.enabled
);
await invalidate(Dependencies.FUNCTION);
addNotification({
type: 'success',
message: 'Timeout has been updated'
});
trackEvent(Submit.FunctionUpdateTimeout);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.FunctionUpdateTimeout);
}
}
</script>
<Form onSubmit={updateTimeout}>
<CardGrid>
<Heading tag="h6" size="7">Timeout</Heading>
<p>Limit the execution time of your function. Maximum value is 900 seconds (15 minutes).</p>
<svelte:fragment slot="aside">
<FormList>
<InputNumber
min={1}
max={900}
id="time"
label="Time (in seconds)"
bind:value={timeout} />
</FormList>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={$func.timeout === timeout || timeout < 1} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -0,0 +1,233 @@
<script lang="ts">
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
import {
Table,
TableBody,
TableCell,
TableCellHead,
TableHeader,
TableRow
} from '$lib/elements/table';
import { Button } from '$lib/elements/forms';
import {
CardGrid,
Heading,
DropList,
DropListItem,
Empty,
Output,
PaginationInline,
Secret
} from '$lib/components';
import Variable from '../../createVariable.svelte';
import UploadVariables from './uploadVariablesModal.svelte';
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Dependencies } from '$lib/constants';
import { addNotification } from '$lib/stores/notifications';
import { func } from '../store';
export let variableList: Models.VariableList;
const functionId = $page.params.function;
let showVariablesDropdown = [];
let selectedVar: Models.Variable = null;
let showVariablesUpload = false;
let showVariablesModal = false;
let offset = 0;
async function handleVariableCreated(event: CustomEvent<Models.Variable>) {
const variable = event.detail;
try {
await sdk.forProject.functions.createVariable(functionId, variable.key, variable.value);
await invalidate(Dependencies.VARIABLES);
showVariablesModal = false;
addNotification({
type: 'success',
message: `${$func.name} variables have been updated`
});
trackEvent(Submit.VariableCreate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.VariableCreate);
}
}
async function handleVariableUpdated(event: CustomEvent<Models.Variable>) {
const variable = event.detail;
try {
await sdk.forProject.functions.updateVariable(
functionId,
variable.$id,
variable.key,
variable.value
);
await invalidate(Dependencies.VARIABLES);
selectedVar = null;
showVariablesModal = false;
addNotification({
type: 'success',
message: `${$func.name} variables have been updated`
});
trackEvent(Submit.VariableUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.VariableUpdate);
}
}
async function handleVariableDeleted(variable: Models.Variable) {
try {
await sdk.forProject.functions.deleteVariable(variable.functionId, variable.$id);
await invalidate(Dependencies.VARIABLES);
addNotification({
type: 'success',
message: `Variable has been deleted`
});
trackEvent(Submit.VariableDelete);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.VariableDelete);
}
}
function downloadVariables() {
if (variableList.total) {
let content = variableList.variables
.map((variable) => `${variable.key}=${variable.value}`)
.join('\n');
const file = new File([content], '.env', {
type: 'application/x-envoy'
});
const link = document.createElement('a');
const url = URL.createObjectURL(file);
link.href = url;
link.download = file.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}
}
</script>
<CardGrid>
<Heading tag="h6" size="7">Variables</Heading>
<p>Set the variables (or secret keys) that will be passed to your function at runtime.</p>
<svelte:fragment slot="aside">
<div class="u-flex u-margin-inline-start-auto u-gap-16">
<Button
secondary
event="download_env"
disabled={!variableList.total}
on:click={downloadVariables}>
<span class="icon-download" />
<span class="text">Download .env file</span>
</Button>
<Button secondary on:click={() => (showVariablesUpload = true)}>
<span class="icon-upload" />
<span class="text">Import .env file</span>
</Button>
</div>
{@const limit = 10}
{@const sum = variableList.total}
{#if sum}
<div class="u-flex u-flex-vertical u-gap-16">
<Table noMargin noStyles>
<TableHeader>
<TableCellHead>Key</TableCellHead>
<TableCellHead width={180}>Value</TableCellHead>
<TableCellHead width={30} />
</TableHeader>
<TableBody>
{#each variableList.variables.slice(offset, offset + limit) as variable, i}
<TableRow>
<TableCell title="key">
<Output value={variable.key} hideCopyIcon>
{variable.key}
</Output>
</TableCell>
<TableCell showOverflow title="value">
<Secret copyEvent="variable" value={variable.value} />
</TableCell>
<TableCell showOverflow title="options">
<DropList
bind:show={showVariablesDropdown[i]}
placement="bottom-start"
noArrow>
<Button
text
round
ariaLabel="more options"
on:click={() =>
(showVariablesDropdown[i] =
!showVariablesDropdown[i])}>
<span class="icon-dots-horizontal" aria-hidden="true" />
</Button>
<svelte:fragment slot="list">
<DropListItem
icon="pencil"
on:click={() => {
selectedVar = variable;
showVariablesDropdown[i] = false;
showVariablesModal = true;
}}>
Edit
</DropListItem>
<DropListItem
icon="trash"
on:click={async () => {
handleVariableDeleted(variable);
showVariablesDropdown[i] = false;
}}>
Delete
</DropListItem>
</svelte:fragment>
</DropList>
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
<Button text noMargin on:click={() => (showVariablesModal = true)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text">Create variable</span>
</Button>
<div class="u-flex u-main-space-between">
<p class="text">Total variables: {sum}</p>
<PaginationInline {sum} {limit} bind:offset hidePages />
</div>
</div>
{:else}
<Empty on:click={() => (showVariablesModal = !showVariablesModal)}>
Create a variable to get started
</Empty>
{/if}
</svelte:fragment>
</CardGrid>
{#if showVariablesModal}
<Variable
bind:selectedVar
bind:showCreate={showVariablesModal}
on:created={handleVariableCreated}
on:updated={handleVariableUpdated} />
{/if}
{#if showVariablesUpload}
<UploadVariables bind:show={showVariablesUpload} />
{/if}