diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f25ff8..60a3533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,30 @@ +# 6.3.0 (2026-06-29) + +## Changes + +- Automated annotation adding in write-record-endpoints has been removed. + The new query parameter `add_submission_tag` is added to write-record-endpoints. + If set to True, the server will add an automated annotation with the annotation + tags that are defined in the server-configuration (the default is False). + + # 6.2.2 (2026-06-26) -# Bugfixes +## Bugfixes - Fix a bug in authentication when non-config tokens are used. # 6.2.1 (2026-06-26) -# Bugfixes +## Bugfixes - Fix a bug in curator-writing when non-config tokens are used. # 6.2.0 (2026-06-24) -# New features +## New features - Add PUT-methods to the administration endpoints `/tokens`, `/collections` and `/admin_tokens`. This allows to update collections, tokens, and @@ -31,7 +41,7 @@ # 6.1.0 (2026-06-22) -# New features +## New features - Add the option: `--ignore-default-config-file`. If the option is provided a version 6 server will not try to initialize its configuration from the diff --git a/dump_things_service/__about__.py b/dump_things_service/__about__.py index ac1df1f..89d98bf 100644 --- a/dump_things_service/__about__.py +++ b/dump_things_service/__about__.py @@ -1 +1 @@ -__version__ = '6.2.2' +__version__ = '6.3.0' diff --git a/dump_things_service/collection.py b/dump_things_service/collection.py index d670cbc..71c1533 100644 --- a/dump_things_service/collection.py +++ b/dump_things_service/collection.py @@ -84,10 +84,11 @@ _endpoint_template = """ def {name}( data: {model_var_name}.{class_name} | Annotated[str, Body(media_type='text/plain')], api_key: str = Depends(api_key_header_scheme), + add_submission_tag: bool = False, format: Format = Format.json, ) -> JSONResponse | PlainTextResponse: - logger.info('{name}(%s, %s, %s, %s)', repr(data), repr('{class_name}'), repr({model_var_name}), repr(format)) - return {handler}('{collection}', data, '{class_name}', {model_var_name}, format, api_key) + logger.info('{name}(%s, %s, %s, %s, %s)', repr(data), repr('{class_name}'), repr({model_var_name}), repr(add_submission_tag), repr(format)) + return {handler}('{collection}', data, '{class_name}', {model_var_name}, format, add_submission_tag, api_key) """ _endpoint_curated_template = """ @@ -536,6 +537,7 @@ def store_record( class_name: str, model: Any, input_format: Format, + add_submission_tag: bool, api_key: str | None = Depends(api_key_header_scheme), ) -> JSONResponse | PlainTextResponse: if input_format == Format.json and isinstance(data, str): @@ -597,7 +599,10 @@ def store_record( instance_state.validators[collection].validate(record) with wrap_http_exception(CurieResolutionError): - stored_records = store.store_object(obj=record, submitter=user_id) + stored_records = store.store_object( + obj=record, + submitter=user_id if add_submission_tag else None, + ) if input_format == Format.ttl: format_converter = FormatConverter( diff --git a/dump_things_service/collection_endpoints.py b/dump_things_service/collection_endpoints.py index 34b9dfe..5972996 100644 --- a/dump_things_service/collection_endpoints.py +++ b/dump_things_service/collection_endpoints.py @@ -24,7 +24,6 @@ from dump_things_service import ( from dump_things_service.abstract_config import ( Configuration, CollectionConfig, - StrictModel, store_config, get_config, get_token_permissions, ) @@ -59,9 +58,6 @@ class TagSpec(BaseModel): submission_time_tag: str = 'http://semanticscience.org/resource/SIO_001083' -from pydantic import ConfigDict, Field -from dump_things_service.abstract_config import RecordDirBackendConfig, SQLiteBackendConfig, GitAuditBackendConfig - class CollectionRequest(CollectionConfig): name: str @@ -124,7 +120,7 @@ async def create_or_replace_collection( # Check for distinct directories. # TODO: we skip this currently because a number of version 5 installations - # deliberately put inboxes into the same path. Those configuration cannot + # deliberately put inboxes into the same path. Those configurations cannot # be established if this check is performed. Instead of the `if False:`- # clause, we should introduce a configuration for the server to specify # whether unique directories are required. diff --git a/dump_things_service/store/model_store.py b/dump_things_service/store/model_store.py index 1ec971e..f77be4e 100644 --- a/dump_things_service/store/model_store.py +++ b/dump_things_service/store/model_store.py @@ -45,7 +45,7 @@ class _ModelStore: def store_object( self, obj: BaseModel, - submitter: str, + submitter: str | None, ) -> Iterable[tuple[str, dict]]: if obj.__class__.__name__ == 'Thing' and 'dlthings:placeholder' in (obj.annotations or dict()): return [] @@ -72,7 +72,7 @@ class _ModelStore: def _store_flat_object( self, obj: BaseModel, - submitter: str, + submitter: str | None, ) -> dict: iri = self.pid_to_iri(obj.pid) class_name = obj.__class__.__name__ @@ -83,7 +83,9 @@ class _ModelStore: ) # Add the submitter id to the record annotations - self.annotate(json_object, submitter) + if submitter: + self.annotate(json_object, submitter) + self.backend.add_record( iri=iri, class_name=class_name, diff --git a/dump_things_service/tests/test_basic.py b/dump_things_service/tests/test_basic.py index 34ce9f7..7591678 100644 --- a/dump_things_service/tests/test_basic.py +++ b/dump_things_service/tests/test_basic.py @@ -202,9 +202,7 @@ def test_store_record(fastapi_client_simple): headers={'x-dumpthings-token': token}, ) assert response.status_code == HTTP_200_OK - assert ( - cleaned_json(response.json(), remove_keys=('annotations',)) == extra_record - ) + assert response.json() == extra_record # Check that other collections do not report the new record for i in range(3, 6): @@ -223,13 +221,12 @@ def test_store_record(fastapi_client_simple): f'/collection_{i}/records/Thing', headers={'x-dumpthings-token': token}, ) - cleaned_response = cleaned_json(response.json(), remove_keys=('annotations',)) - assert extra_record in cleaned_response + assert extra_record in response.json() assert { 'schema_type': 'abc:Person', 'pid': pid, 'given_name': given_name, - } in cleaned_response + } in response.json() # Check pagination for i, token in basic_write_locations: @@ -240,14 +237,12 @@ def test_store_record(fastapi_client_simple): assert response.status_code == HTTP_200_OK for key in ('items', 'total', 'page', 'size', 'pages'): assert key in response.json() - records = response.json()['items'] - cleaned_response = cleaned_json(records, remove_keys=('annotations',)) - assert extra_record in cleaned_response + assert extra_record in response.json()['items'] assert { 'schema_type': 'abc:Person', 'pid': pid, 'given_name': given_name, - } in cleaned_response + } in response.json()['items'] def test_encoding(fastapi_client_simple): diff --git a/dump_things_service/tests/test_collection_administration.py b/dump_things_service/tests/test_collection_administration.py index 259d375..f98b02a 100644 --- a/dump_things_service/tests/test_collection_administration.py +++ b/dump_things_service/tests/test_collection_administration.py @@ -144,7 +144,7 @@ def test_collection_adding(fastapi_client_simple): headers={'x-dumpthings-token': new_token_representation}, ) assert response.status_code == HTTP_200_OK - assert cleaned_json(response.json()[0], ('annotations',)) == new_record + assert response.json()[0] == new_record # Remove the token response = test_client.delete( diff --git a/dump_things_service/tests/test_roundtrip.py b/dump_things_service/tests/test_roundtrip.py index 175d154..50bbb7f 100644 --- a/dump_things_service/tests/test_roundtrip.py +++ b/dump_things_service/tests/test_roundtrip.py @@ -24,6 +24,30 @@ ttl_result_record_a = """@prefix abc: . @prefix oxo: . @prefix xyz: . +xyz:HenryAdams a abc:Person ; + abc:annotations [ a abc:Annotation ; + abc:annotation_tag ; + abc:annotation_value "1970-01-01T00:00:00" ] ; + abc:given_name "Henryöäß" ; + abc:schema_type "abc:Person" . +""" + +ttl_result_record_b = """@prefix abc: . +@prefix oxo: . +@prefix xyz: . + +xyz:HenryAdams a abc:Person ; + abc:annotations [ a abc:Annotation ; + abc:annotation_tag oxo:NCIT_C54269 ; + abc:annotation_value "test_user_1" ] ; + abc:given_name "Henryöäß" ; + abc:schema_type "abc:Person" . +""" + +ttl_result_record_with_annotations_a = """@prefix abc: . +@prefix oxo: . +@prefix xyz: . + xyz:HenryAdams a abc:Person ; abc:annotations [ a abc:Annotation ; abc:annotation_tag ; @@ -35,7 +59,7 @@ xyz:HenryAdams a abc:Person ; abc:schema_type "abc:Person" . """ -ttl_result_record_b = """@prefix abc: . +ttl_result_record_with_annotations_b = """@prefix abc: . @prefix oxo: . @prefix xyz: . diff --git a/dump_things_service/tests/test_roundtrip_flatsocial.py b/dump_things_service/tests/test_roundtrip_flatsocial.py index 8c7f8d9..bb2481c 100644 --- a/dump_things_service/tests/test_roundtrip_flatsocial.py +++ b/dump_things_service/tests/test_roundtrip_flatsocial.py @@ -35,7 +35,23 @@ dlflatsocial:test_john_ttl a dlflatsocial:Person ; dlsocialmx:given_name "Johnöüß" . """ -ttl_output_record_a = """@prefix dlflat: . +ttl_output_record_a = """@prefix dlflatsocial: . +@prefix dlsocialmx: . + +dlflatsocial:another_john_ttl a dlflatsocial:Person ; + dlsocialmx:given_name "Johnöüß" . +""" + + +ttl_output_record_b = """@prefix dlflatsocial: . +@prefix dlsocialmx: . + +dlflatsocial:another_john_ttl a dlflatsocial:Person ; + dlsocialmx:given_name "Johnöüß" . +""" + + +ttl_output_record_with_annotation_a = """@prefix dlflat: . @prefix dlflatsocial: . @prefix dlsocialmx: . @prefix dlthings: . @@ -52,7 +68,7 @@ dlflatsocial:another_john_ttl a dlflatsocial:Person ; """ -ttl_output_record_b = """@prefix dlflat: . +ttl_output_record_with_annotation_b = """@prefix dlflat: . @prefix dlflatsocial: . @prefix dlsocialmx: . @prefix dlthings: . @@ -108,7 +124,7 @@ def test_json_ttl_json_dlflatsocial(fastapi_client_simple): headers={'x-dumpthings-token': 'token-all'}, ) assert response.status_code == HTTP_200_OK - json_object = cleaned_json(response.json(), remove_keys=('annotations',)) + json_object = response.json() assert cleaned_json(json_object, remove_keys=('schema_type',)) != json_record json_object['pid'] = json_record['pid'] assert json_object == json_record_out diff --git a/dump_things_service/validate.py b/dump_things_service/validate.py index 4dd6f6f..eae50f2 100644 --- a/dump_things_service/validate.py +++ b/dump_things_service/validate.py @@ -38,6 +38,7 @@ def validate_record( class_name: str, model: Any, input_format: Format, + _: bool, api_key: str | None = Depends(api_key_header_scheme), ) -> JSONResponse: