Skip to content

Commit

Permalink
Migrate Advisory aliases field to M2M relationship
Browse files Browse the repository at this point in the history
Signed-off-by: Keshav Priyadarshi <[email protected]>
  • Loading branch information
keshav-space committed Feb 13, 2025
1 parent fabe035 commit 21eec6c
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 3 deletions.
117 changes: 117 additions & 0 deletions vulnerabilities/migrations/0089_migrate_advisory_aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#
# Copyright (c) nexB Inc. and others. All rights reserved.
# VulnerableCode is a trademark of nexB Inc.
# SPDX-License-Identifier: Apache-2.0
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
# See https://aboutcode.org for more information about nexB OSS projects.
#

from aboutcode.pipeline import LoopProgress
from django.db import migrations
from django.db import models

"""
Model and data migration for converting the Advisory aliases
JSON field to a concrete M2M Advisory Alias relationship.
"""

def bulk_update(model, items, fields, logger):
item_count = 0
if items:
try:
model.objects.bulk_update(objs=items, fields=fields)
item_count += len(items)
except Exception as e:
logger(f"Error updating Advisory: {e}")
items.clear()
return item_count


class Migration(migrations.Migration):

dependencies = [
("vulnerabilities", "0088_fix_alpine_purl_type"),
]

def populate_new_advisory_aliases_field(apps, schema_editor):
Advisory = apps.get_model("vulnerabilities", "Advisory")
Alias = apps.get_model("vulnerabilities", "Alias")
advisories = Advisory.objects.all()

chunk_size = 10000
advisories_count = advisories.count()
print(f"\nPopulate new advisory aliases relationship.")
progress = LoopProgress(
total_iterations=advisories_count,
logger=print,
progress_step=1,
)
for advisory in progress.iter(advisories.iterator(chunk_size=chunk_size)):
aliases = Alias.objects.filter(alias__in=advisory.old_aliases)
advisory.aliases.set(aliases)

def reverse_populate_new_advisory_aliases_field(apps, schema_editor):
Advisory = apps.get_model("vulnerabilities", "Advisory")
advisories = Advisory.objects.all()

updated_advisory_count = 0
batch_size = 10000
chunk_size = 10000
updated_advisory = []
progress = LoopProgress(
total_iterations=advisories.count(),
logger=print,
progress_step=1,
)
for advisory in progress.iter(advisories.iterator(chunk_size=chunk_size)):
aliases = advisory.aliases.all()
advisory.old_aliases = [alias.alias for alias in aliases]
updated_advisory.append(advisory)

if len(updated_advisory) > batch_size:
updated_advisory_count += bulk_update(
model=Advisory,
items=updated_advisory,
fields=["old_aliases"],
logger=print,
)

updated_advisory_count += bulk_update(
model=Advisory,
items=updated_advisory,
fields=["old_aliases"],
logger=print,
)

operations = [
# Rename aliases field to old_aliases
migrations.AlterModelOptions(
name="advisory",
options={"ordering": ["date_published", "unique_content_id"]},
),
migrations.AlterUniqueTogether(
name="advisory",
unique_together={("unique_content_id", "date_published", "url")},
),
migrations.RenameField(
model_name="advisory",
old_name="aliases",
new_name="old_aliases",
),
migrations.AddField(
model_name="advisory",
name="aliases",
field=models.ManyToManyField(related_name="advisories", to="vulnerabilities.alias"),
),
# Populate the new M2M aliases relation
migrations.RunPython(
code=populate_new_advisory_aliases_field,
reverse_code=reverse_populate_new_advisory_aliases_field,
),
# Delete old_alias field
migrations.RemoveField(
model_name="advisory",
name="old_aliases",
),
]
9 changes: 6 additions & 3 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1318,7 +1318,10 @@ class Advisory(models.Model):
max_length=32,
blank=True,
)
aliases = models.JSONField(blank=True, default=list, help_text="A list of alias strings")
aliases = models.ManyToManyField(
Alias,
related_name="advisories",
)
summary = models.TextField(
blank=True,
)
Expand Down Expand Up @@ -1353,8 +1356,8 @@ class Advisory(models.Model):
objects = AdvisoryQuerySet.as_manager()

class Meta:
unique_together = ["aliases", "unique_content_id", "date_published", "url"]
ordering = ["aliases", "date_published", "unique_content_id"]
unique_together = ["unique_content_id", "date_published", "url"]
ordering = ["date_published", "unique_content_id"]

def save(self, *args, **kwargs):
checksum = hashlib.md5()
Expand Down

0 comments on commit 21eec6c

Please sign in to comment.