Do not create automated submission-tags by default #240
10 changed files with 80 additions and 31 deletions
18
CHANGELOG.md
18
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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = '6.2.2'
|
||||
__version__ = '6.3.0'
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -24,6 +24,30 @@ ttl_result_record_a = """@prefix abc: <http://example.org/person-schema/abc/> .
|
|||
@prefix oxo: <http://purl.obolibrary.org/obo/> .
|
||||
@prefix xyz: <http://example.org/person-schema/xyz/> .
|
||||
|
||||
xyz:HenryAdams a abc:Person ;
|
||||
abc:annotations [ a abc:Annotation ;
|
||||
abc:annotation_tag <http://semanticscience.org/resource/SIO_001083> ;
|
||||
abc:annotation_value "1970-01-01T00:00:00" ] ;
|
||||
abc:given_name "Henryöäß" ;
|
||||
abc:schema_type "abc:Person" .
|
||||
"""
|
||||
|
||||
ttl_result_record_b = """@prefix abc: <http://example.org/person-schema/abc/> .
|
||||
@prefix oxo: <http://purl.obolibrary.org/obo/> .
|
||||
@prefix xyz: <http://example.org/person-schema/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: <http://example.org/person-schema/abc/> .
|
||||
@prefix oxo: <http://purl.obolibrary.org/obo/> .
|
||||
@prefix xyz: <http://example.org/person-schema/xyz/> .
|
||||
|
||||
xyz:HenryAdams a abc:Person ;
|
||||
abc:annotations [ a abc:Annotation ;
|
||||
abc:annotation_tag <http://semanticscience.org/resource/SIO_001083> ;
|
||||
|
|
@ -35,7 +59,7 @@ xyz:HenryAdams a abc:Person ;
|
|||
abc:schema_type "abc:Person" .
|
||||
"""
|
||||
|
||||
ttl_result_record_b = """@prefix abc: <http://example.org/person-schema/abc/> .
|
||||
ttl_result_record_with_annotations_b = """@prefix abc: <http://example.org/person-schema/abc/> .
|
||||
@prefix oxo: <http://purl.obolibrary.org/obo/> .
|
||||
@prefix xyz: <http://example.org/person-schema/xyz/> .
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,23 @@ dlflatsocial:test_john_ttl a dlflatsocial:Person ;
|
|||
dlsocialmx:given_name "Johnöüß" .
|
||||
"""
|
||||
|
||||
ttl_output_record_a = """@prefix dlflat: <https://concepts.datalad.org/s/flat/unreleased/> .
|
||||
ttl_output_record_a = """@prefix dlflatsocial: <https://concepts.datalad.org/s/flat-social/unreleased/> .
|
||||
@prefix dlsocialmx: <https://concepts.datalad.org/s/social-mixin/unreleased/> .
|
||||
|
||||
dlflatsocial:another_john_ttl a dlflatsocial:Person ;
|
||||
dlsocialmx:given_name "Johnöüß" .
|
||||
"""
|
||||
|
||||
|
||||
ttl_output_record_b = """@prefix dlflatsocial: <https://concepts.datalad.org/s/flat-social/unreleased/> .
|
||||
@prefix dlsocialmx: <https://concepts.datalad.org/s/social-mixin/unreleased/> .
|
||||
|
||||
dlflatsocial:another_john_ttl a dlflatsocial:Person ;
|
||||
dlsocialmx:given_name "Johnöüß" .
|
||||
"""
|
||||
|
||||
|
||||
ttl_output_record_with_annotation_a = """@prefix dlflat: <https://concepts.datalad.org/s/flat/unreleased/> .
|
||||
@prefix dlflatsocial: <https://concepts.datalad.org/s/flat-social/unreleased/> .
|
||||
@prefix dlsocialmx: <https://concepts.datalad.org/s/social-mixin/unreleased/> .
|
||||
@prefix dlthings: <https://concepts.datalad.org/s/things/v1/> .
|
||||
|
|
@ -52,7 +68,7 @@ dlflatsocial:another_john_ttl a dlflatsocial:Person ;
|
|||
"""
|
||||
|
||||
|
||||
ttl_output_record_b = """@prefix dlflat: <https://concepts.datalad.org/s/flat/unreleased/> .
|
||||
ttl_output_record_with_annotation_b = """@prefix dlflat: <https://concepts.datalad.org/s/flat/unreleased/> .
|
||||
@prefix dlflatsocial: <https://concepts.datalad.org/s/flat-social/unreleased/> .
|
||||
@prefix dlsocialmx: <https://concepts.datalad.org/s/social-mixin/unreleased/> .
|
||||
@prefix dlthings: <https://concepts.datalad.org/s/things/v1/> .
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue