Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat variable specifications #1301

Merged
merged 15 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/lib/elements/forms/inputSelect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
export let options: {
value: string | boolean | number | null;
label: string;
disabled?: boolean;
}[];
export let isMultiple = false;
export let fullWidth = false;
Expand Down Expand Up @@ -100,7 +101,10 @@
<option value={null} disabled selected hidden>{placeholder}</option>
{/if}
{#each options as option}
<option value={option.value} selected={option.value === value}>
<option
value={option.value}
selected={option.value === value}
disabled={option.disabled}>
{option.label}
</option>
{/each}
Expand Down
8 changes: 8 additions & 0 deletions src/lib/stores/specifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { page } from '$app/stores';
import type { Models } from '@appwrite.io/console';
import { derived } from 'svelte/store';

export const specificationsList = derived(
page,
async ($page) => (await $page.data.specificationsList) as Models.SpecificationList
);
24 changes: 24 additions & 0 deletions src/lib/wizards/functions/components/specificationsTooltip.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script lang="ts">
import { Button } from '$lib/elements/forms';
import { upgradeURL } from '$lib/stores/billing';
</script>

<div class="u-flex-vertical u-gap-16">
<p>Additional CPU and RAM are available for Pro and Scale teams.</p>
<div class="u-flex u-gap-8 u-cross-center">
<a
class="u-bold u-block"
target="_blank"
rel="noopener noreferrer"
href="https://appwrite.io/docs/advanced/platform/gb-hours">Learn more</a>
<Button href={$upgradeURL} secondary fullWidthMobile event={'specifications_cta_upgrade'}>
<span class="text">Upgrade Plan</span>
</Button>
</div>
</div>

<style>
a {
flex-grow: 1;
}
</style>
3 changes: 2 additions & 1 deletion src/lib/wizards/functions/cover.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
repositoryBehaviour: 'new',
repositoryName: template.id,
repositoryPrivate: true,
repositoryId: null
repositoryId: null,
specification: null
});
wizard.start(CreateTemplate);
}
Expand Down
13 changes: 12 additions & 1 deletion src/lib/wizards/functions/createManual.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,18 @@
undefined,
undefined,
$createFunction.entrypoint,
$createFunction.commands || undefined
$createFunction.commands || undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
$createFunction.specification
);
await sdk.forProject.functions.createDeployment(
response.$id,
Expand Down
6 changes: 4 additions & 2 deletions src/lib/wizards/functions/createTemplate.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
$template.providerRepositoryId || undefined,
$template.providerOwner || undefined,
runtimeDetail.providerRootDirectory || undefined,
$template.providerVersion || undefined
$template.providerVersion || undefined,
$templateConfig.specification || undefined
);

if ($templateConfig.variables) {
Expand All @@ -78,7 +79,8 @@
customId: !!response.$id,
runtime: response.runtime,
deployment_type: $templateConfig.repositoryBehaviour,
scopes: $templateConfig.scopes
scopes: $templateConfig.scopes,
specification: $templateConfig.specification
});
resetState();
} catch (error) {
Expand Down
26 changes: 26 additions & 0 deletions src/lib/wizards/functions/steps/manualDetails.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,30 @@
import { onMount } from 'svelte';
import { createFunction } from '../store';
import { runtimesList } from '$lib/stores/runtimes';
import { specificationsList } from '$lib/stores/specifications';
import { isCloud } from '$lib/system';
import { organization } from '$lib/stores/organization';
import { BillingPlan } from '$lib/constants';
import SpecificationsTooltip from '../components/specificationsTooltip.svelte';

let showCustomId = false;

let options = [];
let specificationOptions = [];

onMount(async () => {
options = (await $runtimesList).runtimes.map((runtime) => ({
label: `${runtime.name} - ${runtime.version}`,
value: runtime.$id
}));

specificationOptions = (await $specificationsList).specifications.map((size) => ({
label:
`${size.cpus} CPU, ${size.memory} MB RAM` +
(!size.enabled ? ` (Upgrade to use this)` : ''),
value: size.slug,
disabled: !size.enabled
}));
});
</script>

Expand All @@ -39,6 +53,18 @@
{options}
required />

<InputSelect
label="CPU and memory"
id="specification"
placeholder="Select specification"
required
disabled={specificationOptions.length < 1}
options={specificationOptions}
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
ernstmul marked this conversation as resolved.
Show resolved Hide resolved
? SpecificationsTooltip
: null}
bind:value={$createFunction.specification} />

{#if !showCustomId}
<div>
<Pill button on:click={() => (showCustomId = !showCustomId)}>
Expand Down
47 changes: 47 additions & 0 deletions src/lib/wizards/functions/steps/templateConfiguration.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@
import { FormList, InputSelect, InputText } from '$lib/elements/forms';
import { WizardStep } from '$lib/layout';
import { runtimesList } from '$lib/stores/runtimes';
import { specificationsList } from '$lib/stores/specifications';
import { BillingPlan } from '$lib/constants';
import SpecificationsTooltip from '../components/specificationsTooltip.svelte';
import { template, templateConfig } from '../store';
import { organization } from '$lib/stores/organization';
import { isCloud } from '$lib/system';

let showCustomId = false;

async function beforeSubmit() {
if (!$templateConfig.runtime) {
throw new Error('Please select a runtime.');
}

if (!$templateConfig.specification) {
throw new Error('Please select a specification.');
}
Comment on lines +21 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try this flow?
I think template without specification is okay - it can take default value

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

async function loadRuntimes() {
Expand All @@ -27,6 +36,22 @@

return options;
}

async function loadSpecifications() {
const specificationOptions = (await $specificationsList).specifications.map((size) => ({
label:
`${size.cpus} CPU, ${size.memory} MB RAM` +
(!size.enabled ? ` (Upgrade to use this)` : ''),
value: size.slug,
disabled: !size.enabled
}));

if (!$templateConfig.specification && specificationOptions.length > 0) {
$templateConfig.specification = specificationOptions[0].value;
}

return specificationOptions;
}
</script>

<WizardStep {beforeSubmit}>
Expand Down Expand Up @@ -61,6 +86,28 @@
{options}
bind:value={$templateConfig.runtime} />
{/await}
{#await loadSpecifications()}
<InputSelect
label="CPU and memory"
id="specification"
placeholder="Loading specifications..."
required
disabled
options={[]}
value={null} />
{:then specificationOptions}
<InputSelect
label="CPU and memory"
id="specification"
placeholder="Select specification"
required
disabled={specificationOptions.length < 1}
options={specificationOptions}
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
? SpecificationsTooltip
: null}
bind:value={$templateConfig.specification} />
{/await}
</FormList>

<FormList class="u-margin-block-start-24">
Expand Down
1 change: 1 addition & 0 deletions src/lib/wizards/functions/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const templateConfig = writable<{
repositoryId: string;
execute?: boolean;
scopes?: string[];
specification?: string;
}>();
export const repository = writable<Models.ProviderRepository>();
export const installation = writable<Models.Installation>();
Expand Down
8 changes: 5 additions & 3 deletions src/routes/(console)/project-[project]/functions/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async ({ depends }) => {
depends(Dependencies.FUNCTION_INSTALLATIONS);

const [runtimesList, installations, templatesList] = await Promise.all([
const [runtimesList, installations, templatesList, specificationsList] = await Promise.all([
sdk.forProject.functions.listRuntimes(),
sdk.forProject.vcs.listInstallations([Query.limit(100)]),
sdk.forProject.functions.listTemplates(undefined, undefined, 100)
sdk.forProject.functions.listTemplates(undefined, undefined, 100),
sdk.forProject.functions.listSpecifications()
]);

return {
header: Header,
breadcrumbs: Breadcrumbs,
runtimesList,
installations,
templatesList
templatesList,
specificationsList
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,46 @@
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 { BillingPlan, Dependencies } from '$lib/constants';
import { Button, Form, FormList } 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 InputSelect from '$lib/elements/forms/inputSelect.svelte';
import { specificationsList } from '$lib/stores/specifications';
import { runtimesList } from '$lib/stores/runtimes';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { isCloud } from '$lib/system';
import { organization } from '$lib/stores/organization';
import SpecificationsTooltip from '$lib/wizards/functions/components/specificationsTooltip.svelte';

const functionId = $page.params.function;
let runtime: string = null;
let specification: string = null;

let options = [];
let specificationOptions = [];

onMount(async () => {
runtime ??= $func.runtime;
specification ??= $func.specification;

let runtimes = await $runtimesList;
let allowedSpecifications = (await $specificationsList).specifications;
options = runtimes.runtimes.map((runtime) => ({
label: `${runtime.name} - ${runtime.version}`,
value: runtime.$id
}));

specificationOptions = allowedSpecifications.map((size) => ({
label:
`${size.cpus} CPU, ${size.memory} MB RAM` +
(!size.enabled ? ` (Upgrade to use this)` : ''),
value: size.slug,
disabled: !size.enabled
}));
});

async function updateRuntime() {
Expand All @@ -51,11 +67,12 @@
$func.providerRepositoryId || undefined,
$func.providerBranch || undefined,
$func.providerSilentMode || undefined,
$func.providerRootDirectory || undefined
$func.providerRootDirectory || undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need PR for CLI as well, so CLI stores and sets this new attribute properly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created the PR for CLI Here: appwrite/sdk-generator#1025

specification
);
await invalidate(Dependencies.FUNCTION);
addNotification({
message: 'Runtime has been updated',
message: 'Runtime settings have been updated',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below this, in HTML, line 93, it says Runtime as title of the card. We might want to rename this to "Settings" too, possibly.

Also important to understand how specs behave during build. Is gb-hours increased properly? Is the limit used properly?

Best to have it explained in Console, or docs.

type: 'success'
});
trackEvent(Submit.FunctionUpdateName);
Expand All @@ -67,6 +84,8 @@
trackError(error, Submit.FunctionUpdateName);
}
}

$: isUpdateButtonEnabled = runtime !== $func?.runtime || specification !== $func?.specification;
</script>

<Form onSubmit={updateRuntime}>
Expand All @@ -83,11 +102,22 @@
{options}
required
hideRequired />
<InputSelect
label="CPU and memory"
id="size"
placeholder="Select runtime specification"
bind:value={specification}
options={specificationOptions}
popover={isCloud && $organization?.billingPlan === BillingPlan.FREE
? SpecificationsTooltip
: null}
required
hideRequired />
</FormList>
</svelte:fragment>

<svelte:fragment slot="actions">
<Button disabled={runtime === $func.runtime} submit>Update</Button>
<Button disabled={!isUpdateButtonEnabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
export let data: PageData;
$: total = data.executionsTotal;
$: count = data.executions;
$: gbHoursTotal = data.executionsMbSecondsTotal / 1000 / 3600;
$: mbSecondsCount = data.executionsMbSeconds;
</script>

<Container>
Expand Down Expand Up @@ -47,4 +49,24 @@
]} />
</Card>
{/if}

{#if mbSecondsCount}
<Card>
<Heading tag="h6" size="6">{formatNumberWithCommas(gbHoursTotal)}</Heading>
<p>GB Hours</p>
<div class="u-margin-block-start-16" />
<BarChart
series={[
{
name: 'Count of gbHours over time',
data: [
...mbSecondsCount.map((e) => [
e.date,
Math.ceil((e.value / 1000 / 3600) * 1000) / 1000
])
]
}
]} />
</Card>
{/if}
</Container>
Loading