Skip to content

v2.11

Latest

Choose a tag to compare

@drorganvidez drorganvidez released this 28 Jul 18:23

[2.11] - 2026-07-28

Durable links for programmatic publication: a dataset published through
the API can now be followed across its whole life, from the first version
to the newest one, and can be handed over to another account.

Added

  • Zenodo concept DOI stored in ds_meta_data.dataset_concept_doi (new
    migration b8c9d0e1f2a3, nullable so older records keep working). It
    is read on first publish and on every new version, and every version of
    a lineage shares it. ZenodoService.get_concept_doi exposes it, with a
    fallback that derives it from conceptrecid when Zenodo omits
    conceptdoi.
  • GET /api/v1/datasets/doi/<doi> now also returns concept_doi,
    version, total_versions, is_latest, latest and versions_url,
    so a client holding an old DOI can discover the newest version. The
    existing keys are untouched.
  • GET /api/v1/datasets/<id>/versions, returning the whole version
    lineage oldest first, with dataset id, version, DOI and publication
    date for each entry.
  • GET /api/v1/datasets/concept-doi/<concept_doi>, which resolves a
    lineage (and therefore the latest version) from the stable concept DOI.
  • POST /api/v1/datasets/<id>/transfer, which offers a dataset to
    another account by user_id or email, restricted to the current
    owner. It returns 202 with a pending offer and moves nothing on its
    own. The receiving account answers through
    POST /api/v1/datasets/transfers/<id>/accept or /decline, the sender
    can withdraw it with /cancel, and both sides can list their offers
    with GET /api/v1/datasets/transfers. On acceptance the whole version
    lineage moves as a unit, files included. A dataset carries a permanent
    Zenodo DOI and appears in its owner's public listings, so it is never
    pushed onto an account that did not ask for it.
  • POST /api/v1/datasets/upload accepts optional authors (JSON array,
    or the usual authors[0][name] form fields), so a service account can
    publish while crediting the real developer. The authors are stored as
    Author rows and become the Zenodo creators. Without them the previous
    behaviour is unchanged. Names and affiliations are stripped of control
    and bidirectional characters, an orcid is only accepted when that
    ORCID has signed in to uvlhub at least once, and the publishing account
    is recorded in ds_meta_data.api_publisher_user_id (new migration
    c9d0e1f2a3b4) and published in the Zenodo record's notes, so credit
    given to somebody else stays traceable.
  • concept_doi in the publish and new-version API responses.

Fixed

  • Descriptions sent to POST /api/v1/datasets/upload are sanitized with
    the same allowlist as the web upload. The dataset page renders the
    description with safe, so the API path was an unsanitized HTML sink
    on a public DOI landing page.
  • Versioning a dataset that already has a successor is refused, so a
    lineage cannot branch, and the lineage walk covers every branch of rows
    that already did. A partial walk would hide published versions from
    /versions and move only part of a lineage on a transfer.
  • A version clone whose Zenodo publication fails is removed instead of
    staying in the lineage forever with no DOI, and it no longer inherits
    the concept DOI of its origin before Zenodo has assigned one. The
    removal stops at the point of no return. Publishing a deposition mints
    a permanent public DOI that nobody can withdraw, so the deposition id
    is now committed before that call and the local row is kept afterwards
    whatever else fails. Deleting it would have left a public Zenodo record
    holding the user's files with no local trace of which deposition it is,
    and no way to reconcile the two.
  • Persisting the concept DOI can no longer fail a publication that
    already succeeded, and a concept DOI too long for the column is
    dropped rather than breaking the write.
  • user_id on a transfer is parsed strictly, so a JSON true or 1.9
    is rejected instead of being read as account 1.
  • An orcid that is not a string in an API author payload is rejected
    with 400 instead of raising and returning 500.
  • API keys stop working when their account is deactivated. The key used
    to keep full write_dataset power indefinitely, which includes
    publishing permanent public records to Zenodo.
  • dataset_transfer_request.dataset_id cascades on delete, and the
    rosemary db:delete-dataset cleanup removes transfer offers. Deleting
    a dataset that had ever been offered failed with a foreign key error.
  • Migration c9d0e1f2a3b4 can be downgraded on MySQL and MariaDB. It
    dropped the indexes backing its foreign keys before the table, which
    those engines refuse, leaving the schema pinned at that revision.