Skip to content

Commit

Permalink
Merge pull request #852 from intuitem/perf/load-functions
Browse files Browse the repository at this point in the history
perf/load functions
  • Loading branch information
ab-smith authored Sep 22, 2024
2 parents afc4810 + f33b97c commit 0436803
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 11 deletions.
76 changes: 76 additions & 0 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
BUILD,
VERSION,
)

from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
from django.core.cache import cache

from django.contrib.auth.models import Permission
from django.core.files.storage import default_storage
from django.db import models
Expand Down Expand Up @@ -57,6 +63,10 @@

User = get_user_model()

SHORT_CACHE_TTL = 2 # mn
MED_CACHE_TTL = 5 # mn
LONG_CACHE_TTL = 60 # mn


class BaseModelViewSet(viewsets.ModelViewSet):
filter_backends = [
Expand Down Expand Up @@ -133,6 +143,10 @@ def partial_update(self, request: Request, *args, **kwargs) -> Response:
self._process_request_data(request)
return super().partial_update(request, *args, **kwargs)

def destroy(self, request: Request, *args, **kwargs) -> Response:
self._process_request_data(request)
return super().destroy(request, *args, **kwargs)

class Meta:
abstract = True

Expand Down Expand Up @@ -248,6 +262,12 @@ class ThreatViewSet(BaseModelViewSet):
filterset_fields = ["folder", "risk_scenarios"]
search_fields = ["name", "provider", "description"]

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=False, name="Get threats count")
def threats_count(self, request):
return Response({"results": threats_count_per_name(request.user)})
Expand Down Expand Up @@ -276,10 +296,12 @@ class ReferenceControlViewSet(BaseModelViewSet):
filterset_fields = ["folder", "category", "csf_function"]
search_fields = ["name", "description", "provider"]

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get category choices")
def category(self, request):
return Response(dict(ReferenceControl.CATEGORY))

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get function choices")
def csf_function(self, request):
return Response(dict(ReferenceControl.CSF_FUNCTION))
Expand Down Expand Up @@ -340,6 +362,7 @@ def per_status(self, request):
data = assessment_per_status(request.user, RiskAssessment)
return Response({"results": data})

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get status choices")
def status(self, request):
return Response(dict(RiskAssessment.Status.choices))
Expand Down Expand Up @@ -624,18 +647,22 @@ class AppliedControlViewSet(BaseModelViewSet):
]
search_fields = ["name", "description", "risk_scenarios", "requirement_assessments"]

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get status choices")
def status(self, request):
return Response(dict(AppliedControl.Status.choices))

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get category choices")
def category(self, request):
return Response(dict(AppliedControl.CATEGORY))

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get csf_function choices")
def csf_function(self, request):
return Response(dict(AppliedControl.CSF_FUNCTION))

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get effort choices")
def effort(self, request):
return Response(dict(AppliedControl.EFFORT))
Expand Down Expand Up @@ -761,6 +788,7 @@ class PolicyViewSet(AppliedControlViewSet):
]
search_fields = ["name", "description", "risk_scenarios", "requirement_assessments"]

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get csf_function choices")
def csf_function(self, request):
return Response(dict(AppliedControl.CSF_FUNCTION))
Expand All @@ -782,14 +810,17 @@ class RiskScenarioViewSet(BaseModelViewSet):
"applied_controls",
]

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get treatment choices")
def treatment(self, request):
return Response(dict(RiskScenario.TREATMENT_OPTIONS))

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get qualification choices")
def qualifications(self, request):
return Response(dict(RiskScenario.QUALIFICATIONS))

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=True, name="Get probability choices")
def probability(self, request, pk):
undefined = {-1: "--"}
Expand All @@ -802,6 +833,7 @@ def probability(self, request, pk):
choices = undefined | _choices
return Response(choices)

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=True, name="Get impact choices")
def impact(self, request, pk):
undefined = dict([(-1, "--")])
Expand All @@ -814,6 +846,7 @@ def impact(self, request, pk):
choices = undefined | _choices
return Response(choices)

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=True, name="Get strength of knowledge choices")
def strength_of_knowledge(self, request, pk):
undefined = {-1: RiskScenario.DEFAULT_SOK_OPTIONS[-1]}
Expand Down Expand Up @@ -1104,7 +1137,16 @@ def perform_create(self, serializer):
is_recursive=True,
)
ra4.perimeter_folders.add(folder)
# Clear the cache after a new folder is created - purposely clearing everything

def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)

def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)

@method_decorator(cache_page(60 * MED_CACHE_TTL))
@method_decorator(vary_on_cookie)
@action(detail=False, methods=["get"])
def org_tree(self, request):
"""
Expand Down Expand Up @@ -1150,6 +1192,8 @@ def org_tree(self, request):
return Response(tree)


@cache_page(60 * SHORT_CACHE_TTL)
@vary_on_cookie
@api_view(["GET"])
@permission_classes([permissions.IsAuthenticated])
def get_counters_view(request):
Expand All @@ -1159,6 +1203,8 @@ def get_counters_view(request):
return Response({"results": get_counters(request.user)})


@cache_page(60 * SHORT_CACHE_TTL)
@vary_on_cookie
@api_view(["GET"])
@permission_classes([permissions.IsAuthenticated])
def get_metrics_view(request):
Expand All @@ -1171,6 +1217,8 @@ def get_metrics_view(request):
# TODO: Add all the proper docstrings for the following list of functions


@cache_page(60 * SHORT_CACHE_TTL)
@vary_on_cookie
@api_view(["GET"])
@permission_classes([permissions.IsAuthenticated])
def get_agg_data(request):
Expand Down Expand Up @@ -1254,6 +1302,8 @@ class FrameworkViewSet(BaseModelViewSet):
search_fields = ["name", "description"]
ordering_fields = ["name", "description"]

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@method_decorator(vary_on_cookie)
@action(detail=False, methods=["get"])
def names(self, request):
uuid_list = request.query_params.getlist("id[]", [])
Expand All @@ -1266,6 +1316,12 @@ 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)
Expand Down Expand Up @@ -1322,6 +1378,9 @@ class RequirementNodeViewSet(BaseModelViewSet):
filterset_fields = ["framework", "urn"]
search_fields = ["name", "description"]

def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)


class RequirementViewSet(BaseModelViewSet):
"""
Expand All @@ -1332,6 +1391,9 @@ class RequirementViewSet(BaseModelViewSet):
filterset_fields = ["framework", "urn"]
search_fields = ["name"]

def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)


class EvidenceViewSet(BaseModelViewSet):
"""
Expand Down Expand Up @@ -1415,10 +1477,13 @@ class ComplianceAssessmentViewSet(BaseModelViewSet):
search_fields = ["name", "description"]
ordering_fields = ["name", "description"]

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get status choices")
def status(self, request):
return Response(dict(ComplianceAssessment.Status.choices))

@method_decorator(cache_page(60 * MED_CACHE_TTL))
@method_decorator(vary_on_cookie)
@action(detail=True, name="Get implementation group choices")
def selected_implementation_groups(self, request, pk):
compliance_assessment = self.get_object()
Expand Down Expand Up @@ -1648,6 +1713,8 @@ def quality_check(self, request):
]
return Response({"results": res})

@method_decorator(cache_page(60 * SHORT_CACHE_TTL))
@method_decorator(vary_on_cookie)
@action(detail=True, methods=["get"])
def global_score(self, request, pk):
"""Returns the global score of the compliance assessment"""
Expand Down Expand Up @@ -1745,6 +1812,8 @@ def export(self, request, pk):
else:
return Response({"error": "Permission denied"})

@method_decorator(cache_page(60 * SHORT_CACHE_TTL))
@method_decorator(vary_on_cookie)
@action(detail=True, methods=["get"])
def donut_data(self, request, pk):
compliance_assessment = ComplianceAssessment.objects.get(id=pk)
Expand All @@ -1760,6 +1829,11 @@ class RequirementAssessmentViewSet(BaseModelViewSet):
filterset_fields = ["folder", "evidences"]
search_fields = ["name", "description"]

def update(self, request, *args, **kwargs):
response = super().update(request, *args, **kwargs)
cache.clear()
return response

@action(detail=False, name="Get updatable measures")
def updatables(self, request):
(_, object_ids_change, _) = RoleAssignment.get_accessible_object_ids(
Expand Down Expand Up @@ -1825,10 +1899,12 @@ def to_review(self, request):

return Response({"results": measures})

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get status choices")
def status(self, request):
return Response(dict(RequirementAssessment.Status.choices))

@method_decorator(cache_page(60 * LONG_CACHE_TTL))
@action(detail=False, name="Get result choices")
def result(self, request):
return Response(dict(RequirementAssessment.Result.choices))
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/lib/utils/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,7 @@ export const URL_MODEL_MAP: ModelMap = {
fileFields: ['attachment'],
foreignKeyFields: [
{ field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO&content_type=GL' },
{ field: 'applied_controls', urlModel: 'applied-controls' },
{ field: 'requirement_assessments', urlModel: 'requirement-assessments' }
{ field: 'applied_controls', urlModel: 'applied-controls' }
]
},
'compliance-assessments': {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ import { zod } from 'sveltekit-superforms/adapters';
import { z } from 'zod';
import { safeTranslate } from '$lib/utils/i18n';

import { loadDetail } from '$lib/utils/load';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async (event) => {
return await loadDetail({ event, model: getModelInfo(event.params.model), id: event.params.id });
};

export const actions: Actions = {
create: async (event) => {
const formData = await event.request.formData();
Expand Down Expand Up @@ -70,7 +77,7 @@ export const actions: Actions = {

if (fileFields) {
for (const [, file] of Object.entries(fileFields)) {
if (file.size <= 0) {
if (!file || file.size <= 0) {
continue;
}
const fileUploadEndpoint = `${BASE_API_URL}/${urlModel}/${createdObject.id}/upload/`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
export let data: PageData;
breadcrumbObject.set(data.data);
breadcrumbObject.set(data.object);
</script>

<ModelForm
Expand Down

0 comments on commit 0436803

Please sign in to comment.