Skip to content

Commit

Permalink
Showing properties tab
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Nov 1, 2023
1 parent a082e7e commit 04c5b65
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 49 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.26.0",
"flat": "^6.0.1",
"inter-ui": "^3.19.3",
"oidc-client-ts": "^2.2.4",
"postcss": "^8.4.31",
Expand All @@ -45,4 +46,4 @@
"vite": "^4.5.0"
},
"type": "module"
}
}
7 changes: 6 additions & 1 deletion src/lib/components/DiscoveryGraph.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<script lang="ts">
import {
BuildingLibrary,
CircleStack,
Cloud,
CodeBracket,
CodeBracketSquare,
CommandLine,
ComputerDesktop,
CpuChip,
Expand Down Expand Up @@ -126,7 +129,9 @@
...nodeStyle('NetworkService', ServerStack, overlay),
...nodeStyle('Compute', CpuChip, overlay),
...nodeStyle('VirtualMachine', ComputerDesktop, overlay),
...nodeStyle('Function', CommandLine, overlay)
...nodeStyle('Function', CommandLine, overlay),
...nodeStyle('Application', CodeBracketSquare, overlay),
...nodeStyle('Library', BuildingLibrary, overlay)
]);
return styles;
Expand Down
168 changes: 123 additions & 45 deletions src/lib/components/NodeDetail.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,48 @@
<script lang="ts">
import type { AssessmentResult, Metric } from '$lib/api/assessment';
import type { Resource } from '$lib/api/discovery';
import type { Resource, listResources } from '$lib/api/discovery';
import AssessmentIcon from './AssessmentIcon.svelte';
import { flatten } from 'flat';
export let selected: Resource;
export let results: AssessmentResult[];
// TODO(oxisto): convert this to a store?!
export let metrics: Map<string, Metric>;
export let tab: string = 'results';
function name(id: string) {
let rr = id.split('/');
return rr[rr.length - 1];
}
const tabs = [
{ name: 'Assessment Results', id: 'results', count: 0 },
{ name: 'Properties', id: 'properties', count: 0 }
];
/**
* This function returns an appropriate subset of properties out of the
* complete set, that are suitable for human viewing.
*/
function humanProperties(resource: Resource) {
return Object.entries(flatten(resource.properties) ?? {}).filter(([k, v]) => {
// raw is too big and labels contains a map which we cannot display for now
if (k == 'raw' || k == 'labels') {
return false;
}
// skip empty values
if (v == 0 || v == undefined || v == null || v == '') {
return false;
}
return true;
});
}
$: (() => {
tabs[0].count = results.length;
})();
</script>

<div class="flex flex-col bg-white shadow-xl">
Expand All @@ -32,56 +63,103 @@
</div>
</div>
<div class="flex-1 px-4 sm:px-6">
<div class="space-y-6 pb-5 pt-6">
{#if results.length > 0}
<div>
<div class="text-sm font-medium leading-6 text-gray-900">Assessment Results</div>
<div class="mt-2 space-y-4">
{#each results as result}
<div class="relative flex items-start">
<div class="absolute flex h-6 items-center">
<AssessmentIcon {result} />
</div>
<div class="pl-7 text-sm leading-6">
<label for="privacy-public" class="font-medium text-gray-900">
<a
href={`/cloud/${selected.cloudServiceId}/assessments/?filterIds=${result.id}`}
>
{metrics.get(result.metricId)?.name}
</a>
</label>
<p id="privacy-public-description" class="text-gray-500">
{metrics.get(result.metricId)?.description}
</p>
</div>
</div>
<!-- tab header -->
<div>
<div class="sm:hidden">
<label for="tabs" class="sr-only">Select a tab</label>
<!-- Use an "onChange" listener to redirect the user to the selected tab URL. -->
<select
id="tabs"
name="tabs"
class="block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-clouditor-light focus:outline-none focus:ring-clouditor-light sm:text-sm"
>
{#each tabs as t (t.id)}
<option selected={t.id == tab}>{t.name}</option>
{/each}
</select>
</div>
<div class="hidden sm:block">
<div class="border-b border-gray-200">
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
{#each tabs as t (t.name)}
<a
href="?id={selected.id}&tab={t.id}"
class="{t.id == tab
? 'border-clouditor-light text-clouditor'
: 'border-transparent text-gray-500 hover:border-gray-200 hover:text-gray-700'} flex whitespace-nowrap border-b-2 py-4 px-1 text-sm font-medium"
aria-current={t.id == tab ? 'page' : undefined}
>
{t.name}
&nbsp;
{#if t.count}
<span
class="{t.id == tab
? 'bg-indigo-100 text-clouditor'
: 'bg-gray-100 text-gray-900'}ml-3 hidden rounded-full py-0.5 px-2.5 text-xs font-medium md:inline-block"
>{t.count}</span
>
{/if}
</a>
{/each}
</div>
</nav>
</div>
{/if}
<div>
<h3 class="text-sm font-medium leading-6 text-gray-900">Labels</h3>
<div class="mt-2">
<div class="flex space-x-2">
<div class="px-4 pt-5 sm:px-0 sm:pt-0">
<dl class="space-y-8 sm:space-y-6">
{#each Object.entries(selected.properties.labels ?? {}) as entry}
<div>
<dt class="text-sm font-medium text-gray-500 sm:w-40 sm:flex-shrink-0">
{entry[0]}
</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 truncate ...">
<p>
{entry[1]}
</p>
</dd>
</div>
</div>
<!-- content -->
<div class="pb-5 pt-6">
{#if tab == 'results'}
{#if results.length > 0}
<div>
<div class="space-y-4">
{#each results as result}
<div class="relative flex items-start">
<div class="absolute flex h-6 items-center">
<AssessmentIcon {result} />
</div>
{/each}
</dl>
<div class="pl-7 text-sm leading-6">
<label for="privacy-public" class="font-medium text-gray-900">
<a
href={`/cloud/${selected.cloudServiceId}/assessments/?filterIds=${result.id}`}
>
{metrics.get(result.metricId)?.name}
</a>
</label>
<p id="privacy-public-description" class="text-gray-500">
{metrics.get(result.metricId)?.description}
</p>
</div>
</div>
{/each}
</div>
</div>
{:else}
<div class="text-sm text-gray-900">No assessment results found for this resource.</div>
{/if}
{/if}
{#if tab == 'properties'}
<div>
<div class="mt-2">
<div class="flex space-x-2">
<div class="px-4 pt-5 sm:px-0 sm:pt-0">
<dl class="space-y-2">
{#each humanProperties(selected) as [k, v] (k)}
<div>
<dt class="text-sm font-medium text-gray-500 sm:w-40 sm:flex-shrink-0">
{k}
</dt>
<dd class="mt-1 text-sm text-gray-900sm:col-span-2 w-96">
<p class="truncate ...">
{v}
</p>
</dd>
</div>
{/each}
</dl>
</div>
</div>
</div>
</div>
</div>
{/if}
</div>
</div>
</div>
2 changes: 1 addition & 1 deletion src/routes/(app)/cloud/[id]/graph/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,6 @@

<div class="absolute top-64 right-8 max-w-md z-20">
{#if selected}
<NodeDetail {selected} {results} metrics={data.metrics} />
<NodeDetail {selected} {results} metrics={data.metrics} tab={data.tab ?? 'results'} />
{/if}
</div>
4 changes: 3 additions & 1 deletion src/routes/(app)/cloud/[id]/graph/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ export const load = (async ({ fetch, params, url }) => {
const results = listAssessmentResults(fetch, true);
const edges = await listGraphEdges(fetch);
const id = url.searchParams.get('id');
const tab = url.searchParams.get('tab');

return {
results,
edges,
id
id,
tab
};
}) satisfies PageLoad;

0 comments on commit 04c5b65

Please sign in to comment.