diff --git a/backend/ciso_assistant/settings.py b/backend/ciso_assistant/settings.py index fc4ef2adf..b0eabba21 100644 --- a/backend/ciso_assistant/settings.py +++ b/backend/ciso_assistant/settings.py @@ -127,7 +127,7 @@ def set_ciso_assistant_url(_, __, event_dict): MEDIA_ROOT = LOCAL_STORAGE_DIRECTORY MEDIA_URL = "" -PAGINATE_BY = os.environ.get("PAGINATE_BY", default=5000) +PAGINATE_BY = int(os.environ.get("PAGINATE_BY", default=5000)) # Application definition @@ -214,7 +214,7 @@ def set_ciso_assistant_url(_, __, event_dict): "core.permissions.RBACPermissions", ], "DEFAULT_FILTER_CLASSES": ["django_filters.rest_framework.DjangoFilterBackend"], - "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", "PAGE_SIZE": PAGINATE_BY, "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "EXCEPTION_HANDLER": "core.helpers.handle", diff --git a/backend/core/models.py b/backend/core/models.py index f2e88bfdc..a34b4a5c5 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -735,6 +735,18 @@ def reference_count(self) -> int: + LoadedLibrary.objects.filter(dependencies=self).distinct().count() ) + @property + def has_update(self) -> bool: + last_version = {} + + stored_libraries = StoredLibrary.objects.filter(urn=self.urn) + + for stored_library in stored_libraries: + if last_version.get(stored_library.urn, -1) < stored_library.version: + last_version[stored_library.urn] = stored_library.version + + return last_version.get(self.urn, -1) > self.version + def delete(self, *args, **kwargs): if self.reference_count > 0: raise ValueError( diff --git a/backend/core/views.py b/backend/core/views.py index 6ac7fdfb2..c6b67b392 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -125,7 +125,7 @@ class BaseModelViewSet(viewsets.ModelViewSet): filters.OrderingFilter, ] ordering = ["created_at"] - ordering_fields = ordering + ordering_fields = "__all__" search_fields = ["name", "description"] model: models.Model @@ -1562,7 +1562,6 @@ class RiskScenarioViewSet(BaseModelViewSet): "applied_controls", ] ordering = ["ref_id"] - ordering_fields = ordering def _perform_write(self, serializer): if not serializer.validated_data.get( @@ -1810,7 +1809,6 @@ class UserViewSet(BaseModelViewSet): model = User ordering = ["-is_active", "-is_superuser", "email", "id"] - ordering_fields = ordering filterset_class = UserFilter search_fields = ["email", "first_name", "last_name"] @@ -1853,7 +1851,6 @@ class UserGroupViewSet(BaseModelViewSet): model = UserGroup ordering = ["builtin", "name"] - ordering_fields = ordering filterset_fields = ["folder"] @@ -1864,7 +1861,6 @@ class RoleViewSet(BaseModelViewSet): model = Role ordering = ["builtin", "name"] - ordering_fields = ordering class RoleAssignmentViewSet(BaseModelViewSet): @@ -1874,7 +1870,6 @@ class RoleAssignmentViewSet(BaseModelViewSet): model = RoleAssignment ordering = ["builtin", "folder"] - ordering_fields = ordering filterset_fields = ["folder"] @@ -3017,7 +3012,6 @@ class FrameworkViewSet(BaseModelViewSet): model = Framework filterset_fields = ["folder"] search_fields = ["name", "description"] - ordering_fields = ["name", "description"] @method_decorator(cache_page(60 * LONG_CACHE_TTL)) @method_decorator(vary_on_cookie) @@ -3033,12 +3027,6 @@ def names(self, request): } ) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - - def retrieve(self, request, *args, **kwargs): - return super().retrieve(request, *args, **kwargs) - @action(detail=True, methods=["get"]) def tree(self, request, pk): _framework = Framework.objects.get(id=pk) @@ -3120,7 +3108,6 @@ class EvidenceViewSet(BaseModelViewSet): model = Evidence filterset_fields = ["folder", "applied_controls", "requirement_assessments", "name"] search_fields = ["name"] - ordering_fields = ["name", "description"] @action(methods=["get"], detail=True) def attachment(self, request, pk): @@ -3201,7 +3188,6 @@ class ComplianceAssessmentViewSet(BaseModelViewSet): model = ComplianceAssessment filterset_fields = ["framework", "project", "status", "ebios_rm_studies"] search_fields = ["name", "description", "ref_id"] - ordering_fields = ["name", "description"] @method_decorator(cache_page(60 * LONG_CACHE_TTL)) @action(detail=False, name="Get status choices") @@ -3695,7 +3681,7 @@ class RequirementAssessmentViewSet(BaseModelViewSet): "compliance_assessment", "applied_controls", ] - search_fields = ["name", "description"] + search_fields = ["requirement__name", "requirement__description"] def update(self, request, *args, **kwargs): response = super().update(request, *args, **kwargs) diff --git a/backend/library/serializers.py b/backend/library/serializers.py index a3e91fc59..4720a235f 100644 --- a/backend/library/serializers.py +++ b/backend/library/serializers.py @@ -114,6 +114,7 @@ class Meta: "objects_meta", "reference_count", "locales", + "has_update", ] diff --git a/backend/library/views.py b/backend/library/views.py index b7ee7451a..4ddb20bd5 100644 --- a/backend/library/views.py +++ b/backend/library/views.py @@ -197,45 +197,6 @@ class LoadedLibraryViewSet(viewsets.ModelViewSet): model = LoadedLibrary queryset = LoadedLibrary.objects.all() - def list(self, request, *args, **kwargs): - if "view_loadedlibrary" not in request.user.permissions: - return Response(status=HTTP_403_FORBIDDEN) - - stored_libraries = [*StoredLibrary.objects.all()] - last_version = {} - for stored_library in stored_libraries: - if last_version.get(stored_library.urn, -1) < stored_library.version: - last_version[stored_library.urn] = stored_library.version - - loaded_libraries = [] - for library in LoadedLibrary.objects.all(): - loaded_library = { - key: getattr(library, key) - for key in [ - "id", - "name", - "description", - "urn", - "ref_id", - "locale", - "version", - "packager", - "provider", - "publication_date", - "builtin", - "objects_meta", - "reference_count", - "translations", - ] - } - loaded_library["has_update"] = ( - last_version.get(library.urn, -1) > library.version - ) - loaded_library["locales"] = library.get_locales - loaded_libraries.append(update_translations_in_object(loaded_library)) - - return Response({"results": loaded_libraries}) - def retrieve(self, request, *args, pk, **kwargs): if "view_loadedlibrary" not in request.user.permissions: return Response(status=HTTP_403_FORBIDDEN) diff --git a/enterprise/backend/enterprise_core/settings.py b/enterprise/backend/enterprise_core/settings.py index a128d46e8..7173fc17a 100644 --- a/enterprise/backend/enterprise_core/settings.py +++ b/enterprise/backend/enterprise_core/settings.py @@ -128,7 +128,7 @@ def set_ciso_assistant_url(_, __, event_dict): MEDIA_ROOT = LOCAL_STORAGE_DIRECTORY MEDIA_URL = "" -PAGINATE_BY = os.environ.get("PAGINATE_BY", default=5000) +PAGINATE_BY = int(os.environ.get("PAGINATE_BY", default=5000)) # Application definition @@ -216,7 +216,7 @@ def set_ciso_assistant_url(_, __, event_dict): "enterprise_core.permissions.LicensePermission", ], "DEFAULT_FILTER_CLASSES": ["django_filters.rest_framework.DjangoFilterBackend"], - "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", "PAGE_SIZE": PAGINATE_BY, "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "EXCEPTION_HANDLER": "core.helpers.handle", diff --git a/frontend/src/lib/components/DetailView/DetailView.svelte b/frontend/src/lib/components/DetailView/DetailView.svelte index c15173e8b..d451c3fb0 100644 --- a/frontend/src/lib/components/DetailView/DetailView.svelte +++ b/frontend/src/lib/components/DetailView/DetailView.svelte @@ -256,7 +256,7 @@ > {#if value !== null && value !== undefined && value !== ''} {#if key === 'library'} - {@const itemHref = `/libraries/${value.id}?loaded`} + {@const itemHref = `/loaded-libraries/${value.id}`} {value.name} diff --git a/frontend/src/lib/components/ModelTable/LibraryActions.svelte b/frontend/src/lib/components/ModelTable/LibraryActions.svelte index 9e61d1cce..5be542d6b 100644 --- a/frontend/src/lib/components/ModelTable/LibraryActions.svelte +++ b/frontend/src/lib/components/ModelTable/LibraryActions.svelte @@ -51,7 +51,7 @@
{ loading.form = true; loading.library = library.urn; @@ -101,7 +101,7 @@ { loading.form = true; loading.library = library.urn; diff --git a/frontend/src/lib/components/ModelTable/ModelTable.svelte b/frontend/src/lib/components/ModelTable/ModelTable.svelte index e6780df72..986b641ee 100644 --- a/frontend/src/lib/components/ModelTable/ModelTable.svelte +++ b/frontend/src/lib/components/ModelTable/ModelTable.svelte @@ -1,464 +1,29 @@ -
-
- {#if filteredFields.length > 0 && hasRows && !hideFilters} - -
- {#each filteredFields as field} - - {/each} -
- {/if} - {#if search} - - {/if} - {#if pagination && rowsPerPage} - - {/if} -
- - {#if canCreateObject} - - {/if} -
-
- - - - - {#each Object.entries(source.head) as [key, heading]} - - {/each} - {#if displayActions} - - {/if} - - {#if thFiler} - - {#each Object.keys(source.head) as key} - - {/each} - {#if displayActions} - - {/if} - - {/if} - - - {#each $rows as row, rowIndex} - {@const meta = row.meta ? row.meta : row} - { - onRowClick(e, rowIndex); - }} - on:keydown={(e) => { - onRowKeydown(e, rowIndex); - }} - aria-rowindex={rowIndex + 1} - > - {#each Object.entries(row) as [key, value]} - {#if key !== 'meta'} - {@const component = field_component_map[key]} - - {/if} - {/each} - {#if displayActions} - - {/if} - - {/each} - - {#if source.foot} - - - {#each source.foot as cell} - - {/each} - - - {/if} -
{safeTranslate(heading)}
- {#if component} - - {:else} - - {#if Array.isArray(value)} -
    - {#each value as val} -
  • - {#if val.str && val.id} - {@const itemHref = `/${URL_MODEL_MAP[URLModel]['foreignKeyFields']?.find((item) => item.field === key)?.urlModel}/${val.id}`} - {val.str} - {:else if val.str} - {safeTranslate(val.str)} - {:else if unsafeTranslate(val.split(':')[0])} - {unsafeTranslate(val.split(':')[0] + 'Colon')} - {val.split(':')[1]} - {:else} - {val ?? '-'} - {/if} -
  • - {/each} -
- {:else if value && value.str} - {#if value.id} - {@const itemHref = `/${URL_MODEL_MAP[URLModel]['foreignKeyFields']?.find((item) => item.field === key)?.urlModel}/${value.id}`} - {#if key === 'ro_to_couple'} - {safeTranslate(toCamelCase(value.str.split(' - ')[0]))} - {value.str.split( - '-' - )[1]} - {:else} - {value.str} - {/if} - {:else} - {value.str ?? '-'} - {/if} - {:else if value && value.hexcolor} -

- {safeTranslate(value.name ?? value.str) ?? '-'} -

- {:else if ISO_8601_REGEX.test(value) && (key === 'created_at' || key === 'updated_at' || key === 'publication_date' || key === 'expiry_date' || key === 'accepted_at' || key === 'rejected_at' || key === 'revoked_at' || key === 'eta')} - {formatDateOrDateTime(value, languageTag())} - {:else if [true, false].includes(value)} - {safeTranslate(value ?? '-')} - {:else if key === 'progress'} - {safeTranslate('percentageDisplay', { number: value })} - {:else if URLModel == 'risk-acceptances' && key === 'name' && row.meta?.accepted_at && row.meta?.revoked_at == null} -
- {safeTranslate(value ?? '-')} - - {m.accept()} - -
- {:else} - {safeTranslate(value ?? '-')} - {/if} -
- {/if} -
- - {#if row.meta[identifierField]} - {@const actionsComponent = field_component_map[CUSTOM_ACTIONS_COMPONENT]} - {@const actionsURLModel = source.meta.urlmodel ?? URLModel} - - - {#if $$slots.actionsHead} - - {/if} - - - {#if $$slots.actionsBody} - - {/if} - - - - - - {/if} - -
{cell}
- -
- {#if rowCount && pagination} - - {/if} - {#if pagination} - - {/if} -
-
+ + + + + + diff --git a/frontend/src/lib/components/ModelTable/client/ModelTable.svelte b/frontend/src/lib/components/ModelTable/client/ModelTable.svelte new file mode 100644 index 000000000..46d9eabb2 --- /dev/null +++ b/frontend/src/lib/components/ModelTable/client/ModelTable.svelte @@ -0,0 +1,465 @@ + + +
+
+ {#if filteredFields.length > 0 && hasRows && !hideFilters} + +
+ {#each filteredFields as field} + + {/each} +
+ {/if} + {#if search} + + {/if} + {#if pagination && rowsPerPage} + + {/if} +
+ + {#if canCreateObject} + + {/if} +
+
+ + + + + {#each Object.entries(source.head) as [key, heading]} + + {/each} + {#if displayActions} + + {/if} + + {#if thFiler} + + {#each Object.keys(source.head) as key} + + {/each} + {#if displayActions} + + {/if} + + {/if} + + + {#each $rows as row, rowIndex} + {@const meta = row.meta ? row.meta : row} + { + onRowClick(e, rowIndex); + }} + on:keydown={(e) => { + onRowKeydown(e, rowIndex); + }} + aria-rowindex={rowIndex + 1} + > + {#each Object.entries(row) as [key, value]} + {#if key !== 'meta'} + {@const component = field_component_map[key]} + + {/if} + {/each} + {#if displayActions} + + {/if} + + {/each} + + {#if source.foot} + + + {#each source.foot as cell} + + {/each} + + + {/if} +
{safeTranslate(heading)}
+ {#if component} + + {:else} + + {#if Array.isArray(value)} +
    + {#each value as val} +
  • + {#if val.str && val.id} + {@const itemHref = `/${URL_MODEL_MAP[URLModel]['foreignKeyFields']?.find((item) => item.field === key)?.urlModel}/${val.id}`} + {val.str} + {:else if val.str} + {safeTranslate(val.str)} + {:else if unsafeTranslate(val.split(':')[0])} + {unsafeTranslate(val.split(':')[0] + 'Colon')} + {val.split(':')[1]} + {:else} + {val ?? '-'} + {/if} +
  • + {/each} +
+ {:else if value && value.str} + {#if value.id} + {@const itemHref = `/${URL_MODEL_MAP[URLModel]['foreignKeyFields']?.find((item) => item.field === key)?.urlModel}/${value.id}`} + {#if key === 'ro_to_couple'} + {safeTranslate(toCamelCase(value.str.split(' - ')[0]))} - {value.str.split( + '-' + )[1]} + {:else} + {value.str} + {/if} + {:else} + {value.str ?? '-'} + {/if} + {:else if value && value.hexcolor} +

+ {safeTranslate(value.name ?? value.str) ?? '-'} +

+ {:else if ISO_8601_REGEX.test(value) && (key === 'created_at' || key === 'updated_at' || key === 'publication_date' || key === 'expiry_date' || key === 'accepted_at' || key === 'rejected_at' || key === 'revoked_at' || key === 'eta')} + {formatDateOrDateTime(value, languageTag())} + {:else if [true, false].includes(value)} + {safeTranslate(value ?? '-')} + {:else if key === 'progress'} + {safeTranslate('percentageDisplay', { number: value })} + {:else if URLModel == 'risk-acceptances' && key === 'name' && row.meta?.accepted_at && row.meta?.revoked_at == null} +
+ {safeTranslate(value ?? '-')} + + {m.accept()} + +
+ {:else} + {safeTranslate(value ?? '-')} + {/if} +
+ {/if} +
+ + {#if row.meta[identifierField]} + {@const actionsComponent = field_component_map[CUSTOM_ACTIONS_COMPONENT]} + {@const actionsURLModel = source.meta.urlmodel ?? URLModel} + + + {#if $$slots.actionsHead} + + {/if} + + + {#if $$slots.actionsBody} + + {/if} + + + + + + {/if} + +
{cell}
+ +
+ {#if rowCount && pagination} + + {/if} + {#if pagination} + + {/if} +
+
diff --git a/frontend/src/lib/components/ModelTable/Pagination.svelte b/frontend/src/lib/components/ModelTable/client/Pagination.svelte similarity index 100% rename from frontend/src/lib/components/ModelTable/Pagination.svelte rename to frontend/src/lib/components/ModelTable/client/Pagination.svelte diff --git a/frontend/src/lib/components/ModelTable/RowCount.svelte b/frontend/src/lib/components/ModelTable/client/RowCount.svelte similarity index 100% rename from frontend/src/lib/components/ModelTable/RowCount.svelte rename to frontend/src/lib/components/ModelTable/client/RowCount.svelte diff --git a/frontend/src/lib/components/ModelTable/RowsPerPage.svelte b/frontend/src/lib/components/ModelTable/client/RowsPerPage.svelte similarity index 100% rename from frontend/src/lib/components/ModelTable/RowsPerPage.svelte rename to frontend/src/lib/components/ModelTable/client/RowsPerPage.svelte diff --git a/frontend/src/lib/components/ModelTable/Search.svelte b/frontend/src/lib/components/ModelTable/client/Search.svelte similarity index 100% rename from frontend/src/lib/components/ModelTable/Search.svelte rename to frontend/src/lib/components/ModelTable/client/Search.svelte diff --git a/frontend/src/lib/components/ModelTable/Th.svelte b/frontend/src/lib/components/ModelTable/client/Th.svelte similarity index 100% rename from frontend/src/lib/components/ModelTable/Th.svelte rename to frontend/src/lib/components/ModelTable/client/Th.svelte diff --git a/frontend/src/lib/components/ModelTable/ThFilter.svelte b/frontend/src/lib/components/ModelTable/client/ThFilter.svelte similarity index 100% rename from frontend/src/lib/components/ModelTable/ThFilter.svelte rename to frontend/src/lib/components/ModelTable/client/ThFilter.svelte diff --git a/frontend/src/lib/components/ModelTable/server/ModelTable.svelte b/frontend/src/lib/components/ModelTable/server/ModelTable.svelte new file mode 100644 index 000000000..7adf105a6 --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/ModelTable.svelte @@ -0,0 +1,327 @@ + + +
+
+ {#if search} + + {/if} + {#if pagination && rowsPerPage} + + {/if} +
+ + {#if canCreateObject} + + {/if} +
+
+ + + + + {#each Object.entries(source.head) as [key, heading]} + + {/each} + {#if displayActions} + + {/if} + + {#if thFiler} + + {#each Object.keys(source.head) as key} + + {/each} + {#if displayActions} + + {/if} + + {/if} + + + {#each $rows as row, rowIndex} + {@const meta = row.meta ? row.meta : row} + { + onRowClick(e, rowIndex); + }} + on:keydown={(e) => { + onRowKeydown(e, rowIndex); + }} + aria-rowindex={rowIndex + 1} + > + {#each Object.entries(row) as [key, value]} + {#if key !== 'meta'} + {@const component = field_component_map[key]} + + {/if} + {/each} + {#if displayActions} + + {/if} + + {/each} + + {#if source.foot} + + + {#each source.foot as cell} + + {/each} + + + {/if} +
{safeTranslate(heading)}
+ {#if component} + + {:else} + + {#if Array.isArray(value)} +
    + {#each value as val} +
  • + {#if val.str && val.id} + {@const itemHref = `/${URL_MODEL_MAP[URLModel]['foreignKeyFields']?.find((item) => item.field === key)?.urlModel}/${val.id}`} + {val.str} + {:else if val.str} + {safeTranslate(val.str)} + {:else if unsafeTranslate(val.split(':')[0])} + {unsafeTranslate(val.split(':')[0] + 'Colon')} + {val.split(':')[1]} + {:else} + {val ?? '-'} + {/if} +
  • + {/each} +
+ {:else if value && value.str} + {#if value.id} + {@const itemHref = `/${URL_MODEL_MAP[URLModel]['foreignKeyFields']?.find((item) => item.field === key)?.urlModel}/${value.id}`} + {#if key === 'ro_to_couple'} + {safeTranslate(toCamelCase(value.str.split(' - ')[0]))} - {value.str.split( + '-' + )[1]} + {:else} + {value.str} + {/if} + {:else} + {value.str ?? '-'} + {/if} + {:else if value && value.hexcolor} +

+ {safeTranslate(value.name ?? value.str) ?? '-'} +

+ {:else if ISO_8601_REGEX.test(value) && (key === 'created_at' || key === 'updated_at' || key === 'expiry_date' || key === 'accepted_at' || key === 'rejected_at' || key === 'revoked_at' || key === 'eta')} + {formatDateOrDateTime(value, languageTag())} + {:else if [true, false].includes(value)} + {safeTranslate(value ?? '-')} + {:else if key === 'progress'} + {safeTranslate('percentageDisplay', { number: value })} + {:else if URLModel == 'risk-acceptances' && key === 'name' && row.meta?.accepted_at && row.meta?.revoked_at == null} +
+ {safeTranslate(value ?? '-')} + + {m.accept()} + +
+ {:else} + {safeTranslate(value ?? '-')} + {/if} +
+ {/if} +
+ + {#if row.meta[identifierField]} + {@const actionsComponent = field_component_map[CUSTOM_ACTIONS_COMPONENT]} + {@const actionsURLModel = source.meta.urlmodel ?? URLModel} + + + {#if $$slots.actionsHead} + + {/if} + + + {#if $$slots.actionsBody} + + {/if} + + + + + + {/if} + +
{cell}
+ +
+ {#if rowCount && pagination} + + {/if} + {#if pagination} + + {/if} +
+
diff --git a/frontend/src/lib/components/ModelTable/server/Pagination.svelte b/frontend/src/lib/components/ModelTable/server/Pagination.svelte new file mode 100644 index 000000000..41ea5c1ad --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/Pagination.svelte @@ -0,0 +1,96 @@ + + +
+ + {#if $pages === undefined} + + {:else} + {#each $pages as page} + + {/each} + {/if} + +
+ + diff --git a/frontend/src/lib/components/ModelTable/server/RowCount.svelte b/frontend/src/lib/components/ModelTable/server/RowCount.svelte new file mode 100644 index 000000000..eb1336e8e --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/RowCount.svelte @@ -0,0 +1,18 @@ + + +{#if $rowCount === undefined} +
+{:else} + +{/if} diff --git a/frontend/src/lib/components/ModelTable/server/RowsPerPage.svelte b/frontend/src/lib/components/ModelTable/server/RowsPerPage.svelte new file mode 100644 index 000000000..ea013966e --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/RowsPerPage.svelte @@ -0,0 +1,33 @@ + + + diff --git a/frontend/src/lib/components/ModelTable/server/Search.svelte b/frontend/src/lib/components/ModelTable/server/Search.svelte new file mode 100644 index 000000000..23a0efd44 --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/Search.svelte @@ -0,0 +1,25 @@ + + + diff --git a/frontend/src/lib/components/ModelTable/server/Th.svelte b/frontend/src/lib/components/ModelTable/server/Th.svelte new file mode 100644 index 000000000..d02e3e298 --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/Th.svelte @@ -0,0 +1,53 @@ + + + +
+ + +
+ + + diff --git a/frontend/src/lib/components/ModelTable/server/ThFilter.svelte b/frontend/src/lib/components/ModelTable/server/ThFilter.svelte new file mode 100644 index 000000000..6db98dc0b --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/ThFilter.svelte @@ -0,0 +1,17 @@ + + + + handler.filter(value, filterBy)} + /> + diff --git a/frontend/src/lib/components/ModelTable/server/handler.ts b/frontend/src/lib/components/ModelTable/server/handler.ts new file mode 100644 index 000000000..4f85443f4 --- /dev/null +++ b/frontend/src/lib/components/ModelTable/server/handler.ts @@ -0,0 +1,46 @@ +import { BASE_API_URL } from '$lib/utils/constants'; +import { listViewFields } from '$lib/utils/table'; +import type { urlModel } from '$lib/utils/types'; +import { tableSourceMapper } from '@skeletonlabs/skeleton'; +import type { State } from '@vincjo/datatables/remote'; +import type { TableSource } from './types'; + +export const loadTableData = async (state: State, URLModel: urlModel, endpoint: string) => { + const response = await fetch(`${endpoint}/?${getParams(state)}`).then((res) => res.json()); + state.setTotalRows(response.count); + + const bodyData = tableSourceMapper(response.results, listViewFields[URLModel as urlModel].body); + + const headData: Record = listViewFields[URLModel as urlModel].body.reduce( + (obj, key, index) => { + obj[key] = listViewFields[URLModel as urlModel].head[index]; + return obj; + }, + {} + ); + + const table: TableSource = { + head: headData, + body: bodyData, + meta: response // metaData + }; + + return table.body.map((item: Record, index: number) => { + return { ...item, meta: table.meta ? { ...table.meta[index] } : undefined }; + }); +}; + +const getParams = ({ offset, rowsPerPage, search, sort, filters }: State) => { + let params = `offset=${offset}&limit=${rowsPerPage}`; + // + if (search) { + params += `&search=${search}`; + } + if (sort) { + params += `&ordering=${sort.direction === 'desc' ? '-' : ''}${sort.orderBy}`; + } + // if (filters) { + // params += filters.map(({ filterBy, value }) => `&${filterBy}=${value}`).join(''); + // } + return params; +}; diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index 1711c4b71..dfc434b8f 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -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 } @@ -900,30 +896,6 @@ export const FIELD_COLORED_TAG_MAP: FieldColoredTagMap = { } }; -export const CUSTOM_MODEL_FETCH_MAP: { [key: string]: (load_data: any, language: string) => any } = - { - frameworks: async ({ fetch }) => { - // ({ fetch }, language) - const endpoint = `${BASE_API_URL}/frameworks/`; - const res = await fetch(endpoint); - const response_data = await res.json(); - const frameworks = response_data.results; - - let compliance_assessment_req = null; - let compliance_assessment_data = null; - - for (const framework of frameworks) { - compliance_assessment_req = await fetch( - `${BASE_API_URL}/compliance-assessments/?framework=${framework.id}` - ); - compliance_assessment_data = await compliance_assessment_req.json(); - framework.compliance_assessments = compliance_assessment_data.count; - } - - return frameworks; - } - }; - export const urlParamModelVerboseName = (model: string): string => { return URL_MODEL_MAP[model]?.verboseName || ''; }; diff --git a/frontend/src/lib/utils/types.ts b/frontend/src/lib/utils/types.ts index f61e0d86f..6736c3d9a 100644 --- a/frontend/src/lib/utils/types.ts +++ b/frontend/src/lib/utils/types.ts @@ -48,6 +48,8 @@ export const URL_MODEL = [ 'frameworks', 'requirements', 'requirement-assessments', + 'stored-libraries', + 'loaded-libraries', 'libraries', 'sso-settings', 'general-settings', diff --git a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts index 45ce69a1f..c899de624 100644 --- a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts +++ b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts @@ -2,26 +2,17 @@ import { BASE_API_URL } from '$lib/utils/constants'; import { listViewFields } from '$lib/utils/table'; import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton'; -import { CUSTOM_MODEL_FETCH_MAP, getModelInfo } from '$lib/utils/crud'; +import { getModelInfo } from '$lib/utils/crud'; import type { ModelInfo, urlModel } from '$lib/utils/types'; import type { LayoutServerLoad } from './$types'; -import { languageTag } from '$paraglide/runtime'; export const load = (async ({ fetch, params }) => { - let data = null; const model: ModelInfo = getModelInfo(params.model!); - if (Object.prototype.hasOwnProperty.call(CUSTOM_MODEL_FETCH_MAP, params.model)) { - const fetch_function = CUSTOM_MODEL_FETCH_MAP[params.model]; - data = await fetch_function({ fetch, params }, languageTag()); - } else { - const endpoint = model.endpointUrl - ? `${BASE_API_URL}/${model.endpointUrl}/${model.listViewUrlParams || ''}` - : `${BASE_API_URL}/${params.model}/${model.listViewUrlParams || ''}`; - const res = await fetch(endpoint); - data = await res.json().then((res) => res.results); - } + const endpoint = `${BASE_API_URL}/${model.endpointUrl ?? params.model}/${model.listViewUrlParams || ''}`; + const res = await fetch(endpoint); + const data = await res.json(); - const bodyData = tableSourceMapper(data, listViewFields[params.model as urlModel].body); + const bodyData = tableSourceMapper(data.results, listViewFields[params.model as urlModel].body); const headData: Record = listViewFields[params.model as urlModel].body.reduce( (obj, key, index) => { @@ -34,7 +25,7 @@ export const load = (async ({ fetch, params }) => { const table: TableSource = { head: headData, body: bodyData, - meta: data // metaData + meta: data }; return { table }; diff --git a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+server.ts b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+server.ts index b3abedec3..4dbd44cb2 100644 --- a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+server.ts +++ b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+server.ts @@ -12,7 +12,7 @@ export const GET: RequestHandler = async ({ fetch, params, url }) => { if (!res.ok) { error(res.status as NumericRange<400, 599>, await res.json()); } - const data = await res.json().then((res) => res.results); + const data = await res.json(); return new Response(JSON.stringify(data), { status: res.status, diff --git a/frontend/src/routes/(app)/(internal)/analytics/ComposerSelect.svelte b/frontend/src/routes/(app)/(internal)/analytics/ComposerSelect.svelte index 0dc02cbf0..be8eab5ac 100644 --- a/frontend/src/routes/(app)/(internal)/analytics/ComposerSelect.svelte +++ b/frontend/src/routes/(app)/(internal)/analytics/ComposerSelect.svelte @@ -14,7 +14,9 @@ let options: { label: string; value: string }[]; onMount(async () => { - const riskAssessments = await fetch('/risk-assessments').then((res) => res.json()); + const riskAssessments = await fetch('/risk-assessments') + .then((res) => res.json()) + .then((res) => res.results); options = getOptions({ objects: riskAssessments, label: 'str', diff --git a/frontend/src/routes/(app)/(internal)/frameworks/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/frameworks/[id=uuid]/+page.svelte index 8685313c5..20637a298 100644 --- a/frontend/src/routes/(app)/(internal)/frameworks/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/frameworks/[id=uuid]/+page.svelte @@ -55,7 +55,7 @@
  • {#if value} {#if key === 'library'} - {@const itemHref = `/libraries/${value.id}?loaded`} + {@const itemHref = `/loaded-libraries/${value.id}`} {value.str} {:else if key === 'scores_definition'} {#each Object.entries(value) as [key, definition]} diff --git a/frontend/src/routes/(app)/(internal)/libraries/+page.server.ts b/frontend/src/routes/(app)/(internal)/libraries/+page.server.ts index 851d604dd..c5c51956c 100644 --- a/frontend/src/routes/(app)/(internal)/libraries/+page.server.ts +++ b/frontend/src/routes/(app)/(internal)/libraries/+page.server.ts @@ -15,16 +15,16 @@ 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), fetch(loaded_libaries_endpoint) ]); - const storedLibraries = await stored_libraries_res.json().then((res) => res.results); - const loadedLibraries = await loaded_libaries_res.json().then((res) => res.results); + const storedLibraries = await stored_libraries_res.json(); + const loadedLibraries = await loaded_libaries_res.json(); const prepareRow = (row: Record) => { row.overview = [ @@ -36,8 +36,8 @@ export const load = (async ({ fetch }) => { row.reference_count && row.reference_count > 0 ? false : true; }; - storedLibraries.forEach(prepareRow); - loadedLibraries.forEach(prepareRow); + storedLibraries.results.forEach(prepareRow); + loadedLibraries.results.forEach(prepareRow); type libraryURLModel = 'stored-libraries' | 'loaded-libraries'; @@ -62,10 +62,14 @@ export const load = (async ({ fetch }) => { const storedLibrariesTable = { head: makeHeadData('stored-libraries'), meta: { urlmodel: 'stored-libraries', ...storedLibraries }, - body: tableSourceMapper(storedLibraries, listViewFields['stored-libraries'].body) + body: tableSourceMapper(storedLibraries.results, listViewFields['stored-libraries'].body) }; - const loadedLibrariesTable = makeLibrariesTable(loadedLibraries, 'loaded-libraries'); + const loadedLibrariesTable = { + head: makeHeadData('loaded-libraries'), + meta: { urlmodel: 'loaded-libraries', ...loadedLibraries }, + body: tableSourceMapper(loadedLibraries.results, listViewFields['loaded-libraries'].body) + }; const schema = z.object({ id: z.string() }); const deleteForm = await superValidate(zod(schema)); diff --git a/frontend/src/routes/(app)/(internal)/libraries/+page.svelte b/frontend/src/routes/(app)/(internal)/libraries/+page.svelte index 20833ebc2..721a1a26a 100644 --- a/frontend/src/routes/(app)/(internal)/libraries/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/libraries/+page.svelte @@ -2,37 +2,37 @@ import { LibraryUploadSchema } from '$lib/utils/schemas'; import * as m from '$paraglide/messages'; + import { page } from '$app/stores'; import FileInput from '$lib/components/Forms/FileInput.svelte'; import SuperForm from '$lib/components/Forms/Form.svelte'; import ModelTable from '$lib/components/ModelTable/ModelTable.svelte'; + import { Tab, TabGroup } from '@skeletonlabs/skeleton'; import { superValidate } from 'sveltekit-superforms'; import { zod } from 'sveltekit-superforms/adapters'; - import { page } from '$app/stores'; export let data; - import { TabGroup, Tab } from '@skeletonlabs/skeleton'; - let tabSet: number = data.loadedLibrariesTable.body.length > 0 ? 0 : 1; - $: if (data.loadedLibrariesTable.body.length === 0) tabSet = 0; + let tabSet: number = data.loadedLibrariesTable.meta.count > 0 ? 0 : 1; let fileResetSignal = false; - $: availableUpdatesCount = Object.values(data.loadedLibrariesTable.meta).filter( + $: availableUpdatesCount = Object.values(data.loadedLibrariesTable.meta.results).filter( (lib) => lib.has_update - ).length; + ).length; // NOTE: This will not be accurate for server-side pagination. We should add a new endpoint to get the count of libraries with updates. + + $: if (data.loadedLibrariesTable.meta.count === 0) tabSet = 0;
    - - {#if data.loadedLibrariesTable.body.length > 0} + {#if data.loadedLibrariesTable.meta.count > 0} {m.librariesStore()} - {data.storedLibrariesTable.body.length}{data.storedLibrariesTable.meta.count} {m.loadedLibraries()} - {data.loadedLibrariesTable.body.length} + {data.loadedLibrariesTable.meta.count} {#if availableUpdatesCount > 0} {availableUpdatesCount} {/if} {#if tabSet === 1} {/if} diff --git a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.server.ts b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.server.ts similarity index 62% rename from frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.server.ts rename to frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.server.ts index b9d49de3e..266e1e5af 100644 --- a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.server.ts +++ b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.server.ts @@ -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) diff --git a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.svelte similarity index 100% rename from frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.svelte rename to frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.svelte diff --git a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.ts b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.ts similarity index 87% rename from frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.ts rename to frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.ts index 7e62b5f40..707378dca 100644 --- a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+page.ts +++ b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+page.ts @@ -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()); diff --git a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+server.ts b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+server.ts similarity index 87% rename from frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+server.ts rename to frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+server.ts index d139db160..5615e2908 100644 --- a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/+server.ts +++ b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/+server.ts @@ -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/`; diff --git a/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/tree/+server.ts b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/tree/+server.ts new file mode 100644 index 000000000..479cec9a0 --- /dev/null +++ b/frontend/src/routes/(app)/(internal)/loaded-libraries/[id=uuid]/tree/+server.ts @@ -0,0 +1,19 @@ +import { BASE_API_URL } from '$lib/utils/constants'; + +import { error, type NumericRange } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; + +export const GET: RequestHandler = async ({ fetch, params, url }) => { + const URLModel = 'loaded-libraries'; + const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/tree/`; + const res = await fetch(endpoint); + + if (!res.ok) error(res.status as NumericRange<400, 599>, await res.json()); + + const tree = await res.json(); + return new Response(JSON.stringify(tree), { + headers: { + 'Content-Type': 'application/json' + } + }); +}; diff --git a/frontend/src/routes/(app)/(internal)/risk-matrices/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/risk-matrices/[id=uuid]/+page.svelte index 143805768..6b7579930 100644 --- a/frontend/src/routes/(app)/(internal)/risk-matrices/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/risk-matrices/[id=uuid]/+page.svelte @@ -36,7 +36,7 @@ {:else if value.id} {#if key === 'library'} - {@const itemHref = `/libraries/${value.id}?loaded`} + {@const itemHref = `/loaded-libraries/${value.id}`} {value.name} {:else} {@const itemHref = `/${ diff --git a/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.server.ts b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.server.ts new file mode 100644 index 000000000..cf1351c19 --- /dev/null +++ b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.server.ts @@ -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 + ); + } +}; diff --git a/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.svelte new file mode 100644 index 000000000..7a6dc56ca --- /dev/null +++ b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.svelte @@ -0,0 +1,225 @@ + + +
    +
    + +

    {data.library.name}

    +
    + {#if displayImportButton} + {#if loading.form} + + {:else} + { + 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} + + {/if} + + {/if} + {/if} +
    +
    +
    +

    + {m.description()}: {data.library.description} +

    +

    + {m.provider()}: {data.library.provider} +

    +

    + {m.packager()}: {data.library.packager} +

    +

    + {m.version()}: {data.library.version} +

    + {#if data.library.publication_date} +

    + {m.publicationDate()}: {formatDateOrDateTime( + data.library.publication_date, + languageTag() + )} +

    + {/if} + {#if data.library.dependencies} +

    + {m.dependencies()}: +

    +
      + {#each data.library.dependencies as dependency} +
    • {dependency.name}
    • + {/each} +
    + {/if} + {#if data.library.copyright} +

    + {m.copyright()}: {data.library.copyright} +

    + {/if} +
    +
    + + {#if riskMatrices.length > 0} + + + {#each riskMatricesPreview(riskMatrices) as riskMatrix} + + {/each} + + {/if} + + {#if referenceControls.length > 0} + + + + {/if} + + {#if threats.length > 0} + + + + {/if} + + {#if framework} +

    {m.framework()}

    + {#await data.tree} + + {m.loading()}... + + {:then tree} + + {/await} + {/if} +
    diff --git a/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.ts b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.ts new file mode 100644 index 000000000..9ac9b9738 --- /dev/null +++ b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+page.ts @@ -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 + }; +}; diff --git a/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+server.ts b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+server.ts new file mode 100644 index 000000000..ab1c06089 --- /dev/null +++ b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/+server.ts @@ -0,0 +1,25 @@ +import { BASE_API_URL } from '$lib/utils/constants'; + +import { error, type NumericRange } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; + +export const GET: RequestHandler = async ({ fetch, url, params }) => { + const URLModel = 'stored-libraries'; + const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/`; + const contentEndpoint = `${BASE_API_URL}/${URLModel}/${params.id}/content/`; + + const [res, contentRes] = await Promise.all([fetch(endpoint), fetch(contentEndpoint)]); + + if (!res.ok) error(res.status as NumericRange<400, 599>, await res.json()); + if (!contentRes.ok) error(contentRes.status as NumericRange<400, 599>, await contentRes.json()); + + const data = await res.json(); + const content = await contentRes.json(); + data.objects = content; + + return new Response(JSON.stringify(data), { + headers: { + 'Content-Type': 'application/json' + } + }); +}; diff --git a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/tree/+server.ts b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/tree/+server.ts similarity index 85% rename from frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/tree/+server.ts rename to frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/tree/+server.ts index f2e2f9421..3dff2e0ba 100644 --- a/frontend/src/routes/(app)/(internal)/libraries/[id=uuid]/tree/+server.ts +++ b/frontend/src/routes/(app)/(internal)/stored-libraries/[id=uuid]/tree/+server.ts @@ -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 = 'stored-libraries'; const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/tree`; const res = await fetch(endpoint); diff --git a/frontend/src/routes/(app)/(third-party)/[model=thirdparty_urlmodels]/+layout.server.ts b/frontend/src/routes/(app)/(third-party)/[model=thirdparty_urlmodels]/+layout.server.ts index 128452015..c899de624 100644 --- a/frontend/src/routes/(app)/(third-party)/[model=thirdparty_urlmodels]/+layout.server.ts +++ b/frontend/src/routes/(app)/(third-party)/[model=thirdparty_urlmodels]/+layout.server.ts @@ -2,24 +2,17 @@ import { BASE_API_URL } from '$lib/utils/constants'; import { listViewFields } from '$lib/utils/table'; import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton'; -import { CUSTOM_MODEL_FETCH_MAP, getModelInfo } from '$lib/utils/crud'; +import { getModelInfo } from '$lib/utils/crud'; import type { ModelInfo, urlModel } from '$lib/utils/types'; import type { LayoutServerLoad } from './$types'; -import { languageTag } from '$paraglide/runtime'; export const load = (async ({ fetch, params }) => { - let data = null; const model: ModelInfo = getModelInfo(params.model!); - if (Object.prototype.hasOwnProperty.call(CUSTOM_MODEL_FETCH_MAP, params.model)) { - const fetch_function = CUSTOM_MODEL_FETCH_MAP[params.model]; - data = await fetch_function({ fetch, params }, languageTag()); - } else { - const endpoint = `${BASE_API_URL}/${params.model}/${model.listViewUrlParams || ''}`; - const res = await fetch(endpoint); - data = await res.json().then((res) => res.results); - } + const endpoint = `${BASE_API_URL}/${model.endpointUrl ?? params.model}/${model.listViewUrlParams || ''}`; + const res = await fetch(endpoint); + const data = await res.json(); - const bodyData = tableSourceMapper(data, listViewFields[params.model as urlModel].body); + const bodyData = tableSourceMapper(data.results, listViewFields[params.model as urlModel].body); const headData: Record = listViewFields[params.model as urlModel].body.reduce( (obj, key, index) => { @@ -32,7 +25,7 @@ export const load = (async ({ fetch, params }) => { const table: TableSource = { head: headData, body: bodyData, - meta: data // metaData + meta: data }; return { table };