Skip to content

Issue #1181 doi configuration per community#1370

Open
kuchtiak-ufal wants to merge 18 commits into
clarin-v7from
issue_#1181_doi_configuration_per_community
Open

Issue #1181 doi configuration per community#1370
kuchtiak-ufal wants to merge 18 commits into
clarin-v7from
issue_#1181_doi_configuration_per_community

Conversation

@kuchtiak-ufal

Copy link
Copy Markdown

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for configuring multiple DOI providers scoped to specific communities (Issue #1181), enabling different DOI prefixes/credentials per community while delegating DOI operations to the appropriate provider based on an Item’s community.

Changes:

  • Introduces ClarinDOIIdentifierProvider (dispatcher) and ClarinCommunityDOIIdentifierProvider (community-scoped provider) for community-specific DOI minting/registration.
  • Adds ClarinDataCiteConnector and extends DataCiteConnector to support overriding DOI prefix via setters (in addition to config).
  • Adds an integration test covering mint/register/reserve/update and online operations for community-scoped DOI providers, plus example Spring configuration (commented) for enabling it.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
dspace/config/spring/api/identifier-service.xml Adds commented example Spring wiring for community-specific DOI providers and a CLARIN-specific DataCite connector bean.
dspace-api/src/main/java/org/dspace/identifier/doi/DataCiteConnector.java Adds cached DOI prefix getter and uses it when preparing DataCite crosswalk parameters.
dspace-api/src/main/java/org/dspace/identifier/doi/ClarinDataCiteConnector.java New connector subclass exposing setters for username/password/prefix to support per-community configuration.
dspace-api/src/main/java/org/dspace/identifier/ClarinDOIIdentifierProvider.java New dispatcher DOI provider selecting a community-specific provider per item (and per DOI prefix for deleteOnline).
dspace-api/src/main/java/org/dspace/identifier/ClarinCommunityDOIIdentifierProvider.java New community-scoped provider that configures connector credentials/prefix based on the configured DOI prefix.
dspace-api/src/test/java/org/dspace/identifier/ClarinDOIIdentifierProviderIT.java New integration test validating delegation and DOI lifecycle behavior across multiple community-specific providers.

Comment thread dspace-api/src/test/java/org/dspace/identifier/ClarinDOIIdentifierProviderIT.java Outdated
Comment thread dspace-api/src/test/java/org/dspace/identifier/ClarinDOIIdentifierProviderIT.java Outdated
Comment thread dspace-api/src/test/java/org/dspace/identifier/ClarinDOIIdentifierProviderIT.java Outdated
@kuchtiak-ufal kuchtiak-ufal linked an issue Jun 3, 2026 that may be closed by this pull request

@kosarko kosarko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kuchtiak-ufal I have a fix prepared for most of the changes. I'll ping you on the new PR


private boolean isCollectionInProvider(ClarinCommunityDOIIdentifierProvider provider,
Collection collection) throws SQLException {
List<Community> parentCommunities = collection.getCommunities();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collection.getCommunities() returns only the direct parent communities, not ancestors. If a provider is configured with a top-level community UUID/handle but the collection lives under a sub-community, no provider matches and the item silently gets no DOI (mint returns null with just an info log).

Either walk up the tree (e.g. communityService.getAllParents(...)) or document that only direct-parent communities are supported. The IT only covers the flat case, so it wouldn't catch this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only direct-parent is consistent with the behavior we have for handles. Keep it

assertTrue(doi4.startsWith("doi:10.1/1-"));
// check if the DOI for itemV2 is different from the one for item1
assertFalse(doi4.startsWith(doi1));
checkDoi(doi2, DOIIdentifierProvider.MINTED);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This re-checks doi2 — should this be checkDoi(doi4, DOIIdentifierProvider.MINTED)? As written, the persisted status of the freshly minted version DOI is never verified (testRegister below gets it right at line 200).


private Set<String> communities = new HashSet<>();

public void init() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushing username/password/prefix onto the connector at init() is safe with the example wiring only because each provider gets its own inner-bean connector. If someone later rewires the connector as a shared ref, all providers silently collapse onto the last-initialized credentials/prefix — DOIs minted against the wrong DataCite account, no error.

Since ClarinDataCiteConnector already has the setters, consider configuring username/password/prefix directly on each connector bean in Spring and dropping this push (and the identifier.doi.<prefix>.user/.password indirection) entirely. Smaller class, no hidden coupling.


private List<ClarinCommunityDOIIdentifierProvider> providers;

protected final SimpleCache<UUID, ClarinCommunityDOIIdentifierProvider> simpleCache = new SimpleCache<>(5);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest dropping SimpleCache altogether. The lookup it guards is a handful of in-memory set-contains checks, DOI operations aren't a hot path, and with capacity 5 it will barely ever hit during a DOIOrganiser batch over many items. It adds a hand-rolled LRU plus concurrency surface for negligible benefit.

(Also note getProviderForItem only caches the null result in the parent-collection branch, so unmatched items re-run the full lookup each call — moot if the cache goes away.)

return providers.stream()
.filter(provider -> provider.getPrefix().equals(prefix))
.findFirst()
.orElseThrow(() -> new DOIIdentifierException("No provider found for DOI prefix: " + prefix));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throw makes deleteOnline behave differently from every other delegation method, which log-and-skip when no provider matches. In a DOIOrganiser batch, a single legacy/foreign-prefix DOI will abort the whole run. Failing loud is defensible, but pick one behaviour deliberately and make it consistent.

}

protected String getDoiPrefix() {
if (this.doiPrefix == null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: doiPrefix is non-volatile, so two threads can race on this lazy init. The race is benign (both write the same config value), and ClarinDataCiteConnector sets the field at init so it never hits this path — just noting it's intentional-benign rather than overlooked.

@kosarko kosarko mentioned this pull request Jul 16, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[brainstorming] using dois in lindatrepo

4 participants