Skip to content

Commit

Permalink
Improve libraries routing
Browse files Browse the repository at this point in the history
  • Loading branch information
nas-tabchiche committed Jan 29, 2025
1 parent 9049fc1 commit e26a0ff
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 37 deletions.
4 changes: 2 additions & 2 deletions frontend/src/lib/components/ModelTable/LibraryActions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<span class="hover:text-primary-500">
<form
method="post"
action="/libraries/{library.id}?/load"
action="/stored-libraries/{library.id}?/load"
use:enhance={() => {
loading.form = true;
loading.library = library.urn;
Expand Down Expand Up @@ -101,7 +101,7 @@
<span class="hover:text-primary-500">
<form
method="post"
action="/libraries/{library.id}?/update"
action="/loaded-libraries/{library.id}?/update"
use:enhance={() => {
loading.form = true;
loading.library = library.urn;
Expand Down
16 changes: 6 additions & 10 deletions frontend/src/lib/utils/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,18 +753,14 @@ export const FIELD_COMPONENT_MAP = {
evidences: {
attachment: EvidenceFilePreview
},
libraries: {
locales: LanguageDisplay,
'stored-libraries': {
locale: LanguageDisplay,
[CUSTOM_ACTIONS_COMPONENT]: LibraryActions
},
// "stored-libraries": {
// locale: LanguageDisplay,
// [CUSTOM_ACTIONS_COMPONENT]: LibraryActions
// },
// "loaded-libraries": {
// locale: LanguageDisplay
// // [CUSTOM_ACTIONS_COMPONENT]: LibraryActions
// },
'loaded-libraries': {
locale: LanguageDisplay
// [CUSTOM_ACTIONS_COMPONENT]: LibraryActions
},
'user-groups': {
localization_dict: UserGroupNameDisplay
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export const URL_MODEL = [
'frameworks',
'requirements',
'requirement-assessments',
'stored-libraries',
'loaded-libraries',
'libraries',
'sso-settings',
'general-settings',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { safeTranslate } from '$lib/utils/i18n';
import { nestedDeleteFormAction } from '$lib/utils/actions';

export const load = (async ({ fetch }) => {
const stored_libraries_endpoint = `${BASE_API_URL}/stored-libraries/`;
const loaded_libaries_endpoint = `${BASE_API_URL}/loaded-libraries/`;
const stored_libraries_endpoint = `${BASE_API_URL}/stored-libraries/?limit=5000`;
const loaded_libaries_endpoint = `${BASE_API_URL}/loaded-libraries/?limit=5000`;

const [stored_libraries_res, loaded_libaries_res] = await Promise.all([
fetch(stored_libraries_endpoint),
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/routes/(app)/(internal)/libraries/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
</div>
<ModelTable
source={data.storedLibrariesTable}
URLModel="libraries"
URLModel="stored-libraries"
deleteForm={data.deleteForm}
server={false}
/>
Expand All @@ -63,7 +63,7 @@
<!-- loadedlibraries -->
<ModelTable
source={data.loadedLibrariesTable}
URLModel="libraries"
URLModel="loaded-libraries"
deleteForm={data.deleteForm}
detailQueryParameter="loaded"
server={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,6 @@ import { fail, type Actions } from '@sveltejs/kit';
import { setFlash } from 'sveltekit-flash-message/server';

export const actions: Actions = {
load: async (event) => {
const endpoint = `${BASE_API_URL}/stored-libraries/${event.params.id}/import/`;
const res = await event.fetch(endpoint); // We will have to make this a POST later (we should use POST when creating a new object)
if (!res.ok) {
const response = await res.json();
console.error('server response:', response);
setFlash({ type: 'error', message: safeTranslate(response.error) }, event);
return fail(400, { error: m.errorLoadingLibrary() });
}
setFlash(
{
type: 'success',
message: m.librarySuccessfullyLoaded()
},
event
);
},
update: async (event) => {
const endpoint = `${BASE_API_URL}/loaded-libraries/${event.params.id}/update/`;
const res = await event.fetch(endpoint); // We will have to make this a PATCH later (we should use PATCH when modifying an object)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ fetch, params, url }) => {
const endpoint = `/libraries/${params.id}`;
const endpoint = `/loaded-libraries/${params.id}`;
const queryParams = url.searchParams.toString();
const library = await fetch(`${endpoint}?${queryParams}`).then((res) => res.json());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { error, type NumericRange } from '@sveltejs/kit';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async ({ fetch, url, params }) => {
const isLoaded = url.searchParams.has('loaded');
const URLModel = isLoaded ? 'loaded-libraries' : 'stored-libraries';
const URLModel = 'loaded-libraries';
const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/`;
const contentEndpoint = `${BASE_API_URL}/${URLModel}/${params.id}/content/`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { error, type NumericRange } from '@sveltejs/kit';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async ({ fetch, params, url }) => {
const URLModel = url.searchParams.has('loaded') ? 'loaded-libraries' : 'stored-libraries';
const URLModel = 'loaded-libraries';
const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/tree`;
const res = await fetch(endpoint);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { BASE_API_URL } from '$lib/utils/constants';
import { safeTranslate } from '$lib/utils/i18n';
import * as m from '$paraglide/messages';
import { fail, type Actions } from '@sveltejs/kit';
import { setFlash } from 'sveltekit-flash-message/server';

export const actions: Actions = {
load: async (event) => {
const endpoint = `${BASE_API_URL}/stored-libraries/${event.params.id}/import/`;
const res = await event.fetch(endpoint); // We will have to make this a POST later (we should use POST when creating a new object)
if (!res.ok) {
const response = await res.json();
console.error('server response:', response);
setFlash({ type: 'error', message: safeTranslate(response.error) }, event);
return fail(400, { error: m.errorLoadingLibrary() });
}
setFlash(
{
type: 'success',
message: m.librarySuccessfullyLoaded()
},
event
);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<script lang="ts">
import { page } from '$app/stores';
import Dropdown from '$lib/components/Dropdown/Dropdown.svelte';
import ModelTable from '$lib/components/ModelTable/ModelTable.svelte';
import RiskMatrix from '$lib/components/RiskMatrix/RiskMatrix.svelte';
import * as m from '$paraglide/messages';
import { formatDateOrDateTime } from '$lib/utils/datetime';
import { languageTag } from '$paraglide/runtime';
import TreeViewItemContent from '../../frameworks/[id=uuid]/TreeViewItemContent.svelte';
export let data;
let loading = { form: false, library: '' };
const showRisks = true;
interface LibraryObjects {
[key: string]: any;
}
const libraryObjects: LibraryObjects = data.library.objects ?? [];
const riskMatrices = libraryObjects['risk_matrix'] ?? [];
const referenceControls = libraryObjects['reference_controls'] ?? [];
const threats = libraryObjects['threats'] ?? [];
const framework = libraryObjects['framework'];
function transformToTreeView(nodes) {
return nodes.map(([id, node]) => {
return {
id: id,
content: TreeViewItemContent,
contentProps: node,
children: node.children ? transformToTreeView(Object.entries(node.children)) : []
};
});
}
import { enhance } from '$app/forms';
import type { TableSource } from '$lib/components/ModelTable/types';
import RecursiveTreeView from '$lib/components/TreeView/RecursiveTreeView.svelte';
import { ProgressRadial, tableSourceMapper } from '@skeletonlabs/skeleton';
const riskMatricesTable: TableSource = {
head: { name: 'name', description: 'description' },
body: tableSourceMapper(riskMatrices, ['name', 'description'])
};
const referenceControlsTable: TableSource = {
head: {
ref_id: 'ref',
name: 'name',
description: 'description',
category: 'category',
csf_function: 'csfFunction'
},
body: tableSourceMapper(referenceControls, [
'ref_id',
'name',
'description',
'category',
'csf_function'
])
};
const threatsTable: TableSource = {
head: { ref_id: 'ref', name: 'name', description: 'description' },
body: tableSourceMapper(threats, ['ref_id', 'name', 'description'])
};
function riskMatricesPreview(riskMatrices: []) {
let riskMatricesDumps = [];
let riskMatrixDump = {
json_definition: ''
};
for (const riskMatrix of riskMatrices) {
riskMatrixDump['json_definition'] = JSON.stringify(riskMatrix);
riskMatricesDumps.push(riskMatrixDump);
}
return riskMatricesDumps;
}
$: displayImportButton = !(data.library.is_loaded ?? true);
async function handleSubmit(event: { currentTarget: EventTarget & HTMLFormElement }) {
const data = new FormData(event.currentTarget);
const response = await fetch(event.currentTarget.action, {
method: 'POST',
body: data
});
const result: ActionResult = deserialize(await response.text());
if (result.type === 'success') {
await invalidateAll();
}
applyAction(result);
}
</script>

<div class="card bg-white p-4 shadow space-y-4">
<div class="flex flex-col space-y-2">
<span class="w-full flex flex-row justify-between">
<h1 class="font-medium text-xl">{data.library.name}</h1>
<div>
{#if displayImportButton}
{#if loading.form}
<ProgressRadial width="w-6" meter="stroke-primary-500" />
{:else}
<form
method="post"
action="/libraries/{data.library.id}?/load"
use:enhance={() => {
loading.form = true;
loading.library = data.library.urn;
return async ({ update }) => {
loading.form = false;
loading.library = '';
update();
};
}}
on:submit={handleSubmit}
>
{#if $page.data.user.is_admin}
<button type="submit" class="p-1 btn text-xl hover:text-primary-500">
<i class="fa-solid fa-file-import" />
</button>
{/if}
</form>
{/if}
{/if}
</div>
</span>
<div class="space-y-1">
<p class="text-md leading-5 text-gray-700">
<strong>{m.description()}</strong>: {data.library.description}
</p>
<p class="text-md leading-5 text-gray-700">
<strong>{m.provider()}</strong>: {data.library.provider}
</p>
<p class="text-md leading-5 text-gray-700">
<strong>{m.packager()}</strong>: {data.library.packager}
</p>
<p class="text-md leading-5 text-gray-700">
<strong>{m.version()}</strong>: {data.library.version}
</p>
{#if data.library.publication_date}
<p class="text-md leading-5 text-gray-700">
<strong>{m.publicationDate()}</strong>: {formatDateOrDateTime(
data.library.publication_date,
languageTag()
)}
</p>
{/if}
{#if data.library.dependencies}
<p class="text-md leading-5 text-gray-700">
<strong>{m.dependencies()}</strong>:
</p>
<ul class="list-disc list-inside">
{#each data.library.dependencies as dependency}
<li>{dependency.name}</li>
{/each}
</ul>
{/if}
{#if data.library.copyright}
<p class="text-md leading-5 text-gray-700">
<strong>{m.copyright()}</strong>: {data.library.copyright}
</p>
{/if}
</div>
</div>

{#if riskMatrices.length > 0}
<Dropdown
open={riskMatrices.length == 1}
style="hover:text-indigo-700"
icon="fa-solid fa-table-cells-large"
header="{riskMatrices.length} {m.riskMatrices()}"
>
<ModelTable
source={riskMatricesTable}
displayActions={false}
pagination={false}
rowCount={false}
rowsPerPage={false}
search={false}
interactive={false}
/>
{#each riskMatricesPreview(riskMatrices) as riskMatrix}
<RiskMatrix {riskMatrix} {showRisks} wrapperClass="mt-8" />
{/each}
</Dropdown>
{/if}

{#if referenceControls.length > 0}
<Dropdown
style="hover:text-indigo-700"
icon="fa-solid fa-gears"
header="{referenceControls.length} {m.referenceControls()}"
>
<ModelTable source={referenceControlsTable} displayActions={false} interactive={false} />
</Dropdown>
{/if}

{#if threats.length > 0}
<Dropdown
style="hover:text-indigo-700"
icon="fa-solid fa-biohazard"
header="{threats.length} {m.threats()}"
>
<ModelTable source={threatsTable} displayActions={false} interactive={false} />
</Dropdown>
{/if}

{#if framework}
<h4 class="h4 font-medium">{m.framework()}</h4>
{#await data.tree}
<span data-testid="loading-field">
{m.loading()}...
</span>
{:then tree}
<RecursiveTreeView
nodes={transformToTreeView(Object.entries(tree))}
hover="hover:bg-initial"
/>
{/await}
{/if}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ fetch, params, url }) => {
const endpoint = `/stored-libraries/${params.id}`;
const queryParams = url.searchParams.toString();
const library = await fetch(`${endpoint}?${queryParams}`).then((res) => res.json());

return {
tree: fetch(`${endpoint}/tree?${queryParams}`).then((res) => res.json()) ?? {},
library,
title: library.name
};
};
Loading

0 comments on commit e26a0ff

Please sign in to comment.