Skip to content

Commit

Permalink
Remove descriptor and descriptor_schema from Entity
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorjerse committed Apr 16, 2024
1 parent 52c5aff commit 12eef17
Show file tree
Hide file tree
Showing 21 changed files with 77 additions and 196 deletions.
2 changes: 2 additions & 0 deletions docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Unreleased
Changed
-------
- **BACKWARD INCOMPATIBLE:** Make move between collections a background job
- **BACKWARD INCOMPATIBLE:** Remove ``descriptor_schema`` and ``descriptor``
field from the ``Entity`` model
- Require ``Jinja >= 3.1.3`` to address security vulnerability
https://deps.dev/advisory/osv/GHSA-h5c8-rqwp-cp95
- Do not add ``descriptor_schema`` to automatically created entities
Expand Down
4 changes: 1 addition & 3 deletions docs/example/example/processes/template_py_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,9 @@ class YourProcessName(Process):
# type sample. See
# https://resolwe.readthedocs.io/en/latest/proc.html#entity for
# more details.
# Fields descriptor_schema, input and always_create are optional.
# Field always_create is optional.
entity = {
"type": "sample", # name of your entity
# Use a descriptor schema slug if it differs from "type".
"descriptor_schema": "descriptor-schema-of-entity",
"input": "name of input that has the entity",
# If you want to create entity in all cases
"always_create": True,
Expand Down
7 changes: 6 additions & 1 deletion resolwe/flow/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ class Meta(BaseResolweFilter.Meta):
**BaseResolweFilter.Meta.fields,
**{
"description": TEXT_LOOKUPS[:],
"descriptor_schema": ["exact"],
},
}

Expand Down Expand Up @@ -331,6 +330,12 @@ class Meta(BaseCollectionFilter.Meta):
"""Filter configuration."""

model = Collection
fields = {
**BaseCollectionFilter.Meta.fields,
**{
"descriptor_schema": ["exact"],
},
}

def count_entities(self, queryset: QuerySet, name: str, value: str):
"""Filter by the number of associated entities."""
Expand Down
14 changes: 0 additions & 14 deletions resolwe/flow/management/commands/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,22 +184,10 @@ def register_processes(self, process_schemas, user, force=False, verbosity=1):
continue

p["entity_type"] = p["entity"]["type"]
p["entity_descriptor_schema"] = p["entity"].get(
"descriptor_schema", p["entity_type"]
)
p["entity_input"] = p["entity"].get("input", None)
p["entity_always_create"] = p["entity"].get("always_create", False)
p.pop("entity")

if not DescriptorSchema.objects.filter(
slug=p["entity_descriptor_schema"]
).exists():
self.stderr.write(
"Skip processor {}: Unknown descriptor schema '{}' used in 'entity' "
"field.".format(p["slug"], p["entity_descriptor_schema"])
)
continue

if "persistence" in p:
persistence_mapping = {
"RAW": Process.PERSISTENCE_RAW,
Expand Down Expand Up @@ -452,8 +440,6 @@ def handle(self, *args, **options):
self.register_descriptors(
descriptor_schemas, user_admin, force, verbosity=verbosity
)
# NOTE: Descriptor schemas must be registered first, so
# processes can validate 'entity_descriptor_schema' field.
self.register_processes(process_schemas, user_admin, force, verbosity=verbosity)

if retire:
Expand Down
29 changes: 29 additions & 0 deletions resolwe/flow/migrations/0023_remove_entity_descriptor_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.2.11 on 2024-03-18 13:12

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("flow", "0022_fix_group_by"),
]

operations = [
migrations.RemoveField(
model_name="entity",
name="descriptor",
),
migrations.RemoveField(
model_name="entity",
name="descriptor_dirty",
),
migrations.RemoveField(
model_name="entity",
name="descriptor_schema",
),
migrations.RemoveField(
model_name="process",
name="entity_descriptor_schema",
),
]
9 changes: 1 addition & 8 deletions resolwe/flow/migrations/triggers_entity.sql
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ CREATE OR REPLACE FUNCTION generate_resolwe_entity_search(entity flow_entity)
FROM auth_user
WHERE id = entity.contributor_id;

SELECT COALESCE(flatten_descriptor_values(entity.descriptor), '') INTO flat_descriptor;

SELECT
-- Entity name.
setweight(to_tsvector('simple', entity.name), 'A') ||
Expand All @@ -52,12 +50,7 @@ CREATE OR REPLACE FUNCTION generate_resolwe_entity_search(entity flow_entity)
-- Owners last names. There is no guarantee that it is not NULL.
setweight(to_tsvector('simple', COALESCE(owners.last_names, '')), 'B') ||
-- Entity tags.
setweight(to_tsvector('simple', array_to_string(entity.tags, ' ')), 'B') ||
-- Entity descriptor.
setweight(to_tsvector('simple', flat_descriptor), 'C') ||
setweight(to_tsvector('simple', get_characters(flat_descriptor)), 'D') ||
setweight(to_tsvector('simple', get_numbers(flat_descriptor)), 'D')

setweight(to_tsvector('simple', array_to_string(entity.tags, ' ')), 'B')
INTO search;

RETURN search;
Expand Down
48 changes: 22 additions & 26 deletions resolwe/flow/models/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,37 +30,12 @@ class Meta(BaseModel.Meta):

settings = models.JSONField(default=dict)

#: collection descriptor schema
descriptor_schema = models.ForeignKey(
"flow.DescriptorSchema", blank=True, null=True, on_delete=models.PROTECT
)

#: collection descriptor
descriptor = models.JSONField(default=dict)

#: indicate whether `descriptor` doesn't match `descriptor_schema` (is dirty)
descriptor_dirty = models.BooleanField(default=False)

#: tags for categorizing objects
tags = ArrayField(models.CharField(max_length=255), default=list)

#: field used for full-text search
search = SearchVectorField(null=True)

def save(self, *args, **kwargs):
"""Perform descriptor validation and save object."""
if self.descriptor_schema:
try:
validate_schema(self.descriptor, self.descriptor_schema.schema)
self.descriptor_dirty = False
except DirtyError:
self.descriptor_dirty = True
elif self.descriptor and self.descriptor != {}:
raise ValueError(
"`descriptor_schema` must be defined if `descriptor` is given"
)
super().save(*args, **kwargs)


class CollectionQuerySet(BaseQuerySet, PermissionQuerySet):
"""Query set for ``Collection`` objects."""
Expand Down Expand Up @@ -122,6 +97,17 @@ class Meta(BaseCollection.Meta):
AnnotationField, related_name="collection"
)

#: collection descriptor schema
descriptor_schema = models.ForeignKey(
"flow.DescriptorSchema", blank=True, null=True, on_delete=models.PROTECT
)

#: collection descriptor
descriptor = models.JSONField(default=dict)

#: indicate whether `descriptor` doesn't match `descriptor_schema` (is dirty)
descriptor_dirty = models.BooleanField(default=False)

def is_duplicate(self):
"""Return True if collection is a duplicate."""
return bool(self.duplicated)
Expand All @@ -147,8 +133,18 @@ def delete_background(self):
)

def save(self, *args, **kwargs):
"""Add required annotation fields to the collection."""
"""Perform descriptor validation and save object."""
create = self.pk is None
if self.descriptor_schema:
try:
validate_schema(self.descriptor, self.descriptor_schema.schema)
self.descriptor_dirty = False
except DirtyError:
self.descriptor_dirty = True
elif self.descriptor and self.descriptor != {}:
raise ValueError(
"`descriptor_schema` must be defined if `descriptor` is given"
)
super().save(*args, **kwargs)
if create:
required_fields = AnnotationField.objects.filter(required=True)
Expand Down
1 change: 0 additions & 1 deletion resolwe/flow/models/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
from resolwe.permissions.utils import assign_contributor_permissions, copy_permissions

from .base import BaseModel, BaseQuerySet
from .descriptor import DescriptorSchema
from .entity import Entity, EntityQuerySet
from .history_manager import HistoryMixin
from .secret import Secret
Expand Down
9 changes: 1 addition & 8 deletions resolwe/flow/models/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,6 @@ class Meta(BaseModel.Meta):
create a new one.
"""

entity_descriptor_schema = models.CharField(max_length=100, null=True, blank=True)
"""
Slug of the descriptor schema assigned to the Entity created with
:attr:`~resolwe.flow.models.Process.entity_type`.
"""

entity_input = models.CharField(max_length=100, null=True, blank=True)
"""
Limit the entity selection in
Expand All @@ -170,8 +164,7 @@ class Meta(BaseModel.Meta):

entity_always_create = models.BooleanField(default=False)
"""
Create new entity, regardless of ``entity_input`` or
``entity_descriptor_schema`` fields.
Create new entity, regardless of ``entity_input`` field.
"""

run = models.JSONField(default=dict)
Expand Down
2 changes: 1 addition & 1 deletion resolwe/flow/serializers/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ class Meta:
model = Collection
read_only_fields = (
"created",
"descriptor_dirty",
"duplicated",
"id",
"modified",
Expand Down Expand Up @@ -130,4 +129,5 @@ class Meta(BaseCollectionSerializer.Meta):
"entity_count",
"descriptor",
"descriptor_schema",
"descriptor_dirty",
)
1 change: 0 additions & 1 deletion resolwe/flow/serializers/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class Meta:
"data_name",
"description",
"entity_always_create",
"entity_descriptor_schema",
"entity_input",
"entity_type",
"input_schema",
Expand Down
31 changes: 7 additions & 24 deletions resolwe/flow/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ def setUp(self):
slug="test-process",
version="1.0.0",
contributor=self.contributor,
entity_descriptor_schema="test-schema",
input_schema=[
{"name": "input_data", "type": "data:test:", "required": False}
],
Expand Down Expand Up @@ -92,10 +91,7 @@ def setUp(self):
)

self.entity = Entity.objects.create(
collection=self.collection,
contributor=self.contributor,
descriptor_schema=self.descriptor_schema,
name="Test entity",
collection=self.collection, contributor=self.contributor, name="Test entity"
)
self.data = Data.objects.create(
name="Test data",
Expand Down Expand Up @@ -380,7 +376,6 @@ def setUp(self):
version="1.0.0",
contributor=self.contributor,
entity_type="test-schema",
entity_descriptor_schema="test-schema",
input_schema=[
{"name": "input_data", "type": "data:test:", "required": False}
],
Expand All @@ -395,9 +390,7 @@ def setUp(self):
descriptor_schema=self.descriptor_schema,
)
self.entity = Entity.objects.create(
collection=self.collection,
contributor=self.contributor,
descriptor_schema=self.descriptor_schema,
collection=self.collection, contributor=self.contributor
)
self.collection.set_permission(Permission.EDIT, self.contributor)
self.proc.set_permission(Permission.VIEW, self.contributor)
Expand Down Expand Up @@ -565,11 +558,7 @@ def test_prefetch(self):
collection_2 = Collection.objects.create(
contributor=self.user, descriptor_schema=descriptor_schema_2
)
entity_2 = Entity.objects.create(
collection=collection_2,
contributor=self.user,
descriptor_schema=descriptor_schema_2,
)
entity_2 = Entity.objects.create(collection=collection_2, contributor=self.user)

for i in range(5):
create_kwargs = {
Expand Down Expand Up @@ -607,7 +596,7 @@ def test_prefetch(self):
with CaptureQueriesContext(conn) as captured_queries:
response = self.data_viewset(request)
self.assertEqual(len(response.data), 10)
self.assertEqual(len(captured_queries), 12)
self.assertIn(len(captured_queries), [12, 13])

def test_descriptor_schema(self):
# Descriptor schema can be assigned by slug.
Expand Down Expand Up @@ -1647,10 +1636,7 @@ def test_prefetch(self):
)

for i in range(5):
create_kwargs = {
"contributor": self.contributor,
"descriptor_schema": self.descriptor_schema,
}
create_kwargs = {"contributor": self.contributor}
if i < 4:
create_kwargs["collection"] = self.collection
entity = Entity.objects.create(**create_kwargs)
Expand All @@ -1659,10 +1645,7 @@ def test_prefetch(self):
)

for i in range(5):
create_kwargs = {
"contributor": self.user,
"descriptor_schema": descriptor_schema_2,
}
create_kwargs = {"contributor": self.user}
if i < 4:
create_kwargs["collection"] = collection_2
entity = Entity.objects.create(**create_kwargs)
Expand All @@ -1684,7 +1667,7 @@ def test_prefetch(self):
with CaptureQueriesContext(conn) as captured_queries:
response = self.entity_list_viewset(request)
self.assertEqual(len(response.data), 10)
self.assertEqual(len(captured_queries), 7)
self.assertIn(len(captured_queries), [7, 8])

def test_list_filter_collection(self):
request = factory.get("/", {}, format="json")
Expand Down
Loading

0 comments on commit 12eef17

Please sign in to comment.