Skip to content

Commit

Permalink
Feat/add label dynamically (#998)
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-intuitem authored Oct 31, 2024
2 parents 54a1e7f + 126fdd5 commit 7dd0b7d
Show file tree
Hide file tree
Showing 19 changed files with 164 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import iam.models
import uuid
from django.db import migrations, models
import django.core.validators


class Migration(migrations.Migration):
Expand Down Expand Up @@ -47,7 +48,20 @@ class Migration(migrations.Migration):
"is_published",
models.BooleanField(default=False, verbose_name="published"),
),
("label", models.CharField(max_length=100, verbose_name="Label")),
(
"label",
models.CharField(
max_length=100,
verbose_name="Label",
validators=[
django.core.validators.RegexValidator(
code="invalid_label",
message="invalidLabel",
regex="^[\\w-]{1,36}$",
)
],
),
),
(
"folder",
models.ForeignKey(
Expand Down
14 changes: 12 additions & 2 deletions backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from django.contrib.auth import get_user_model
from django.core import serializers
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator
from django.core.validators import MaxValueValidator, RegexValidator
from django.db import models, transaction
from django.db.models import Q
from django.forms.models import model_to_dict
Expand Down Expand Up @@ -173,7 +173,17 @@ class Meta:


class FilteringLabel(FolderMixin, AbstractBaseModel, PublishInRootFolderMixin):
label = models.CharField(max_length=100, verbose_name=_("Label"))
label = models.CharField(
max_length=100,
verbose_name=_("Label"),
validators=[
RegexValidator(
regex=r"^[\w-]{1,36}$",
message="invalidLabel",
code="invalid_label",
)
],
)

def __str__(self) -> str:
return self.label
Expand Down
28 changes: 28 additions & 0 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,34 @@ 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
6 changes: 6 additions & 0 deletions frontend/messages/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "قابلة للاستغلال",
"mitigated": "مخفف",
"fixed": "مُثَبَّت",
"labels": "العلامات",
"addLabel": "إضافة تسمية",
"labelsHelpText": "يتم استخدام العلامات لتصنيف العناصر وتصفيتها.",
"filteringLabel": "ملصق",
"filteringLabels": "العلامات",
"invalidLabel": "يجب أن تكون العلامة أبجدية رقمية وتحتوي على 36 حرفًا على الأكثر",
"tags": "العلامات",
"addTag": "إضافة علامة",
"tagsHelpText": "تُستخدم العلامات لتصنيف العناصر وتصفيتها. يمكنك إضافة علامات في قسم \"إضافي\""
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Ausnutzbar",
"mitigated": "Mildernd",
"fixed": "Behoben",
"labels": "Labels",
"addLabel": "Etikett hinzufügen",
"labelsHelpText": "Etiketten dienen zum Kategorisieren und Filtern der Elemente.",
"filteringLabel": "Etikett",
"filteringLabels": "Labels",
"invalidLabel": "Das Etikett muss alphanumerisch sein und darf höchstens 36 Zeichen enthalten",
"tags": "Schlagwörter",
"addTag": "Tag hinzufügen",
"tagsHelpText": "Tags werden zum Kategorisieren und Filtern der Elemente verwendet. Sie können Tags im Abschnitt Extra hinzufügen"
Expand Down
5 changes: 3 additions & 2 deletions frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,8 @@
"fixed": "Fixed",
"labels": "Labels",
"addLabel": "Add label",
"labelsHelpText": "Labels are used to categorize and filter the items. You can add labels in the Extra section",
"labelsHelpText": "Labels are used to categorize and filter the items.",
"filteringLabel": "Label",
"filteringLabels": "Labels"
"filteringLabels": "Labels",
"invalidLabel": "Label must be alphanumeric and contain at most 36 characters"
}
6 changes: 6 additions & 0 deletions frontend/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Explotable",
"mitigated": "Mitigada",
"fixed": "Fija",
"labels": "Etiquetas",
"addLabel": "Agregar etiqueta",
"labelsHelpText": "Las etiquetas se utilizan para categorizar y filtrar los elementos.",
"filteringLabel": "Etiqueta",
"filteringLabels": "Etiquetas",
"invalidLabel": "La etiqueta debe ser alfanumérica y contener como máximo 36 caracteres.",
"tags": "Etiquetas",
"addTag": "Agregar etiqueta",
"tagsHelpText": "Las etiquetas se utilizan para categorizar y filtrar los elementos. Puedes agregar etiquetas en la sección Extra"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Exploitable",
"mitigated": "Atténuée",
"fixed": "Fixée",
"labels": "Étiquettes",
"addLabel": "Ajouter une étiquette",
"labelsHelpText": "Les étiquettes sont utilisées pour catégoriser et filtrer les éléments.",
"filteringLabel": "Étiquette",
"filteringLabels": "Étiquettes",
"invalidLabel": "L'étiquette doit être alphanumérique et contenir au maximum 36 caractères",
"tags": "Étiquettes",
"addTag": "Ajouter une étiquette",
"tagsHelpText": "Les étiquettes sont utilisées pour classer et filtrer les éléments. Vous pouvez ajouter des étiquettes dans la section Extra"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/hi.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "दोहन",
"mitigated": "कम",
"fixed": "तय",
"labels": "लेबल",
"addLabel": "लेबल जोड़ें",
"labelsHelpText": "लेबल का उपयोग वस्तुओं को वर्गीकृत और फ़िल्टर करने के लिए किया जाता है।",
"filteringLabel": "लेबल",
"filteringLabels": "लेबल",
"invalidLabel": "लेबल अल्फ़ान्यूमेरिक होना चाहिए और उसमें अधिकतम 36 अक्षर होने चाहिए",
"tags": "टैग",
"addTag": "टैग जोड़ें",
"tagsHelpText": "टैग का उपयोग आइटम को वर्गीकृत और फ़िल्टर करने के लिए किया जाता है। आप अतिरिक्त अनुभाग में टैग जोड़ सकते हैं"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Sfruttabile",
"mitigated": "Mitigata",
"fixed": "Fissa",
"labels": "Etichette",
"addLabel": "Aggiungi etichetta",
"labelsHelpText": "Le etichette vengono utilizzate per categorizzare e filtrare gli elementi.",
"filteringLabel": "Etichetta",
"filteringLabels": "Etichette",
"invalidLabel": "L'etichetta deve essere alfanumerica e contenere al massimo 36 caratteri",
"tags": "Etichette",
"addTag": "Aggiungi tag",
"tagsHelpText": "I tag vengono utilizzati per categorizzare e filtrare gli elementi. Puoi aggiungere tag nella sezione Extra"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Exploiteerbaar",
"mitigated": "Verzacht",
"fixed": "Vast",
"labels": "Etiketten",
"addLabel": "Label toevoegen",
"labelsHelpText": "Labels worden gebruikt om items te categoriseren en te filteren.",
"filteringLabel": "Label",
"filteringLabels": "Etiketten",
"invalidLabel": "Het label moet alfanumeriek zijn en maximaal 36 tekens bevatten",
"tags": "Labels",
"addTag": "Tag toevoegen",
"tagsHelpText": "Tags worden gebruikt om de items te categoriseren en te filteren. U kunt tags toevoegen in de sectie Extra"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Wykorzystywany",
"mitigated": "Złagodzony",
"fixed": "Naprawił",
"labels": "Etykiety",
"addLabel": "Dodaj etykietę",
"labelsHelpText": "Etykiety służą do kategoryzowania i filtrowania elementów.",
"filteringLabel": "Etykieta",
"filteringLabels": "Etykiety",
"invalidLabel": "Etykieta musi być alfanumeryczna i zawierać maksymalnie 36 znaków",
"tags": "Tagi",
"addTag": "Dodaj tag",
"tagsHelpText": "Tagi służą do kategoryzowania i filtrowania elementów. Możesz dodać tagi w sekcji Extra"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Explorável",
"mitigated": "Mitigado",
"fixed": "Fixo",
"labels": "Etiquetas",
"addLabel": "Adicionar rótulo",
"labelsHelpText": "Os rótulos são usados para categorizar e filtrar os itens.",
"filteringLabel": "Rótulo",
"filteringLabels": "Etiquetas",
"invalidLabel": "O rótulo deve ser alfanumérico e conter no máximo 36 caracteres",
"tags": "Etiquetas",
"addTag": "Adicionar etiqueta",
"tagsHelpText": "As tags são usadas para categorizar e filtrar os itens. Você pode adicionar tags na seção Extra"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/ro.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "Exploatabil",
"mitigated": "Atenuat",
"fixed": "Fix",
"labels": "Etichete",
"addLabel": "Adăugați o etichetă",
"labelsHelpText": "Etichetele sunt folosite pentru a clasifica și filtra articolele.",
"filteringLabel": "Eticheta",
"filteringLabels": "Etichete",
"invalidLabel": "Eticheta trebuie să fie alfanumerice și să conțină cel mult 36 de caractere",
"tags": "Etichete",
"addTag": "Adăugați etichetă",
"tagsHelpText": "Etichetele sunt folosite pentru a clasifica și filtra articolele. Puteți adăuga etichete în secțiunea Extra"
Expand Down
6 changes: 6 additions & 0 deletions frontend/messages/ur.json
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,12 @@
"exploitable": "استحصال کے قابل",
"mitigated": "تخفیف",
"fixed": "فکسڈ",
"labels": "لیبلز",
"addLabel": "لیبل شامل کریں۔",
"labelsHelpText": "لیبلز کا استعمال اشیاء کی درجہ بندی اور فلٹر کرنے کے لیے کیا جاتا ہے۔",
"filteringLabel": "لیبل",
"filteringLabels": "لیبلز",
"invalidLabel": "لیبل حروف عددی ہونا چاہیے اور زیادہ سے زیادہ 36 حروف پر مشتمل ہونا چاہیے۔",
"tags": "ٹیگز",
"addTag": "ٹیگ شامل کریں۔",
"tagsHelpText": "ٹیگز اشیاء کی درجہ بندی اور فلٹر کرنے کے لیے استعمال ہوتے ہیں۔ آپ اضافی سیکشن میں ٹیگ شامل کر سکتے ہیں۔"
Expand Down
35 changes: 34 additions & 1 deletion frontend/src/lib/components/Forms/AutocompleteSelect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
resolve: (x) => x
};
export let cachedValue: any[] | undefined = undefined;
export let createFromSelection = false;
const { value, errors, constraints } = formFieldProxy(form, field);
Expand Down Expand Up @@ -81,6 +82,18 @@
dispatch('change', $value);
dispatch('cache', selected);
}
function addOption(event) {
if (event.key === 'Enter') {
const newOption = {
label: event.target.value,
value: event.target.value
};
options = event.target.value !== '' ? [...options, newOption] : options;
selected = event.target.value !== '' ? [...selected, newOption] : selected;
event.target.value = '';
}
}
</script>

<div {hidden}>
Expand Down Expand Up @@ -108,7 +121,27 @@
{/if}
<div class="control overflow-x-clip" data-testid="form-input-{field.replaceAll('_', '-')}">
<input type="hidden" name={field} value={$value ? $value : ''} />
{#if options.length > 0}
{#if createFromSelection}
<MultiSelect
bind:selected
{options}
{...multiSelectOptions}
disabled={disabled || $$restProps.disabled}
allowEmpty={true}
{...$$restProps}
let:option
on:keyup={(event) => addOption(event)}
>
{#if option.suggested}
<span class="text-indigo-600">{option.label}</span>
<span class="text-sm text-gray-500"> (suggested)</span>
{:else if translateOptions}
{safeTranslate(option.label)}
{:else}
{option.label}
{/if}
</MultiSelect>
{:else if options.length > 0}
<MultiSelect
bind:selected
{options}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
<AutocompleteSelect
multiple
{form}
createFromSelection={true}
options={getOptions({ objects: model.foreignKeys['filtering_labels'], label: 'label' })}
field="filtering_labels"
helpText={m.labelsHelpText()}
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/lib/utils/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ export async function handleErrorResponse({
}) {
const res: Record<string, string> = await response.json();
console.error(res);
if (res.label) {
res['filtering_labels'] = res.label;
}
if (res.warning) {
setFlash({ type: 'warning', message: res.warning }, event);
return { form };
Expand All @@ -75,7 +78,7 @@ export async function handleErrorResponse({
return { form };
}
Object.entries(res).forEach(([key, value]) => {
setError(form, key, value);
setError(form, key, safeTranslate(value));
});
return fail(400, { form });
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ export const vulnerabilitySchema = baseNamedObject({
status: z.string().default('--'),
severity: z.number().default(-1),
applied_controls: z.string().uuid().optional().array().optional(),
filtering_labels: z.string().uuid().optional().array().optional()
filtering_labels: z.string().optional().array().optional()
});

const SCHEMA_MAP: Record<string, AnyZodObject> = {
Expand Down

0 comments on commit 7dd0b7d

Please sign in to comment.