Skip to content

Commit

Permalink
feat: observatory dashboard
Browse files Browse the repository at this point in the history
Signed-off-by: David Dal Busco <[email protected]>
  • Loading branch information
peterpeterparker committed Nov 16, 2023
1 parent eb75ea7 commit 5d60670
Show file tree
Hide file tree
Showing 19 changed files with 156 additions and 46 deletions.
5 changes: 5 additions & 0 deletions src/declarations/observatory/observatory.did.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { ActorMethod } from '@dfinity/agent';
import type { Principal } from '@dfinity/principal';

export interface ArchiveStatuses {
statuses: Result_1;
timestamp: bigint;
}
export interface CanisterStatusResponse {
status: CanisterStatusType;
memory_size: bigint;
Expand Down Expand Up @@ -79,6 +83,7 @@ export interface SetCronTab {
export interface _SERVICE {
del_controllers: ActorMethod<[DeleteControllersArgs], undefined>;
get_cron_tab: ActorMethod<[], [] | [CronTab]>;
get_statuses: ActorMethod<[], [] | [ArchiveStatuses]>;
list_statuses: ActorMethod<[ListStatusesArgs], Array<ListStatuses>>;
set_controllers: ActorMethod<[SetControllersArgs], undefined>;
set_cron_tab: ActorMethod<[SetCronTab], CronTab>;
Expand Down
7 changes: 6 additions & 1 deletion src/declarations/observatory/observatory.factory.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const idlFactory = ({ IDL }) => {
mission_control_id: IDL.Principal,
created_at: IDL.Nat64
});
const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) });
const CanisterStatusType = IDL.Variant({
stopped: IDL.Null,
stopping: IDL.Null,
Expand Down Expand Up @@ -57,6 +56,11 @@ export const idlFactory = ({ IDL }) => {
mission_control: Result
});
const Result_1 = IDL.Variant({ Ok: SegmentsStatuses, Err: IDL.Text });
const ArchiveStatuses = IDL.Record({
statuses: Result_1,
timestamp: IDL.Nat64
});
const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) });
const ListStatuses = IDL.Record({
cron_jobs: CronJobs,
statuses: Result_1,
Expand All @@ -83,6 +87,7 @@ export const idlFactory = ({ IDL }) => {
return IDL.Service({
del_controllers: IDL.Func([DeleteControllersArgs], [], []),
get_cron_tab: IDL.Func([], [IDL.Opt(CronTab)], ['query']),
get_statuses: IDL.Func([], [IDL.Opt(ArchiveStatuses)], ['query']),
list_statuses: IDL.Func([ListStatusesArgs], [IDL.Vec(ListStatuses)], ['query']),
set_controllers: IDL.Func([SetControllersArgs], [], []),
set_cron_tab: IDL.Func([SetCronTab], [CronTab], []),
Expand Down
7 changes: 6 additions & 1 deletion src/declarations/observatory/observatory.factory.did.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const idlFactory = ({ IDL }) => {
mission_control_id: IDL.Principal,
created_at: IDL.Nat64
});
const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) });
const CanisterStatusType = IDL.Variant({
stopped: IDL.Null,
stopping: IDL.Null,
Expand Down Expand Up @@ -57,6 +56,11 @@ export const idlFactory = ({ IDL }) => {
mission_control: Result
});
const Result_1 = IDL.Variant({ Ok: SegmentsStatuses, Err: IDL.Text });
const ArchiveStatuses = IDL.Record({
statuses: Result_1,
timestamp: IDL.Nat64
});
const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) });
const ListStatuses = IDL.Record({
cron_jobs: CronJobs,
statuses: Result_1,
Expand All @@ -83,6 +87,7 @@ export const idlFactory = ({ IDL }) => {
return IDL.Service({
del_controllers: IDL.Func([DeleteControllersArgs], [], []),
get_cron_tab: IDL.Func([], [IDL.Opt(CronTab)], ['query']),
get_statuses: IDL.Func([], [IDL.Opt(ArchiveStatuses)], ['query']),
list_statuses: IDL.Func([ListStatusesArgs], [IDL.Vec(ListStatuses)], ['query']),
set_controllers: IDL.Func([SetControllersArgs], [], []),
set_cron_tab: IDL.Func([SetCronTab], [CronTab], []),
Expand Down
11 changes: 8 additions & 3 deletions src/frontend/src/lib/api/observatory.api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import type { CronJobs, CronTab } from '$declarations/observatory/observatory.did';
import type { ArchiveStatuses, CronJobs, CronTab } from '$declarations/observatory/observatory.did';
import { getObservatoryActor } from '$lib/utils/actor.juno.utils';
import type { Principal } from '@dfinity/principal';
import { toNullable } from '@dfinity/utils';

export const getCronTab = async (): Promise<[] | [CronTab]> => {
const actor = await getObservatoryActor();
return actor.get_cron_tab();
const { get_cron_tab } = await getObservatoryActor();
return get_cron_tab();
};

export const getStatuses = async (): Promise<[] | [ArchiveStatuses]> => {
const { get_statuses } = await getObservatoryActor();
return get_statuses();
};

export const setCronTab = async ({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import { i18n } from '$lib/stores/i18n.store';
import SpinnerParagraph from '$lib/components/ui/SpinnerParagraph.svelte';
import { getContext, onMount } from 'svelte';
import { fromNullable, isNullish } from '@dfinity/utils';
import { getStatuses } from '$lib/api/observatory.api';
import { toasts } from '$lib/stores/toasts.store';
import Value from '$lib/components/ui/Value.svelte';
import { fade } from 'svelte/transition';
import { formatToDate } from '$lib/utils/date.utils';
import { TABS_CONTEXT_KEY, type TabsContext } from '$lib/types/tabs.context';
let loading = true;
let timestamp: bigint | undefined;
const loadStatuses = async () => {
try {
const results = fromNullable(await getStatuses());
timestamp = results?.timestamp;
loading = false;
} catch (err: unknown) {
toasts.error({
text: $i18n.errors.observatory_get_unexpected_error,
detail: err
});
}
};
onMount(async () => await loadStatuses());
const { store } = getContext<TabsContext>(TABS_CONTEXT_KEY);
</script>

<div class="card-container">
{#if loading}
<SpinnerParagraph>{$i18n.core.loading}</SpinnerParagraph>
{:else if isNullish(timestamp)}
<div in:fade>
<p>
{$i18n.observatory.disabled_go_settings}
<button on:click={() => store.update((state) => ({ ...state, tabId: state.tabs[1].id }))}
>{$i18n.core.settings}</button
>
</p>
</div>
{:else}
<div in:fade>
<Value>
<svelte:fragment slot="label">{$i18n.observatory.last_data_collection}</svelte:fragment>
<p>{formatToDate(timestamp)}</p>
</Value>
</div>
{/if}
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@

<div class="card-container">
{#if loading}
<SpinnerParagraph>{$i18n.observatory.loading}</SpinnerParagraph>
<SpinnerParagraph>{$i18n.core.loading}</SpinnerParagraph>
{:else}
<form on:submit|preventDefault={onSubmit} in:fade>
<div>
Expand Down
7 changes: 4 additions & 3 deletions src/frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,7 @@
},
"observatory": {
"title": "Monitoring",
"overview": "Overview",
"loading": "Loading your settings",
"dashboard": "Dashboard",
"monitoring": "Monitoring",
"enabled": "Enabled",
"disabled": "Disabled",
Expand All @@ -395,7 +394,9 @@
"email_notifications": "Email notifications",
"email_notifications_placeholder": "Enter an email to receive notifications",
"cycles_threshold": "TCycles threshold (min 0.5)",
"cycles_threshold_placeholder": "A threshold to limit the notifications"
"cycles_threshold_placeholder": "A threshold to limit the notifications",
"last_data_collection": "Last data collection",
"disabled_go_settings": "The monitoring feature appears to be disabled. You can enable it by going to the"
},
"settings": {
"title": "Settings",
Expand Down
7 changes: 4 additions & 3 deletions src/frontend/src/lib/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,7 @@
},
"observatory": {
"title": "Osservatorio",
"overview": "Panoramica",
"loading": "Carica le tue impostazioni",
"dashboard": "Dashboard",
"monitoring": "Monitoraggio",
"enabled": "Abilitato",
"disabled": "Disabilitato",
Expand All @@ -395,7 +394,9 @@
"email_notifications": "Notifiche email",
"email_notifications_placeholder": "Inserisci la tua email per ricevere notifiche",
"cycles_threshold": "Soglia TCycles (min 0.5)",
"cycles_threshold_placeholder": "Una soglia per limitare le notifiche"
"cycles_threshold_placeholder": "Una soglia per limitare le notifiche",
"last_data_collection": "Last data collection",
"disabled_go_settings": "The monitoring feature appears to be disabled. You can enable it by going to the"
},
"settings": {
"title": "Impostazioni",
Expand Down
7 changes: 4 additions & 3 deletions src/frontend/src/lib/i18n/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,7 @@
},
"observatory": {
"title": "Observatory",
"overview": "开要",
"loading": "加载设置",
"dashboard": "Dashboard",
"monitoring": "监控",
"enabled": "已启用",
"disabled": "已禁用",
Expand All @@ -395,7 +394,9 @@
"email_notifications": "邮件提醒",
"email_notifications_placeholder": "输入用于接受提醒的邮箱",
"cycles_threshold": "TCycles 阈值 (最小 0.5)",
"cycles_threshold_placeholder": "阈值设置提醒"
"cycles_threshold_placeholder": "阈值设置提醒",
"last_data_collection": "Last data collection",
"disabled_go_settings": "The monitoring feature appears to be disabled. You can enable it by going to the"
},
"settings": {
"title": "设置",
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/src/lib/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,7 @@ interface I18nUsers {

interface I18nObservatory {
title: string;
overview: string;
loading: string;
dashboard: string;
monitoring: string;
enabled: string;
disabled: string;
Expand All @@ -421,6 +420,8 @@ interface I18nObservatory {
email_notifications_placeholder: string;
cycles_threshold: string;
cycles_threshold_placeholder: string;
last_data_collection: string;
disabled_go_settings: string;
}

interface I18nSettings {
Expand Down
5 changes: 0 additions & 5 deletions src/frontend/src/routes/(monitor)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@
import Layout from '$lib/components/ui/Layout.svelte';
import Footer from '$lib/components/ui/Footer.svelte';
import Navbar from '$lib/components/core/Navbar.svelte';
import { missionControlStore } from '$lib/stores/mission-control.store';
import { loadSatellites } from '$lib/services/satellites.services';
$: $missionControlStore,
(async () => await loadSatellites({ missionControl: $missionControlStore }))();
</script>

<Layout>
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/src/routes/(monitor)/analytics/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
import { onMount } from 'svelte';
import { layoutTitle } from '$lib/stores/layout.store';
import { i18n } from '$lib/stores/i18n.store';
import { missionControlStore } from '$lib/stores/mission-control.store';
import { loadSatellites } from '$lib/services/satellites.services';
onMount(() => layoutTitle.set($i18n.analytics.title));
$: $missionControlStore,
(async () => await loadSatellites({ missionControl: $missionControlStore }))();
</script>

<slot />
9 changes: 9 additions & 0 deletions src/frontend/src/routes/(monitor)/monitoring/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script lang="ts">
import { onMount } from 'svelte';
import { layoutTitle } from '$lib/stores/layout.store';
import { i18n } from '$lib/stores/i18n.store';
onMount(() => layoutTitle.set($i18n.observatory.title));
</script>

<slot />
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,42 @@
import IdentityGuard from '$lib/components/guards/IdentityGuard.svelte';
import { nonNullish } from '@dfinity/utils';
import { missionControlStore } from '$lib/stores/mission-control.store';
import MissionControlObservatory from '$lib/components/mission-control/MissionControlObservatory.svelte';
import ObservatorySettings from '$lib/components/observatory/ObservatorySettings.svelte';
import ObservatoryDashboard from '$lib/components/observatory/ObservatoryDashboard.svelte';
import { initTabId } from '$lib/utils/tabs.utils';
const tabs: Tab[] = [
{
id: Symbol('1'),
labelKey: 'observatory.overview'
labelKey: 'observatory.dashboard'
},
{
id: Symbol('2'),
labelKey: 'core.settings'
}
];
const store = writable<TabsStore>({
tabId: tabs[0].id,
tabId: initTabId(tabs),
tabs
});
setContext<TabsContext>(TABS_CONTEXT_KEY, {
store
});
$: store.set({
tabId: initTabId(tabs),
tabs
});
</script>

<IdentityGuard>
<Tabs help="https://juno.build/docs/miscellaneous/monitoring">
{#if nonNullish($missionControlStore)}
<MissionControlObservatory missionControlId={$missionControlStore} />
{#if $store.tabId === $store.tabs[0].id}
<ObservatoryDashboard />
{:else if $store.tabId === $store.tabs[1].id && nonNullish($missionControlStore)}
<ObservatorySettings missionControlId={$missionControlStore} />
{/if}
</Tabs>
</IdentityGuard>
5 changes: 5 additions & 0 deletions src/frontend/src/routes/(monitor)/monitoring/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { loadRouteSatellite, type RouteSatellite } from '$lib/utils/nav.utils';
import type { LoadEvent } from '@sveltejs/kit';
import type { PageLoad } from './$types';

export const load: PageLoad = ($event: LoadEvent): RouteSatellite => loadRouteSatellite($event);
18 changes: 0 additions & 18 deletions src/frontend/src/routes/(sub)/monitoring/+layout.svelte

This file was deleted.

2 changes: 2 additions & 0 deletions src/observatory/observatory.did
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
type ArchiveStatuses = record { statuses : Result_1; timestamp : nat64 };
type CanisterStatusResponse = record {
status : CanisterStatusType;
memory_size : nat;
Expand Down Expand Up @@ -72,6 +73,7 @@ type SetCronTab = record {
service : () -> {
del_controllers : (DeleteControllersArgs) -> ();
get_cron_tab : () -> (opt CronTab) query;
get_statuses : () -> (opt ArchiveStatuses) query;
list_statuses : (ListStatusesArgs) -> (vec ListStatuses) query;
set_controllers : (SetControllersArgs) -> ();
set_cron_tab : (SetCronTab) -> (CronTab);
Expand Down
11 changes: 10 additions & 1 deletion src/observatory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ use crate::reports::collect_statuses as collect_statuses_report;
use crate::store::{
delete_controllers, get_cron_tab as get_cron_tab_store,
set_controllers as set_controllers_store, set_cron_tab as set_cron_tab_store,
get_statuses as get_statuses_store
};
use crate::types::interface::{ListStatuses, ListStatusesArgs, SetCronTab};
use crate::types::state::{Archive, CronTab, StableState, State};
use crate::types::state::{Archive, ArchiveStatuses, CronTab, StableState, State};
use crate::upgrade::types::upgrade::UpgradeStableState;
use ic_cdk::storage::{stable_restore, stable_save};
use ic_cdk::{caller, trap};
Expand Down Expand Up @@ -104,6 +105,14 @@ fn get_cron_tab() -> Option<CronTab> {
get_cron_tab_store(&user)
}

/// Statuses
#[query]
fn get_statuses() -> Option<ArchiveStatuses> {
let user = caller();
get_statuses_store(&user)
}

/// Reports
#[query(guard = "caller_can_execute_cron_jobs")]
Expand Down
Loading

0 comments on commit 5d60670

Please sign in to comment.