Skip to content

Commit

Permalink
Ca 651 add labels on assets and fix creation from forms (#1094)
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-intuitem authored Nov 28, 2024
2 parents c9cacbe + 515de01 commit 4f6c867
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 50 deletions.
19 changes: 19 additions & 0 deletions backend/core/migrations/0042_asset_filtering_labels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.1.1 on 2024-11-28 10:13

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("core", "0041_add_ref_id_to_project_appliedcontrol_assessment"),
]

operations = [
migrations.AddField(
model_name="asset",
name="filtering_labels",
field=models.ManyToManyField(
blank=True, to="core.filteringlabel", verbose_name="Labels"
),
),
]
4 changes: 3 additions & 1 deletion backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1206,7 +1206,9 @@ def __str__(self):
return self.folder.name + "/" + self.name


class Asset(NameDescriptionMixin, FolderMixin, PublishInRootFolderMixin):
class Asset(
NameDescriptionMixin, FolderMixin, PublishInRootFolderMixin, FilteringLabelMixin
):
class Type(models.TextChoices):
"""
The type of the asset.
Expand Down
2 changes: 1 addition & 1 deletion backend/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class AssetReadSerializer(AssetWriteSerializer):
disaster_recovery_objectives = serializers.JSONField(
source="get_disaster_recovery_objectives_display"
)

filtering_labels = FieldsRelatedField(["folder"], many=True)
type = serializers.CharField(source="get_type_display")


Expand Down
52 changes: 24 additions & 28 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,36 @@ def _process_request_data(self, request: Request) -> None:
elif not request.data[field][0]:
request.data[field] = []

def _process_labels(self, labels):
"""
Creates a FilteringLabel and replaces the value with the ID of the newly created label.
"""
new_labels = []
for label in labels:
try:
uuid.UUID(label, version=4)
new_labels.append(label)
except ValueError:
new_label = FilteringLabel(label=label)
new_label.full_clean()
new_label.save()
new_labels.append(str(new_label.id))
return new_labels

def create(self, request: Request, *args, **kwargs) -> Response:
self._process_request_data(request)
if request.data.get("filtering_labels"):
request.data["filtering_labels"] = self._process_labels(
request.data["filtering_labels"]
)
return super().create(request, *args, **kwargs)

def update(self, request: Request, *args, **kwargs) -> Response:
self._process_request_data(request)
if request.data.get("filtering_labels"):
request.data["filtering_labels"] = self._process_labels(
request.data["filtering_labels"]
)
return super().update(request, *args, **kwargs)

def partial_update(self, request: Request, *args, **kwargs) -> Response:
Expand Down Expand Up @@ -448,34 +472,6 @@ class VulnerabilityViewSet(BaseModelViewSet):
def status(self, request):
return Response(dict(Vulnerability.Status.choices))

def _process_labels(self, labels):
"""
Creates a FilteringLabel and replaces the value with the ID of the newly created label.
"""
new_labels = []
for label in labels:
try:
uuid.UUID(label, version=4)
new_labels.append(label)
except ValueError:
new_label = FilteringLabel(label=label)
new_label.full_clean()
new_label.save()
new_labels.append(str(new_label.id))
return new_labels

def update(self, request: Request, *args, **kwargs) -> Response:
request.data["filtering_labels"] = self._process_labels(
request.data["filtering_labels"]
)
return super().update(request, *args, **kwargs)

def create(self, request: Request, *args, **kwargs) -> Response:
request.data["filtering_labels"] = self._process_labels(
request.data["filtering_labels"]
)
return super().create(request, *args, **kwargs)


class FilteringLabelViewSet(BaseModelViewSet):
"""
Expand Down
17 changes: 3 additions & 14 deletions frontend/src/lib/components/Forms/AutocompleteSelect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
let selectedValues: (string | undefined)[] = [];
$: selectedValues = selected.map((item) => item.value);
$: selectedValues = selected.map((item) => item.value || item.label || item);
const default_value = nullable ? null : selectedValues[0];
Expand All @@ -85,17 +85,6 @@
dispatch('change', $value);
dispatch('cache', selected);
}
$: {
selected = selected.map((option) => {
const newOption = {
label: option.label,
value: option.value || option.label
};
selected = [...selected, newOption];
return newOption;
});
}
</script>

<div {hidden}>
Expand Down Expand Up @@ -136,10 +125,10 @@
{#if option.suggested}
<span class="text-indigo-600">{option.label}</span>
<span class="text-sm text-gray-500"> (suggested)</span>
{:else if translateOptions}
{:else if translateOptions && option.label}
{safeTranslate(option.label)}
{:else}
{option.label}
{option.label || option}
{/if}
</MultiSelect>
</div>
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/lib/components/Forms/ModelForm/AssetForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,13 @@
</div>
</Dropdown>
{/if}
<AutocompleteSelect
multiple
{form}
createFromSelection={true}
options={getOptions({ objects: model.foreignKeys['filtering_labels'], label: 'label' })}
field="filtering_labels"
helpText={m.labelsHelpText()}
label={m.labels()}
allowUserOptions="append"
/>
6 changes: 4 additions & 2 deletions frontend/src/lib/utils/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,14 +370,16 @@ export const URL_MODEL_MAP: ModelMap = {
foreignKeyFields: [
{ field: 'parent_assets', urlModel: 'assets' },
{ field: 'owner', urlModel: 'users' },
{ field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO&content_type=GL' }
{ field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO&content_type=GL' },
{ field: 'filtering_labels', urlModel: 'filtering-labels' }
],
selectFields: [{ field: 'type' }],
filters: [
{ field: 'parent_assets' },
{ field: 'folder' },
{ field: 'type' },
{ field: 'owner' }
{ field: 'owner' },
{ field: 'filtering_labels' }
]
},
users: {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ export const AssetSchema = baseNamedObject({
})
.optional(),
reference_link: z.string().url().optional().or(z.literal('')),
owner: z.string().uuid().optional().array().optional()
owner: z.string().uuid().optional().array().optional(),
filtering_labels: z.string().optional().array().optional()
});

export const FilteringLabelSchema = z.object({
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/lib/utils/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,8 @@ export const listViewFields: ListViewFieldsConfig = {
'securityObjectives',
'disasterRecoveryObjectives',
'owner',
'domain'
'domain',
'labels'
],
body: [
'name',
Expand All @@ -408,11 +409,13 @@ export const listViewFields: ListViewFieldsConfig = {
'security_objectives',
'disaster_recovery_objectives',
'owner',
'folder'
'folder',
'filtering_labels'
],
filters: {
folder: DOMAIN_FILTER,
type: ASSET_TYPE_FILTER
type: ASSET_TYPE_FILTER,
filtering_labels: LABELS_FILTER
}
},
users: {
Expand Down

0 comments on commit 4f6c867

Please sign in to comment.