-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Findings dashboard for all organizations #4007
Open
madelondohmen
wants to merge
124
commits into
main
Choose a base branch
from
feature/findings-dashboard-for-all-orgs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 123 commits
Commits
Show all changes
124 commits
Select commit
Hold shift + click to select a range
8aa5814
Report recipe for all orgs
Rieven fe1c88a
Create Findings Aggregate Report
madelondohmen e8269b2
add model and signal
Rieven f82cbfd
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven b3308bb
Merge branch 'main' of github.com:minvws/nl-kat-coordination into fea…
Rieven 7c410a2
check pk instead of id
Rieven ee9eb65
Fixes for organization Dashboard
Rieven 1567bfa
fix get findings from bytes
Rieven 92ff8a5
return 1 finding
Rieven 6ee322c
Update templates and aggregate report
madelondohmen 1114a10
Fix merge conflict
madelondohmen 1ec80c1
Fixes in templates
madelondohmen 6b717e4
fix scheduler is ready
Rieven a1ca8d9
Fix logic
Rieven eac10c7
Fix aggregate summary
madelondohmen 683033e
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen 5e636fe
Fix occurrences count
madelondohmen 80d8e94
Add sorting to reports to get latest report
madelondohmen bfc5a7d
fix none findings for orgs
Rieven ef0b5c2
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 4f43a9d
Update templates
madelondohmen 98b1598
Fix merge conflict
madelondohmen c7db309
Changes in templates
madelondohmen 68d75ef
Add findings org summary and show datetime
madelondohmen 47e1f00
change models and data
Rieven 1a50e80
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 929fcb5
More table fixes
madelondohmen 85ed236
fixes models, views, templates
Rieven 09adcc5
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 4083062
Create Multi Report Findings Report
madelondohmen c8e79d5
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen b34e2e2
Add dashboard org table to findings report
madelondohmen 0d5c5df
fix integer in seeder
Rieven e68f6ec
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 648ea8f
fix report data
Rieven d8da888
Create findings_report_data method
madelondohmen b7cb246
Show 5 most recent reports on scheduled report page
madelondohmen d464c13
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen b6ea32f
fix report data
Rieven d5dc74f
Fix findings report
madelondohmen 7b4d630
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven cd14b63
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 73ebd07
Model changes and views
Rieven 0be5dd8
Create two tabs for crisis room: dashboards and findings
madelondohmen 7a11d1b
Resolve merge conflict
madelondohmen d0fd2b3
Fix crisis room findings templates
madelondohmen 70a5657
fix views
Rieven a973727
fix views
Rieven fcc0f79
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven c8485f3
organizations findings
Rieven d095abf
Fix include template
madelondohmen 23b899d
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen 4c6b58f
Fix duplicates on finding dashboards
madelondohmen 1e93b8d
set limit report
Rieven df2551a
set offset report
Rieven aca2b86
add description
Rieven 91b59e4
Add titels to crisis rooms + styling and naming fixes
madelondohmen 7cccfa4
findings settings
Rieven cc3ecc9
extend report data with report instance
Rieven aa2ec80
Add findings to report sidemenu
madelondohmen 9a96847
Update intro text in findings report
madelondohmen 1ab95a0
Small fix in template
madelondohmen a241b95
Add highest_risk_level and report_id to report data + show only 25 cr…
madelondohmen a48c5fe
Add links to findings report
madelondohmen 88da5ea
Move textblocks to improve readability
madelondohmen 8f5f75d
Fix dashboard template
madelondohmen c07e8a2
Changes in template
madelondohmen b0cad1d
fix view
Rieven 775492b
Remove navbar and update headers
madelondohmen ac8e944
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven e78d4a4
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 081dbe1
Remove findings header
madelondohmen c327ef4
Merge branch 'main' of github.com:minvws/nl-kat-coordination into fea…
Rieven 82a7500
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 1c9fb27
Fix buttons
madelondohmen a075275
fix models and migrations
Rieven c9d2f6e
Add empty text for 0 organizations
madelondohmen 26db560
Add documentation
madelondohmen e531500
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen d3af0e1
Add created_at date and recurrence
madelondohmen f3ea66d
Cages for report and making tests easier
Rieven ca8ecd4
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 87e6fdb
Small fixes in templates
madelondohmen eca40ea
Add setup for tests
madelondohmen 9bf0e50
Update empty state text for findings dashboard
madelondohmen b9ea527
fix test
Rieven 5016ea6
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven b0a71de
fix test
Rieven cbda800
fix test
Rieven 06ba31a
Check for findings dashboard name
madelondohmen 5273f19
Add tests
madelondohmen 59e9525
add more test
Rieven 2cd168d
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
madelondohmen c4c1d1b
Update translations
madelondohmen 5529aed
Make dashboards done
Rieven 818a8eb
file changes
Rieven 978cfc2
Update translations and small fix
madelondohmen 4b2f7e1
fix test and lang
Rieven 48fa856
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 7bb8d75
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
Rieven ea90b5c
Add breadcrumbs
Rieven 8c005be
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven 0c507c9
revert the unnecessary
Rieven 4c96bf6
update recipe seder
Rieven a22f1bd
Update rocky/crisis_room/models.py
Rieven 2e9eeac
fixes for review and QA notes
Rieven 37b6663
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven c4da3db
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
Rieven 247074e
check if dashboard is updated or created
Rieven 7e212e4
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
madelondohmen 78b5a7c
Fixes after report refactoring
madelondohmen b1f6188
Create dashboard automatically after creating organization
madelondohmen 62c2d99
Template fixes
madelondohmen fa12fde
Add pending and unknown to severity table
madelondohmen 97a61ce
Fix typo
madelondohmen 1694880
00~Merge branch 'main' of github.com:minvws/nl-kat-coordination into …
Rieven 68ec10b
fix lang
Rieven 4b6dc3a
fix some tests
Rieven d9e1053
Merge branch 'main' of github.com:minvws/nl-kat-coordination into fea…
Rieven 707c94c
fix test
Rieven ebd743c
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
Rieven dff9b68
fix itest
Rieven 8be813b
fix precommit
Rieven 5586b3e
Update rocky/crisis_room/models.py
Rieven File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
=========== | ||
Crisis Room | ||
=========== | ||
|
||
In OpenKAT we differentiate two Crisis Rooms: | ||
|
||
- **Single Organization Crisis Room:** a Crisis Room for each organization separately | ||
- **General Crisis Room:** one general Crisis Room with for all organizations | ||
|
||
|
||
Single Organization Crisis Room | ||
=============================== | ||
|
||
This page shows a Crisis Room for each organization separately. | ||
Currently, this Crisis Room shows the top 10 most severe Findings. | ||
In the future it will serve as a dashboard which can be customized by the user. | ||
|
||
|
||
General Crisis Room | ||
=================== | ||
|
||
This page shows the Crisis Room for all organizations. | ||
Currently, this Crisis Room only shows the Findings, but in the future it will also show dashboards, | ||
which can be customized by the user. | ||
|
||
Findings | ||
-------- | ||
This section shows all the findings that have been identified for all organizations. | ||
These findings are shown in a table, grouped by organization and finding types. | ||
|
||
Every organization has one default report recipe. This recipe is used to create an Aggregate Findings Report. | ||
The output of this report, for each organization, is shown in this section. | ||
|
||
The default settings for this report recipe are: | ||
|
||
- report_name_format = ``Crisis Room Aggregate Report`` | ||
- ooi_types = ``["IPAddressV6", "Hostname", "IPAddressV4", "URL"]`` | ||
- scan_level = ``[1, 2, 3, 4]`` | ||
- scan_type = ``["declared"]`` | ||
- report_types = ``["systems-report", "findings-report"]`` | ||
- cron_expression = ``0 * * * *`` (every hour) | ||
|
||
It is possible to update the report recipe*. To do this: | ||
|
||
- Go to "Reports"- Click on the tab "Scheduled" | ||
- Look for the "Criris Room Aggregate Report" | ||
- Open the row | ||
- Click on "Edit report recipe" | ||
|
||
*\*Note: if you want to update the report recipe, you have to do this for every organization.* | ||
|
||
Create a Findings Dashboard for Your Organization | ||
================================================= | ||
|
||
OpenKAT automates the process of creating findings dashboards for your organization. | ||
|
||
Steps to Create a Findings Dashboard: | ||
-------------------------------------- | ||
|
||
1. **Install OpenKAT or Add a New Organization:** | ||
Ensure that you have OpenKAT installed or a new organization has been added to your setup. | ||
|
||
2. **Navigate to Your OpenKAT Installation Directory:** | ||
Open a terminal and change to the OpenKAT installation folder: | ||
|
||
.. code-block:: bash | ||
|
||
cd nl-kat-coordination | ||
|
||
3. **Go to the 'rocky' Folder:** | ||
Within the OpenKAT directory, enter the ``rocky`` folder: | ||
|
||
.. code-block:: bash | ||
|
||
cd rocky | ||
|
||
4. **Run the Dashboard Creation Command:** | ||
Execute the following command to create the findings dashboard: | ||
|
||
.. code-block:: bash | ||
|
||
make dashboards | ||
|
||
What Happens After Running the Command: | ||
--------------------------------------- | ||
|
||
- The system will automatically search for all installed organizations. | ||
- A **recipe** for the findings dashboard will be generated. | ||
- A **scheduled task** will be created to generate findings reports every hour. | ||
- Findings will be **added to the organization’s crisis room** for easy access and monitoring. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import json | ||
import logging | ||
from datetime import datetime, timezone | ||
from pathlib import Path | ||
from typing import Any | ||
from uuid import uuid4 | ||
|
||
from crisis_room.models import Dashboard, DashboardData | ||
from django.conf import settings | ||
from django.core.management import BaseCommand | ||
from tools.models import Organization | ||
from tools.ooi_helpers import create_ooi | ||
|
||
from octopoes.connector.octopoes import OctopoesAPIConnector | ||
from octopoes.models.ooi.reports import ReportRecipe | ||
from rocky.bytes_client import get_bytes_client | ||
from rocky.scheduler import ReportTask, ScheduleRequest, scheduler_client | ||
|
||
FINDINGS_DASHBOARD_NAME = "Crisis Room Findings Dashboard" | ||
|
||
|
||
def get_or_create_default_dashboard( | ||
organization: Organization, octopoes_client: OctopoesAPIConnector | None = None | ||
) -> bool: | ||
valid_time = datetime.now(timezone.utc) | ||
created = False | ||
path = Path(__file__).parent / "recipe_seeder.json" | ||
|
||
with path.open("r") as recipe_seeder: | ||
recipe_default = json.load(recipe_seeder) | ||
|
||
dashboard, _ = Dashboard.objects.get_or_create(name=FINDINGS_DASHBOARD_NAME, organization=organization) | ||
|
||
dashboard_data, created = DashboardData.objects.get_or_create(dashboard=dashboard) | ||
if created: | ||
recipe = create_organization_recipe(octopoes_client, valid_time, organization, recipe_default) | ||
dashboard_data.recipe = recipe.recipe_id | ||
schedule_request = create_schedule_request(valid_time, organization, recipe) | ||
scheduler_client(organization.code).post_schedule(schedule=schedule_request) | ||
|
||
dashboard_data.findings_dashboard = True | ||
dashboard_data.save() | ||
return created | ||
|
||
|
||
def create_organization_recipe( | ||
octopoes_client: OctopoesAPIConnector | None, | ||
valid_time: datetime, | ||
organization: Organization, | ||
recipe_default: dict[str, Any], | ||
) -> ReportRecipe: | ||
report_recipe = ReportRecipe(recipe_id=uuid4(), **recipe_default) | ||
|
||
if octopoes_client is None: | ||
octopoes_client = OctopoesAPIConnector( | ||
settings.OCTOPOES_API, organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT | ||
) | ||
|
||
bytes_client = get_bytes_client(organization.code) | ||
|
||
create_ooi(api_connector=octopoes_client, bytes_client=bytes_client, ooi=report_recipe, observed_at=valid_time) | ||
return report_recipe | ||
|
||
|
||
def create_schedule_request( | ||
start_datetime: datetime, organization: Organization, report_recipe: ReportRecipe | ||
) -> ScheduleRequest: | ||
report_task = ReportTask( | ||
organisation_id=organization.code, report_recipe_id=str(report_recipe.recipe_id) | ||
).model_dump() | ||
|
||
return ScheduleRequest( | ||
scheduler_id="report", | ||
organisation=organization.code, | ||
data=report_task, | ||
schedule=report_recipe.cron_expression, | ||
deadline_at=start_datetime.isoformat(), | ||
) | ||
|
||
|
||
class Command(BaseCommand): | ||
def handle(self, *args, **options): | ||
organizations = Organization.objects.all() | ||
for organization in organizations: | ||
created = get_or_create_default_dashboard(organization) | ||
if created: | ||
logging.info("Dashboard created for organization %s", organization.name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"report_name_format": "Crisis Room Aggregate Report", | ||
"input_recipe": { | ||
"query": { | ||
"ooi_types": [ | ||
"IPAddressV6", | ||
"Hostname", | ||
"IPAddressV4", | ||
"URL" | ||
], | ||
"scan_level": [ | ||
1, | ||
2, | ||
3, | ||
4 | ||
], | ||
"scan_type": [ | ||
"declared" | ||
], | ||
"search_string": "", | ||
"order_by": "object_type", | ||
"asc_desc": "desc" | ||
} | ||
}, | ||
"report_type": "aggregate-organisation-report", | ||
"asset_report_types": [ | ||
"systems-report", | ||
"findings-report" | ||
], | ||
"cron_expression": "0 * * * *" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Generated by Django 5.0.9 on 2024-12-18 11:29 | ||
|
||
import django.core.validators | ||
import django.db.models.deletion | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
initial = True | ||
|
||
dependencies = [("tools", "0044_alter_organization_options")] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="Dashboard", | ||
fields=[ | ||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), | ||
("name", models.CharField(max_length=126)), | ||
( | ||
"organization", | ||
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to="tools.organization"), | ||
), | ||
], | ||
options={"unique_together": {("name", "organization")}}, | ||
), | ||
migrations.CreateModel( | ||
name="DashboardData", | ||
fields=[ | ||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), | ||
("recipe", models.CharField(max_length=126)), | ||
("template", models.CharField(blank=True, default="findings_report/report.html", max_length=126)), | ||
( | ||
"position", | ||
models.IntegerField( | ||
blank=True, | ||
default=1, | ||
help_text="Where on the dashboard do you want to show the data? Position 1 is the most top level and the max position is 16.", | ||
validators=[ | ||
django.core.validators.MinValueValidator(1), | ||
django.core.validators.MaxValueValidator(16), | ||
], | ||
), | ||
), | ||
( | ||
"display_in_crisis_room", | ||
models.BooleanField( | ||
default=False, help_text="Will be displayed on the general crisis room, for all organizations." | ||
), | ||
), | ||
( | ||
"display_in_dashboard", | ||
models.BooleanField( | ||
default=False, help_text="Will be displayed on a single organization dashboard" | ||
), | ||
), | ||
( | ||
"findings_dashboard", | ||
models.BooleanField( | ||
default=False, help_text="Will be displayed on the findings dashboard for all organizations" | ||
), | ||
), | ||
( | ||
"dashboard", | ||
models.ForeignKey( | ||
null=True, on_delete=django.db.models.deletion.SET_NULL, to="crisis_room.dashboard" | ||
), | ||
), | ||
], | ||
options={"unique_together": {("dashboard", "findings_dashboard"), ("dashboard", "position")}}, | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from django.core.validators import MaxValueValidator, MinValueValidator | ||
from django.db import models | ||
from django.utils.translation import gettext_lazy as _ | ||
from tools.models import Organization | ||
|
||
|
||
class Dashboard(models.Model): | ||
name = models.CharField(blank=False, max_length=126) | ||
organization = models.ForeignKey(Organization, on_delete=models.SET_NULL, null=True) | ||
|
||
class Meta: | ||
unique_together = ["name", "organization"] | ||
|
||
def __str__(self) -> str: | ||
if self.name: | ||
return f"{self.name} for organization {self.organization}" | ||
return super().__str__() | ||
|
||
|
||
class DashboardData(models.Model): | ||
dashboard = models.ForeignKey(Dashboard, on_delete=models.SET_NULL, null=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. null is not allowed. On_delete from a dashboard, the dashboardData should also be deleted. so cascade. |
||
recipe = models.CharField(blank=False, max_length=126) | ||
template = models.CharField(blank=True, max_length=126, default="findings_report/report.html") | ||
position = models.IntegerField( | ||
Rieven marked this conversation as resolved.
Show resolved
Hide resolved
|
||
blank=True, | ||
default=1, | ||
validators=[MinValueValidator(1), MaxValueValidator(16)], | ||
help_text=_( | ||
"Where on the dashboard do you want to show the data? " | ||
"Position 1 is the most top level and the max position is 16." | ||
), | ||
) | ||
display_in_crisis_room = models.BooleanField( | ||
default=False, help_text=_("Will be displayed on the general crisis room, for all organizations.") | ||
) | ||
display_in_dashboard = models.BooleanField( | ||
default=False, help_text=_("Will be displayed on a single organization dashboard") | ||
) | ||
findings_dashboard = models.BooleanField( | ||
default=False, help_text=_("Will be displayed on the findings dashboard for all organizations") | ||
) | ||
|
||
class Meta: | ||
unique_together = [["dashboard", "position"], ["dashboard", "findings_dashboard"]] | ||
|
||
def __str__(self) -> str: | ||
if self.dashboard: | ||
return str(self.dashboard) | ||
return super().__str__() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{% extends "layouts/base.html" %} | ||
|
||
{% load i18n %} | ||
{% load static %} | ||
|
||
{% block content %} | ||
{% include "header.html" %} | ||
|
||
<main id="main-content" tabindex="-1" class="crisisroom"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is there a tabindex on these? |
||
<section> | ||
<div> | ||
<h1>{% translate "Crisis Room" %}</h1> | ||
<p class="emphasized">{% translate "Crisis Room overview for all organizations" %}</p> | ||
</div> | ||
{% include "crisis_room_findings.html" %} | ||
|
||
</section> | ||
</main> | ||
{% endblock content %} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can probably be a cascade, where the dashboarddata gets deleted when the dashboard gets deleted. Not just set to null. Null values are not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that the data can still exist after deleting the dashboard. So they can delete the dashboard and asign the data to another dashboard? i don't know if this was the intention. I see a possibility to re-asign data to another dashboard.