Skip to content

Commit

Permalink
names: add internal id to identify the names
Browse files Browse the repository at this point in the history
  • Loading branch information
kpsherva committed Dec 12, 2024
1 parent ca6e3ae commit 3d5d006
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 6 deletions.
36 changes: 36 additions & 0 deletions invenio_vocabularies/alembic/3ba812d80559_add_internal_name_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#
# This file is part of Invenio.
# Copyright (C) 2024 CERN.
#
# Invenio is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Add internal name ID."""

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "3ba812d80559"
down_revision = "55a700f897b6"
branch_labels = ()
depends_on = None


def upgrade():
"""Upgrade database."""
op.add_column(
"name_metadata", sa.Column("internal_id", sa.String(length=255), nullable=True)
)
op.create_unique_constraint(
op.f("uq_name_metadata_internal_id"), "name_metadata", ["internal_id"]
)


def downgrade():
"""Downgrade database."""
op.drop_constraint(
op.f("uq_name_metadata_internal_id"), "name_metadata", type_="unique"
)
op.drop_column("name_metadata", "internal_id")
24 changes: 24 additions & 0 deletions invenio_vocabularies/contrib/names/components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# This file is part of Invenio.
# Copyright (C) 2024 CERN.
#
# Invenio is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Names service components."""

from invenio_records_resources.services.records.components import ServiceComponent


class InternalIDComponent(ServiceComponent):
"""Service component for internal id field."""

field = "internal_id"

def create(self, identity, data=None, record=None, **kwargs):
"""Create handler."""
setattr(record, self.field, data.pop(self.field, None))

def update(self, identity, data=None, record=None, **kwargs):
"""Update handler."""
setattr(record, self.field, data.pop(self.field, None))
2 changes: 2 additions & 0 deletions invenio_vocabularies/contrib/names/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from werkzeug.local import LocalProxy

from ...services.components import PIDComponent
from .components import InternalIDComponent

names_schemes = LocalProxy(lambda: current_app.config["VOCABULARIES_NAMES_SCHEMES"])

Expand Down Expand Up @@ -67,6 +68,7 @@ class NamesSearchOptions(SearchOptions):

service_components = [
# Order of components are important!
InternalIDComponent,
DataComponent,
PIDComponent,
RelationsComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@
"uniqueItems": true
}
}
}
}
4 changes: 3 additions & 1 deletion invenio_vocabularies/contrib/names/names.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from invenio_records.dumpers import SearchDumper
from invenio_records.dumpers.indexedat import IndexedAtDumperExt
from invenio_records.dumpers.relations import RelationDumperExt
from invenio_records.systemfields import RelationsField
from invenio_records.systemfields import ModelField, RelationsField
from invenio_records_resources.factories.factory import RecordTypeFactory
from invenio_records_resources.records.systemfields import (
ModelPIDField,
Expand Down Expand Up @@ -47,10 +47,12 @@
# cannot set to nullable=False because it would fail at
# service level when create({}), see records-resources.
"pid": db.Column(db.String(255), unique=True),
"internal_id": db.Column(db.String(255), unique=True, nullable=True),
},
schema_version="1.0.0",
schema_path="local://names/name-v1.0.0.json",
index_name="names-name-v2.0.0",
record_cls_attrs={"internal_id": ModelField("internal_id", dump=False)},
record_relations=name_relations,
record_dumper=SearchDumper(
model_fields={"pid": ("id", str)},
Expand Down
3 changes: 2 additions & 1 deletion invenio_vocabularies/contrib/names/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class NameSchema(BaseVocabularySchema, ModePIDFieldVocabularyMixin):
so it does not inherit from it.
"""

internal_id = fields.Str(allow_none=True)
name = SanitizedUnicode()
given_name = SanitizedUnicode()
family_name = SanitizedUnicode()
Expand Down Expand Up @@ -66,7 +67,7 @@ def validate_names(self, data, **kwargs):
raise ValidationError({"family_name": messages})

@validates_schema
def validate_affiliatons(self, data, **kwargs):
def validate_affiliations(self, data, **kwargs):
"""Validate names."""
affiliations = data.get("affiliations", [])
seen_names = set()
Expand Down
4 changes: 2 additions & 2 deletions invenio_vocabularies/contrib/names/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def resolve(self, identity, id_, id_type, many=False):
else:
results = self._read_many(identity, search_query, max_records=1)

# cant use the results_item because it returns dicts intead of records
# cant use the results_item because it returns dicts instead of records
total = results.hits.total["value"]
if total == 0:
# Not a PID but trated as such
# Not a PID but treated as such
raise PIDDoesNotExistError(pid_type=id_type, pid_value=id_)
if many:
for result in results:
Expand Down
3 changes: 2 additions & 1 deletion tests/contrib/names/test_names_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def test_simple_flow(app, service, identity, name_full_data, example_affiliation

# Create it
item = service.create(identity, name_full_data)

id_ = item.id

# add dereferenced values
Expand Down Expand Up @@ -68,7 +69,7 @@ def test_simple_flow(app, service, identity, name_full_data, example_affiliation
# Fail to retrieve it
# - db
# only the metadata is removed from the record, it is still resolvable
base_keys = {"created", "updated", "id", "links", "revision_id"}
base_keys = {"created", "updated", "id", "links", "revision_id", "internal_id"}
deleted_rec = service.read(identity, id_).to_dict()
assert set(deleted_rec.keys()) == base_keys
# - search
Expand Down

0 comments on commit 3d5d006

Please sign in to comment.