Skip to content

Commit 3437881

Browse files
committed
Create model for DiscoveredDependency #447
Signed-off-by: Jono Yang <[email protected]>
1 parent 08d7b16 commit 3437881

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 4.0.5 on 2022-06-14 19:26
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import scanpipe.models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('scanpipe', '0016_discoveredpackage_package_uid'),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name='DiscoveredDependency',
17+
fields=[
18+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19+
('purl', models.CharField(help_text='The Package URL of this dependency.', max_length=1024)),
20+
('extracted_requirement', models.CharField(help_text='The version requirements of this dependency.', max_length=32)),
21+
('scope', models.CharField(help_text='The scope of this dependency, how it is used in a project.', max_length=32)),
22+
('is_runtime', models.BooleanField(default=False)),
23+
('is_optional', models.BooleanField(default=False)),
24+
('is_resolved', models.BooleanField(default=False)),
25+
('dependency_uid', models.CharField(help_text='The unique identifier of this dependency.', max_length=1024)),
26+
('for_package_uid', models.CharField(help_text='The unique identifier of the package this dependency is for.', max_length=1024)),
27+
('datafile_path', models.CharField(blank=True, help_text='The relative path to the datafile where this dependency was detected from.', max_length=1024)),
28+
('datasource_id', models.CharField(help_text='The identifier for the datafile handler used to obtain this dependency.', max_length=64)),
29+
('project', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='scanpipe.project')),
30+
],
31+
options={
32+
'abstract': False,
33+
},
34+
bases=(models.Model, scanpipe.models.SaveProjectErrorMixin),
35+
),
36+
]

scanpipe/models.py

+97
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,103 @@ def update_from_data(self, package_data):
18371837
return updated_fields
18381838

18391839

1840+
class DiscoveredDependency(
1841+
ProjectRelatedModel,
1842+
SaveProjectErrorMixin,
1843+
):
1844+
"""
1845+
A project's Discovered Dependencies are records of the dependencies used by
1846+
system and application packages discovered in the code under analysis.
1847+
"""
1848+
purl = models.CharField(
1849+
max_length=1024,
1850+
help_text=_(
1851+
"The Package URL of this dependency."
1852+
),
1853+
)
1854+
extracted_requirement = models.CharField(
1855+
max_length=32,
1856+
help_text=_(
1857+
"The version requirements of this dependency."
1858+
),
1859+
)
1860+
scope = models.CharField(
1861+
max_length=32,
1862+
help_text=_(
1863+
"The scope of this dependency, how it is used in a project."
1864+
),
1865+
)
1866+
1867+
is_runtime = models.BooleanField(default=False)
1868+
is_optional = models.BooleanField(default=False)
1869+
is_resolved = models.BooleanField(default=False)
1870+
1871+
dependency_uid = models.CharField(
1872+
max_length=1024,
1873+
help_text=_(
1874+
"The unique identifier of this dependency."
1875+
),
1876+
)
1877+
for_package_uid = models.CharField(
1878+
max_length=1024,
1879+
help_text=_(
1880+
"The unique identifier of the package this dependency is for."
1881+
),
1882+
)
1883+
datafile_path = models.CharField(
1884+
max_length=1024,
1885+
blank=True,
1886+
help_text=_(
1887+
"The relative path to the datafile where this dependency was detected from."
1888+
),
1889+
)
1890+
datasource_id = models.CharField(
1891+
max_length=64,
1892+
help_text=_(
1893+
"The identifier for the datafile handler used to obtain this dependency."
1894+
)
1895+
)
1896+
1897+
@classmethod
1898+
def create_from_data(cls, project, dependency_data):
1899+
"""
1900+
Creates and returns a DiscoveredPackage for a `project` from the `dependency_data`.
1901+
"""
1902+
if "resolved_package" in dependency_data:
1903+
dependency_data.pop("resolved_package")
1904+
discovered_dependency = cls(project=project, **dependency_data)
1905+
discovered_dependency.save()
1906+
return discovered_dependency
1907+
1908+
def update_from_data(self, dependency_data):
1909+
"""
1910+
Update this discovered dependency instance with the provided `dependency_data`.
1911+
The `save()` is called only if at least one field was modified.
1912+
"""
1913+
model_fields = DiscoveredPackage.model_fields()
1914+
updated_fields = []
1915+
1916+
for field_name, value in dependency_data.items():
1917+
skip_reasons = [
1918+
not value,
1919+
field_name not in model_fields,
1920+
]
1921+
if any(skip_reasons):
1922+
continue
1923+
1924+
current_value = getattr(self, field_name, None)
1925+
if not current_value:
1926+
setattr(self, field_name, value)
1927+
updated_fields.append(field_name)
1928+
elif current_value != value:
1929+
pass # TODO: handle this case
1930+
1931+
if updated_fields:
1932+
self.save()
1933+
1934+
return updated_fields
1935+
1936+
18401937
class WebhookSubscription(UUIDPKModel, ProjectRelatedModel):
18411938
target_url = models.URLField(_("Target URL"), max_length=1024)
18421939
sent = models.BooleanField(default=False)

0 commit comments

Comments
 (0)