diff --git a/doi/README.md b/doi/README.md
index 3491aeb..63cf2ee 100644
--- a/doi/README.md
+++ b/doi/README.md
@@ -21,20 +21,17 @@ See cadc-util
See cadc-registry .
### doi.properties
-The doi.properties configures the DataCite service used to register new DOI's.
+The doi.properties configures the DataCite service used to register new DOIs.
```
-# Vault Service resourceID
-ca.nrc.cadc.doi.vaultResourceID = {vault service resourceID}
-
-# Group Management Service (GMS) resourceID
-ca.nrc.cadc.doi.gmsResourceID = {GMS service resourceID}
-
-# Path in vault to the parent folder containing the DOI's.
-ca.nrc.cadc.doi.parentPath = {DOI parent folder path}
+# VOSpace uri to the parent DOI folder.
+ca.nrc.cadc.doi.vospaceParentUri = {parent folder URI}
# Prefix to the DOI metadata file
-ca.nrc.cadc.doi.metadataFilePrefix = {file prefix}
+ca.nrc.cadc.doi.metaDataPrefix = {metadata file prefix}
+
+# Prefix to the DOI GMS Group name
+ca.nrc.cadc.doi.groupPrefix = {group prefix}
# DOI landing page url
ca.nrc.cadc.doi.landingUrl = {landing page url}
@@ -42,42 +39,40 @@ ca.nrc.cadc.doi.landingUrl = {landing page url}
# DataCite MDS REST endpoint
ca.nrc.cadc.doi.datacite.mdsUrl = {MDS url}
-# DataCite account prefix
-ca.nrc.cadc.doi.datacite.accountPrefix = {account prefix}
-
# DataCite account username
ca.nrc.cadc.doi.datacite.username = {username}
# DataCite account password
ca.nrc.cadc.doi.datacite.password = {password}
-```
-
-_vaultResourceID_ the resourceID to the vault service used to store the DOI metadata and files.
-_gmsResourceID_ the resourceID to the GMS service used for authentication and authorization.
+# DataCite account prefix
+ca.nrc.cadc.doi.datacite.accountPrefix = {account prefix}
+```
-_parentPath_ is the path in the vault service to the DOI parent folder.
+_parentUri_ is the URI to the DOI parent folder in the VOSpace service.
-_metadataFilePrefix_ is the prefix prepended to the DOI name to create the filename for the DOI specific metadata stored in VOSpace.
+_metaDataPrefix_ is the prefix prepended to the DOI name used to create the file for the DOI specific metadata stored in VOSpace.
-_landingUrl_ is the base URL used to compose URLs to individual DOI's.
+_groupPrefix_ is the prefix prepended to the DOI name to create the group name for the DOI.
-_mdsUrl_ is the URL to the Datacite MDS rest endpoint used to create and update DOI's.
+_landingUrl_ is the base URL used to compose URLs to individual DOIs.
-_accountPrefix_ is the registered prefix for the DataCite account.
+_mdsUrl_ is the URL to the DataCite MDS rest endpoint used to create and update DOIs.
_username_ is the DataCite account username.
_password_ is the DataCite account password.
+_accountPrefix_ is the registered prefix for a DataCite account.
+
**For developer testing only:**
```
-# (optional) Create a random DOI name for testing
-ca.nrc.cadc.doi.test.randomName = {true|false}
-
-# (optional) Group URI for a group that has read/write permissions to test DOI's
-ca.nrc.cadc.doi.test.groupUri = {group URI}
+# (optional) Create a random DOI ID for testing
+ca.nrc.cadc.doi.randomTestID = {true|false}
```
+
+_randomID_ is a flag to create a random DOI ID for testing purposes, to avoid conflicts with existing DOIs in VOSpace or DOI groups in GMS.
+
### required certificates
The following certificates are required to run the service, and are expected to be in the `/config` directory.
@@ -113,10 +108,10 @@ Usage of this service can be divided into three distinct phases described below.
### DOI creation
1. user enters metadata of a publication using a GUI
- title
- - first auther
+ - first author
- additional authors
- journal reference
-2. user submits the metdata
+2. user submits the metadata
3. this service then:
- validates the metadata
- assigns a DOI for the metadata
diff --git a/doi/src/intTest/README.md b/doi/src/intTest/README.md
index 1979c1d..5169e2f 100644
--- a/doi/src/intTest/README.md
+++ b/doi/src/intTest/README.md
@@ -1,18 +1,21 @@
# doi service integration tests
-The integration tests run against a local doi instance defined by the `ivo://opencadc.org/doi` resourceID,
-and a vault service defined by the `ivo://opencadc.org/vault` resourceID'.
+The integration tests can run against a local `doi` service, and either a local or remote VOSpace service.
-Client test certificates in the `$A/test-certificates/` directory are used to authenticate to the doi service.
-The following certificates are expected.
-- `doiadmin.pem` owns and has full access to a test DOI.
-- `doi-auth.pem` has read-write access to a test DOI.
-- `doi-noauth.pem` has read-only access to a test DOI.
+## configuration
+A file called `intTest.properties` can be in the classpath (in `src/intTest/resources`) to override properties.
-The integration tests expect the following entries in `doi.properties`.
+### intTest.properties
+```
+doiResourceID={resourceID of the doi service}
+vospaceParentUri={VOSURI to the DOI parent folder in the VOSpace service}
+```
-`ca.nrc.cadc.doi.test.randomName = true` to create random DOI names for testing.
+**_vospaceParentUri_** must match `ca.nrc.cadc.doi.vospaceParentUri` configured in the doi service `doi.properties`.
-`ca.nrc.cadc.doi.test.groupUri = {group URI}` to specify the group URI that will have read/write permissions to a test DOI.
-The `doi-auth.pem` user is a member of this group, giving this user read/write access to a test DOI.
-The `doi-noauth.pem` user is not a member of this group, giving this user read-only access to a test DOI.
+### certificates
+Client test certificates in the `$A/test-certificates/` directory are used to authenticate to the doi service.
+The following certificates are expected.
+- `doi-admin.pem` owns and has full permissions to the test DOI.
+- `doi-auth.pem` is a member of a group that has read-write permissions to the test DOI.
+- `doi-noauth.pem` is not a member of any group that has permissions to the test DOI, resulting in read-only permissions to the test DOI.
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/CreateTest.java b/doi/src/intTest/java/ca/nrc/cadc/doi/CreateTest.java
deleted file mode 100644
index 7af2c1e..0000000
--- a/doi/src/intTest/java/ca/nrc/cadc/doi/CreateTest.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
-************************************************************************
-******************* CANADIAN ASTRONOMY DATA CENTRE *******************
-************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
-*
-* (c) 2024. (c) 2024.
-* Government of Canada Gouvernement du Canada
-* National Research Council Conseil national de recherches
-* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
-* All rights reserved Tous droits réservés
-*
-* NRC disclaims any warranties, Le CNRC dénie toute garantie
-* expressed, implied, or énoncée, implicite ou légale,
-* statutory, of any kind with de quelque nature que ce
-* respect to the software, soit, concernant le logiciel,
-* including without limitation y compris sans restriction
-* any warranty of merchantability toute garantie de valeur
-* or fitness for a particular marchande ou de pertinence
-* purpose. NRC shall not be pour un usage particulier.
-* liable in any event for any Le CNRC ne pourra en aucun cas
-* damages, whether direct or être tenu responsable de tout
-* indirect, special or general, dommage, direct ou indirect,
-* consequential or incidental, particulier ou général,
-* arising from the use of the accessoire ou fortuit, résultant
-* software. Neither the name de l'utilisation du logiciel. Ni
-* of the National Research le nom du Conseil National de
-* Council of Canada nor the Recherches du Canada ni les noms
-* names of its contributors may de ses participants ne peuvent
-* be used to endorse or promote être utilisés pour approuver ou
-* products derived from this promouvoir les produits dérivés
-* software without specific prior de ce logiciel sans autorisation
-* written permission. préalable et particulière
-* par écrit.
-*
-* This file is part of the Ce fichier fait partie du projet
-* OpenCADC project. OpenCADC.
-*
-* OpenCADC is free software: OpenCADC est un logiciel libre ;
-* you can redistribute it and/or vous pouvez le redistribuer ou le
-* modify it under the terms of modifier suivant les termes de
-* the GNU Affero General Public la “GNU Affero General Public
-* License as published by the License” telle que publiée
-* Free Software Foundation, par la Free Software Foundation
-* either version 3 of the : soit la version 3 de cette
-* License, or (at your option) licence, soit (à votre gré)
-* any later version. toute version ultérieure.
-*
-* OpenCADC is distributed in the OpenCADC est distribué
-* hope that it will be useful, dans l’espoir qu’il vous
-* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
-* without even the implied GARANTIE : sans même la garantie
-* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
-* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
-* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
-* General Public License for Générale Publique GNU Affero
-* more details. pour plus de détails.
-*
-* You should have received Vous devriez avoir reçu une
-* a copy of the GNU Affero copie de la Licence Générale
-* General Public License along Publique GNU Affero avec
-* with OpenCADC. If not, see OpenCADC ; si ce n’est
-* . pas le cas, consultez :
-* .
-*
-* $Revision: 5 $
-*
-************************************************************************
-*/
-
-package ca.nrc.cadc.doi;
-
-import ca.nrc.cadc.doi.datacite.Resource;
-import ca.nrc.cadc.doi.datacite.Title;
-import ca.nrc.cadc.doi.io.DoiXmlReader;
-import ca.nrc.cadc.doi.status.DoiStatus;
-import ca.nrc.cadc.doi.status.DoiStatusListXmlReader;
-import ca.nrc.cadc.doi.status.DoiStatusXmlReader;
-import ca.nrc.cadc.doi.status.Status;
-import ca.nrc.cadc.net.HttpGet;
-import ca.nrc.cadc.util.Log4jInit;
-import java.io.ByteArrayOutputStream;
-import java.io.StringReader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.ArrayList;
-import java.util.List;
-import javax.security.auth.Subject;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- */
-public class CreateTest extends IntTestBase {
- private static final Logger log = Logger.getLogger(CreateTest.class);
-
- static final String JSON = "application/json";
-
- static {
- Log4jInit.setLevel("ca.nrc.cadc.doi", Level.INFO);
- }
-
- @Test
- public void createDOIAndStatusTest() throws Exception {
- log.info("createDOIAndStatusTest");
- Subject.doAs(readWriteSubject, (PrivilegedExceptionAction) () -> {
- // create new doi
- Resource testResource = getTestResource(false, true, true);
- String testXML = getResourceXML(testResource);
-
- String doiSuffix = null;
- try {
- // check that the service processed the document and added an identifier
- String persistedXml = postDOI(doiServiceURL, testXML, TEST_JOURNAL_REF);
- DoiXmlReader reader = new DoiXmlReader();
- Resource persistedResource = reader.read(persistedXml);
-
- String testIdentifier = testResource.getIdentifier().getValue();
- String persistedIdentifier = persistedResource.getIdentifier().getValue();
- Assert.assertNotEquals("New identifier not received from doi service.",
- testIdentifier, persistedIdentifier);
- doiSuffix = getDOISuffix(persistedIdentifier);
-
- // Get the DOI in JSON format
- URL doiURL = new URL(String.format("%s/%s", doiServiceURL, doiSuffix));
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- HttpGet get = new HttpGet(doiURL, bos);
- get.setRequestProperty("Accept", JSON);
- get.prepare();
- Assert.assertNull("GET exception", get.getThrowable());
- Assert.assertEquals(JSON, get.getContentType());
-
- // Get the DOI status
- URL statusURL = new URL(String.format("%s/%s", doiURL, DoiAction.STATUS_ACTION));
- log.debug("statusURL: " + statusURL);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- HttpGet getStatus = new HttpGet(statusURL, baos);
- getStatus.run();
- Assert.assertNull("GET exception", get.getThrowable());
- String status = baos.toString(StandardCharsets.UTF_8);
- log.debug("status: " + status);
-
- DoiStatusXmlReader statusReader = new DoiStatusXmlReader();
- DoiStatus doiStatus = statusReader.read(new StringReader(status));
-
- Assert.assertEquals("identifier mismatch",
- persistedIdentifier, doiStatus.getIdentifier().getValue());
- String expectedDataDirectory = String.format("%s/%s/data", TestUtil.DOI_PARENT_PATH, doiSuffix);
- Assert.assertEquals("dataDirectory mismatch",
- expectedDataDirectory, doiStatus.getDataDirectory());
- Title expectedTitle = testResource.getTitles().get(0);
- Assert.assertEquals("title mismatch",
- expectedTitle.getValue(), doiStatus.getTitle().getValue());
- Assert.assertEquals("status mismatch",
- Status.DRAFT, doiStatus.getStatus());
- Assert.assertEquals("journalRef mismatch",
- TEST_JOURNAL_REF, doiStatus.journalRef);
- } finally {
- if (doiSuffix != null) {
- cleanup(doiSuffix);
- }
- }
- return null;
- });
- }
-
- @Test
- public void testGetStatusList() {
- log.info("testGetStatusList");
- List testDOIList = new ArrayList<>();
- try {
- // create test DOI's
- testDOIList.add(createDOI(readWriteSubject));
- testDOIList.add(createDOI(readWriteSubject));
- testDOIList.add(createDOI(readWriteSubject));
-
- // invoke the doi list service
- List doiStatusList = getDoiStatusList(readWriteSubject);
- Assert.assertEquals("test DOI list and status DOI list mismatch",
- testDOIList.size(), doiStatusList.size());
-
- for (String doiSuffix : testDOIList) {
- boolean found = false;
- for (DoiStatus doiStatus : doiStatusList) {
- log.debug("actual doi: " + doiStatus.getIdentifier().getValue());
- if (doiStatus.getIdentifier().getValue().endsWith(doiSuffix)) {
- log.debug("doisuffix match");
- found = true;
- break;
- }
- }
- if (!found) {
- Assert.fail(String.format("doiSuffix %s not found in DOI status list", doiSuffix));
- }
- }
- } catch (Exception e) {
- log.error("unexpected exception", e);
- Assert.fail("unexpected exception: " + e.getMessage());
- } finally {
- for (String doiSuffix : testDOIList) {
- cleanup(doiSuffix);
- }
- }
- }
-
- private List getDoiStatusList(Subject testSubject)
- throws PrivilegedActionException {
- return Subject.doAs(testSubject, (PrivilegedExceptionAction>) () -> {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- HttpGet get = new HttpGet(doiServiceURL, bos);
- get.setRequestProperty("Accept", "text/xml");
- get.run();
- DoiStatusListXmlReader reader = new DoiStatusListXmlReader();
- return reader.read(new StringReader(bos.toString(StandardCharsets.UTF_8)));
- });
- }
-
-}
\ No newline at end of file
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/InitFolderTest.java b/doi/src/intTest/java/ca/nrc/cadc/doi/InitFolderTest.java
deleted file mode 100644
index 84c2caf..0000000
--- a/doi/src/intTest/java/ca/nrc/cadc/doi/InitFolderTest.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
-************************************************************************
-******************* CANADIAN ASTRONOMY DATA CENTRE *******************
-************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
-*
-* (c) 2024. (c) 2024.
-* Government of Canada Gouvernement du Canada
-* National Research Council Conseil national de recherches
-* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
-* All rights reserved Tous droits réservés
-*
-* NRC disclaims any warranties, Le CNRC dénie toute garantie
-* expressed, implied, or énoncée, implicite ou légale,
-* statutory, of any kind with de quelque nature que ce
-* respect to the software, soit, concernant le logiciel,
-* including without limitation y compris sans restriction
-* any warranty of merchantability toute garantie de valeur
-* or fitness for a particular marchande ou de pertinence
-* purpose. NRC shall not be pour un usage particulier.
-* liable in any event for any Le CNRC ne pourra en aucun cas
-* damages, whether direct or être tenu responsable de tout
-* indirect, special or general, dommage, direct ou indirect,
-* consequential or incidental, particulier ou général,
-* arising from the use of the accessoire ou fortuit, résultant
-* software. Neither the name de l'utilisation du logiciel. Ni
-* of the National Research le nom du Conseil National de
-* Council of Canada nor the Recherches du Canada ni les noms
-* names of its contributors may de ses participants ne peuvent
-* be used to endorse or promote être utilisés pour approuver ou
-* products derived from this promouvoir les produits dérivés
-* software without specific prior de ce logiciel sans autorisation
-* written permission. préalable et particulière
-* par écrit.
-*
-* This file is part of the Ce fichier fait partie du projet
-* OpenCADC project. OpenCADC.
-*
-* OpenCADC is free software: OpenCADC est un logiciel libre ;
-* you can redistribute it and/or vous pouvez le redistribuer ou le
-* modify it under the terms of modifier suivant les termes de
-* the GNU Affero General Public la “GNU Affero General Public
-* License as published by the License” telle que publiée
-* Free Software Foundation, par la Free Software Foundation
-* either version 3 of the : soit la version 3 de cette
-* License, or (at your option) licence, soit (à votre gré)
-* any later version. toute version ultérieure.
-*
-* OpenCADC is distributed in the OpenCADC est distribué
-* hope that it will be useful, dans l’espoir qu’il vous
-* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
-* without even the implied GARANTIE : sans même la garantie
-* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
-* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
-* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
-* General Public License for Générale Publique GNU Affero
-* more details. pour plus de détails.
-*
-* You should have received Vous devriez avoir reçu une
-* a copy of the GNU Affero copie de la Licence Générale
-* General Public License along Publique GNU Affero avec
-* with OpenCADC. If not, see OpenCADC ; si ce n’est
-* . pas le cas, consultez :
-* .
-*
-* $Revision: 5 $
-*
-************************************************************************
-*/
-
-package ca.nrc.cadc.doi;
-
-import ca.nrc.cadc.doi.datacite.Resource;
-import ca.nrc.cadc.net.FileContent;
-import ca.nrc.cadc.net.HttpPost;
-import ca.nrc.cadc.util.Log4jInit;
-import java.io.File;
-import java.nio.charset.StandardCharsets;
-import java.security.AccessControlException;
-import java.security.PrivilegedExceptionAction;
-import java.util.HashMap;
-import java.util.Map;
-import javax.security.auth.Subject;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.junit.Assert;
-import org.junit.Test;
-import org.opencadc.vospace.DataNode;
-import org.opencadc.vospace.VOS;
-import org.opencadc.vospace.VOSURI;
-import org.opencadc.vospace.client.ClientTransfer;
-import org.opencadc.vospace.transfer.Direction;
-import org.opencadc.vospace.transfer.Protocol;
-import org.opencadc.vospace.transfer.Transfer;
-
-/**
- */
-public class InitFolderTest extends IntTestBase {
- private static final Logger log = Logger.getLogger(InitFolderTest.class);
-
- static {
- Log4jInit.setLevel("ca.nrc.cadc.doi", Level.INFO);
- }
-
- /**
- * new folder is created - calling user should have read access but not write
- * new XML file is created - calling user should have read access
- * data folder created - calling user should have write access
- */
- @Test
- public void testInitDoi() throws Exception {
- log.info("testInitDoi");
- Resource testResource = getTestResource(false, true, true);
- final String testXML = getResourceXML(testResource);
-
- // Create the folder for the test, and the initial XML file
- Subject.doAs(readWriteSubject, (PrivilegedExceptionAction) () -> {
- String doiSuffix = null;
- try {
- FileContent fileContent = new FileContent(testXML, "text/xml", StandardCharsets.UTF_8);
- Map params = new HashMap<>();
- params.put("doiMetadata", fileContent);
- params.put("journalref", TEST_JOURNAL_REF);
- HttpPost post = new HttpPost(doiServiceURL, params, false);
- post.run();
-
- Assert.assertNull("POST exception", post.getThrowable());
- Assert.assertEquals("expected 303 response code", 303, post.getResponseCode());
- String doiPath = post.getRedirectURL().getPath();
- log.debug("redirectURL path: " + doiPath);
-
- // Folder name should be /AstroDataCitationDOI/CISTI.CANFAR/
- String[] doiNumberParts = doiPath.split("/");
- doiSuffix = doiNumberParts[3];
-
- String dataNodeName = String.format("%s/data", doiSuffix);
- log.debug("write to data folder " + dataNodeName);
-
- // Test writing to the data directory
- String fileName = "doi-test-write-file.txt";
- String dataFileToWrite = dataNodeName + "/" + fileName;
-
- VOSURI target = getVOSURI(dataFileToWrite);
- DataNode dataNode = new DataNode(fileName);
- log.debug("PUT target:" + target.getURI());
- vosClient.createNode(target, dataNode);
-
- Transfer transfer = new Transfer(target.getURI(), Direction.pushToVoSpace);
- Protocol put = new Protocol(VOS.PROTOCOL_HTTPS_PUT);
- transfer.getProtocols().add(put);
-
- log.debug("file to be written: " + dataFileToWrite);
- ClientTransfer clientTransfer = vosClient.createTransfer(transfer);
- File testFile = new File("src/intTest/resources/" + fileName);
- clientTransfer.setFile(testFile);
- clientTransfer.run();
-
- // Check that file is there.
- String newFilePath = target.getPath();
- vosClient.getNode(newFilePath);
-
- // Test that read only subject CAN'T write to the same folder
- String writeName = "doi-test-write-failed.txt";
- final String writeFile = dataNodeName + "/" + writeName;
-
- // Try to write to data directory as read only subject
- Subject.doAs(readOnlySubject, (PrivilegedExceptionAction) () -> {
- log.debug("write as read only subject");
- try {
- VOSURI target1 = getVOSURI(writeFile);
- DataNode dataNode1 = new DataNode(writeName);
- vosClient.createNode(target1, dataNode1);
- } catch (AccessControlException e) {
- log.debug("expected exception: " + e.getMessage());
- } catch (Exception e) {
- Assert.fail("exception writing file: " + e.getMessage());
- }
- return null;
- });
- return null;
- } finally {
- if (doiSuffix != null) {
- cleanup(doiSuffix);
- }
- }
- });
- }
-
-}
\ No newline at end of file
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/IntTestBase.java b/doi/src/intTest/java/ca/nrc/cadc/doi/IntTestBase.java
index a86ae9c..8cf8c22 100644
--- a/doi/src/intTest/java/ca/nrc/cadc/doi/IntTestBase.java
+++ b/doi/src/intTest/java/ca/nrc/cadc/doi/IntTestBase.java
@@ -69,11 +69,9 @@
package ca.nrc.cadc.doi;
-import ca.nrc.cadc.ac.client.GMSClient;
import ca.nrc.cadc.auth.AuthMethod;
import ca.nrc.cadc.auth.SSLUtil;
import ca.nrc.cadc.doi.datacite.Resource;
-import ca.nrc.cadc.doi.io.DoiXmlReader;
import ca.nrc.cadc.doi.io.DoiXmlWriter;
import ca.nrc.cadc.net.FileContent;
import ca.nrc.cadc.net.HttpPost;
@@ -96,6 +94,7 @@
import org.junit.Assert;
import org.junit.BeforeClass;
import org.opencadc.vospace.ContainerNode;
+import org.opencadc.vospace.DataNode;
import org.opencadc.vospace.VOSURI;
import org.opencadc.vospace.client.VOSpaceClient;
import org.opencadc.vospace.client.async.RecursiveDeleteNode;
@@ -108,6 +107,8 @@
public abstract class IntTestBase extends TestBase {
private static final Logger log = Logger.getLogger(IntTestBase.class);
+ static final String JSON = "application/json";
+ static final String XML = "text/xml";
static final String TEST_JOURNAL_REF = "2018, Test Journal ref. ApJ 1000,100";
static Subject adminSubject;
@@ -129,12 +130,12 @@ public static void staticInit() {
RegistryClient regClient = new RegistryClient();
doiServiceURL = regClient.getServiceURL(TestUtil.DOI_RESOURCE_ID, Standards.DOI_INSTANCES_10, AuthMethod.CERT);
- doiParentPathURI = new VOSURI(TestUtil.VAULT_RESOURCE_ID, TestUtil.DOI_PARENT_PATH);
- vosClient = new VOSpaceClient(TestUtil.VAULT_RESOURCE_ID);
+ doiParentPathURI = new VOSURI(TestUtil.VOSPACE_RESOURCE_ID, TestUtil.DOI_PARENT_PATH);
+ vosClient = new VOSpaceClient(TestUtil.VOSPACE_RESOURCE_ID);
}
protected VOSURI getVOSURI(String path) {
- return new VOSURI(TestUtil.VAULT_RESOURCE_ID, String.format("%s/%s", TestUtil.DOI_PARENT_PATH, path));
+ return new VOSURI(TestUtil.VOSPACE_RESOURCE_ID, String.format("%s/%s", TestUtil.DOI_PARENT_PATH, path));
}
protected String getDOISuffix(String doiIdentifier) {
@@ -152,6 +153,18 @@ protected String getResourceXML(Resource resource) throws IOException {
return sb.toString();
}
+ protected ContainerNode createContainerNode(String path, String name) throws Exception {
+ ContainerNode node = new ContainerNode(name);
+ VOSURI nodeURI = getVOSURI(path);
+ return (ContainerNode) vosClient.createNode(nodeURI, node);
+ }
+
+ protected DataNode createDataNode(String path, String name) throws Exception {
+ DataNode node = new DataNode(name);
+ VOSURI nodeURI = getVOSURI(path);
+ return (DataNode) vosClient.createNode(nodeURI, node);
+ }
+
protected ContainerNode getContainerNode(String path) throws Exception {
String nodePath = doiParentPathURI.getPath();
if (StringUtil.hasText(path)) {
@@ -160,23 +173,11 @@ protected ContainerNode getContainerNode(String path) throws Exception {
return (ContainerNode) vosClient.getNode(nodePath);
}
- protected String createDOI(Subject testSubject)
- throws IOException, PrivilegedActionException {
- Resource resource = getTestResource(false, false, true);
- final String doiXML = getResourceXML(resource);
- return (String) Subject.doAs(testSubject, (PrivilegedExceptionAction) () -> {
- String persistedXML = postDOI(doiServiceURL, doiXML, TEST_JOURNAL_REF);
- DoiXmlReader reader = new DoiXmlReader();
- Resource persistedResource = reader.read(persistedXML);
- return getDOISuffix(persistedResource.getIdentifier().getValue());
- });
- }
-
protected String postDOI(URL postUrl, String doiXML, String journalRef)
throws Exception {
Map params = new HashMap<>();
if (StringUtil.hasText(doiXML)) {
- FileContent fileContent = new FileContent(doiXML, "text/xml", StandardCharsets.UTF_8);
+ FileContent fileContent = new FileContent(doiXML, XML, StandardCharsets.UTF_8);
params.put("doiMetadata", fileContent);
}
if (journalRef != null) {
@@ -187,15 +188,15 @@ protected String postDOI(URL postUrl, String doiXML, String journalRef)
post.prepare();
Assert.assertNull("POST exception", post.getThrowable());
- Assert.assertEquals("non 200 response code", post.getResponseCode(), 200);
+ Assert.assertEquals("non 200 response code", 200, post.getResponseCode());
return new String(post.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
}
protected void cleanup(String doiSuffix) {
- // delete doi as doiadmin
+ // delete doi as admin
try {
Subject.doAs(adminSubject, (PrivilegedExceptionAction) () -> {
- log.debug("cleanup as doiadmin");
+ log.debug("cleanup as doi admin");
try {
VOSURI nodeUri = getVOSURI(doiSuffix);
log.debug("recursiveDeleteNode: " + nodeUri);
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/LandingPageTest.java b/doi/src/intTest/java/ca/nrc/cadc/doi/LandingPageTest.java
deleted file mode 100644
index eb35140..0000000
--- a/doi/src/intTest/java/ca/nrc/cadc/doi/LandingPageTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- ************************************************************************
- ******************* CANADIAN ASTRONOMY DATA CENTRE *******************
- ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
- *
- * (c) 2024. (c) 2024.
- * Government of Canada Gouvernement du Canada
- * National Research Council Conseil national de recherches
- * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
- * All rights reserved Tous droits réservés
- *
- * NRC disclaims any warranties, Le CNRC dénie toute garantie
- * expressed, implied, or énoncée, implicite ou légale,
- * statutory, of any kind with de quelque nature que ce
- * respect to the software, soit, concernant le logiciel,
- * including without limitation y compris sans restriction
- * any warranty of merchantability toute garantie de valeur
- * or fitness for a particular marchande ou de pertinence
- * purpose. NRC shall not be pour un usage particulier.
- * liable in any event for any Le CNRC ne pourra en aucun cas
- * damages, whether direct or être tenu responsable de tout
- * indirect, special or general, dommage, direct ou indirect,
- * consequential or incidental, particulier ou général,
- * arising from the use of the accessoire ou fortuit, résultant
- * software. Neither the name de l'utilisation du logiciel. Ni
- * of the National Research le nom du Conseil National de
- * Council of Canada nor the Recherches du Canada ni les noms
- * names of its contributors may de ses participants ne peuvent
- * be used to endorse or promote être utilisés pour approuver ou
- * products derived from this promouvoir les produits dérivés
- * software without specific prior de ce logiciel sans autorisation
- * written permission. préalable et particulière
- * par écrit.
- *
- * This file is part of the Ce fichier fait partie du projet
- * OpenCADC project. OpenCADC.
- *
- * OpenCADC is free software: OpenCADC est un logiciel libre ;
- * you can redistribute it and/or vous pouvez le redistribuer ou le
- * modify it under the terms of modifier suivant les termes de
- * the GNU Affero General Public la “GNU Affero General Public
- * License as published by the License” telle que publiée
- * Free Software Foundation, par la Free Software Foundation
- * either version 3 of the : soit la version 3 de cette
- * License, or (at your option) licence, soit (à votre gré)
- * any later version. toute version ultérieure.
- *
- * OpenCADC is distributed in the OpenCADC est distribué
- * hope that it will be useful, dans l’espoir qu’il vous
- * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
- * without even the implied GARANTIE : sans même la garantie
- * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
- * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
- * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
- * General Public License for Générale Publique GNU Affero
- * more details. pour plus de détails.
- *
- * You should have received Vous devriez avoir reçu une
- * a copy of the GNU Affero copie de la Licence Générale
- * General Public License along Publique GNU Affero avec
- * with OpenCADC. If not, see OpenCADC ; si ce n’est
- * . pas le cas, consultez :
- * .
- *
- * : 5 $
- *
- ************************************************************************
- */
-
-package ca.nrc.cadc.doi;
-
-import ca.nrc.cadc.auth.AuthMethod;
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.net.HttpGet;
-import ca.nrc.cadc.reg.Standards;
-import ca.nrc.cadc.reg.client.RegistryClient;
-import ca.nrc.cadc.util.Log4jInit;
-import java.net.URL;
-import java.security.PrivilegedExceptionAction;
-import javax.security.auth.Subject;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class LandingPageTest extends IntTestBase {
- private static final Logger log = Logger.getLogger(LandingPageTest.class);
-
- static {
- Log4jInit.setLevel("ca.nrc.cadc.doi", Level.INFO);
- }
-
- private static final String TEST_DOI = "13.0001";
- private static URL prodDoiServiceURL;
-
- public LandingPageTest() {
- }
-
- @BeforeClass
- public static void setUpClass() throws Exception {
- RegistryClient regClient = new RegistryClient();
- prodDoiServiceURL = regClient.getServiceURL(TestUtil.PROD_DOI_RESOURCE_ID, Standards.DOI_INSTANCES_10, AuthMethod.CERT);
- }
-
- @Test
- public void anonGetTest() {
- log.info("anonGetTest");
- try {
- URL doiURL = new URL(prodDoiServiceURL.toExternalForm() + "/" + TEST_DOI + "/status/public");
- log.debug("test url: " + doiURL.toExternalForm());
- Subject testSubject = AuthenticationUtil.getAnonSubject();
-
- Subject.doAs(testSubject, (PrivilegedExceptionAction) () -> {
- HttpGet get = new HttpGet(doiURL, true);
- get.prepare();
-
- Assert.assertEquals(200, get.getResponseCode());
- return null;
- });
-
- } catch (Exception e) {
- log.error("Unexpected error", e);
- Assert.fail("Unexpected error: " + e);
- }
- }
-
- @Test
- public void authGetTest() {
- log.info("authGetTest");
- try {
- log.debug("doi instances url: " + doiServiceURL);
- URL doiURL = new URL(prodDoiServiceURL.toExternalForm() + "/" + TEST_DOI + "/status/public");
- log.debug("test url: " + doiURL.toExternalForm());
-
- Subject.doAs(adminSubject, (PrivilegedExceptionAction) () -> {
- HttpGet get = new HttpGet(doiURL, true);
- get.prepare();
-
- Assert.assertEquals(200, get.getResponseCode());
- return null;
- });
-
- } catch (Exception e) {
- log.error("Unexpected error", e);
- Assert.fail("Unexpected error: " + e);
- }
- }
-
-}
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/LifecycleTest.java b/doi/src/intTest/java/ca/nrc/cadc/doi/LifecycleTest.java
new file mode 100644
index 0000000..1fb4ac2
--- /dev/null
+++ b/doi/src/intTest/java/ca/nrc/cadc/doi/LifecycleTest.java
@@ -0,0 +1,472 @@
+/*
+ ************************************************************************
+ ******************* CANADIAN ASTRONOMY DATA CENTRE *******************
+ ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
+ *
+ * (c) 2024. (c) 2024.
+ * Government of Canada Gouvernement du Canada
+ * National Research Council Conseil national de recherches
+ * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
+ * All rights reserved Tous droits réservés
+ *
+ * NRC disclaims any warranties, Le CNRC dénie toute garantie
+ * expressed, implied, or énoncée, implicite ou légale,
+ * statutory, of any kind with de quelque nature que ce
+ * respect to the software, soit, concernant le logiciel,
+ * including without limitation y compris sans restriction
+ * any warranty of merchantability toute garantie de valeur
+ * or fitness for a particular marchande ou de pertinence
+ * purpose. NRC shall not be pour un usage particulier.
+ * liable in any event for any Le CNRC ne pourra en aucun cas
+ * damages, whether direct or être tenu responsable de tout
+ * indirect, special or general, dommage, direct ou indirect,
+ * consequential or incidental, particulier ou général,
+ * arising from the use of the accessoire ou fortuit, résultant
+ * software. Neither the name de l'utilisation du logiciel. Ni
+ * of the National Research le nom du Conseil National de
+ * Council of Canada nor the Recherches du Canada ni les noms
+ * names of its contributors may de ses participants ne peuvent
+ * be used to endorse or promote être utilisés pour approuver ou
+ * products derived from this promouvoir les produits dérivés
+ * software without specific prior de ce logiciel sans autorisation
+ * written permission. préalable et particulière
+ * par écrit.
+ *
+ * This file is part of the Ce fichier fait partie du projet
+ * OpenCADC project. OpenCADC.
+ *
+ * OpenCADC is free software: OpenCADC est un logiciel libre ;
+ * you can redistribute it and/or vous pouvez le redistribuer ou le
+ * modify it under the terms of modifier suivant les termes de
+ * the GNU Affero General Public la “GNU Affero General Public
+ * License as published by the License” telle que publiée
+ * Free Software Foundation, par la Free Software Foundation
+ * either version 3 of the : soit la version 3 de cette
+ * License, or (at your option) licence, soit (à votre gré)
+ * any later version. toute version ultérieure.
+ *
+ * OpenCADC is distributed in the OpenCADC est distribué
+ * hope that it will be useful, dans l’espoir qu’il vous
+ * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
+ * without even the implied GARANTIE : sans même la garantie
+ * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
+ * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
+ * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
+ * General Public License for Générale Publique GNU Affero
+ * more details. pour plus de détails.
+ *
+ * You should have received Vous devriez avoir reçu une
+ * a copy of the GNU Affero copie de la Licence Générale
+ * General Public License along Publique GNU Affero avec
+ * with OpenCADC. If not, see OpenCADC ; si ce n’est
+ * . pas le cas, consultez :
+ * .
+ *
+ * : 5 $
+ *
+ ************************************************************************
+ */
+
+package ca.nrc.cadc.doi;
+
+import ca.nrc.cadc.doi.datacite.Creator;
+import ca.nrc.cadc.doi.datacite.Date;
+import ca.nrc.cadc.doi.datacite.DateType;
+import ca.nrc.cadc.doi.datacite.Language;
+import ca.nrc.cadc.doi.datacite.Resource;
+import ca.nrc.cadc.doi.datacite.Title;
+import ca.nrc.cadc.doi.datacite.TitleType;
+import ca.nrc.cadc.doi.io.DoiXmlReader;
+import ca.nrc.cadc.doi.status.DoiStatus;
+import ca.nrc.cadc.doi.status.DoiStatusXmlReader;
+import ca.nrc.cadc.doi.status.Status;
+import ca.nrc.cadc.net.FileContent;
+import ca.nrc.cadc.net.HttpGet;
+import ca.nrc.cadc.net.HttpPost;
+import ca.nrc.cadc.util.Log4jInit;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.StringReader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.AccessControlException;
+import java.security.PrivilegedExceptionAction;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.security.auth.Subject;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opencadc.vospace.ContainerNode;
+import org.opencadc.vospace.DataNode;
+import org.opencadc.vospace.VOS;
+import org.opencadc.vospace.VOSURI;
+import org.opencadc.vospace.client.ClientTransfer;
+import org.opencadc.vospace.transfer.Direction;
+import org.opencadc.vospace.transfer.Protocol;
+import org.opencadc.vospace.transfer.Transfer;
+
+public class LifecycleTest extends IntTestBase {
+
+ private static final Logger log = Logger.getLogger(LifecycleTest.class);
+ static {
+ Log4jInit.setLevel("ca.nrc.cadc.doi", Level.INFO);
+ }
+
+ @Test
+ public void testLifecycle() throws Exception {
+ log.debug("testLifecycle()");
+
+ // Create a new DOI
+ Resource expected = getTestResource(true, true, true);
+
+ String doiSuffix = Subject.doAs(readWriteSubject, (PrivilegedExceptionAction) () -> {
+
+ // create a new DOI
+ Resource actual = create(expected);
+ String doiID = getDOISuffix(actual.getIdentifier().getValue());
+
+ // update the DOI
+ update(actual, doiID);
+
+ // publish the DOI
+ publish(actual, doiID);
+
+ return doiID;
+ });
+ }
+
+ Resource create(Resource expected) throws Exception {
+
+ // Create the folder for the test, and the initial XML file
+ String doiXML = getResourceXML(expected);
+ FileContent fileContent = new FileContent(doiXML, XML, StandardCharsets.UTF_8);
+ Map params = new HashMap<>();
+ params.put("doiMetadata", fileContent);
+ params.put("journalref", TEST_JOURNAL_REF);
+ HttpPost post = new HttpPost(doiServiceURL, params, false);
+ post.run();
+
+ Assert.assertNull("POST exception", post.getThrowable());
+ Assert.assertEquals("expected 303 response code", 303, post.getResponseCode());
+ String doiPath = post.getRedirectURL().getPath();
+ log.debug("redirectURL path: " + doiPath);
+
+ // get the doi suffix
+ int index = doiPath.lastIndexOf("/");
+ String pathDoiSuffix = doiPath.substring(index + 1);
+
+ // Get the new DOI in XML format
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ HttpGet get = new HttpGet(post.getRedirectURL(), bos);
+ get.setRequestProperty("Accept", XML);
+ get.run();
+ Assert.assertNull("GET exception", get.getThrowable());
+ Assert.assertEquals("expected 200 response code", 200, get.getResponseCode());
+
+ // actual resource
+ DoiXmlReader reader = new DoiXmlReader();
+ Resource actual = reader.read(bos.toString(StandardCharsets.UTF_8));
+
+ String expectedIdentifier = expected.getIdentifier().getValue();
+ String actualIdentifier = actual.getIdentifier().getValue();
+ Assert.assertNotEquals("New identifier not received from doi service.",
+ expectedIdentifier, actualIdentifier);
+ compareResource(expected, actual, false);
+
+ String doiSuffix = getDOISuffix(actualIdentifier);
+ Assert.assertEquals("DOI suffix", pathDoiSuffix, doiSuffix);
+
+ // Get the DOI in JSON format
+ URL doiURL = new URL(String.format("%s/%s", doiServiceURL, doiSuffix));
+ bos = new ByteArrayOutputStream();
+ get = new HttpGet(doiURL, bos);
+ get.setRequestProperty("Accept", JSON);
+ get.prepare();
+ Assert.assertNull("GET exception", get.getThrowable());
+ Assert.assertEquals(JSON, get.getContentType());
+
+ // Get the DOI status
+ URL statusURL = new URL(String.format("%s/%s", doiURL, DoiAction.STATUS_ACTION));
+ log.debug("statusURL: " + statusURL);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ HttpGet getStatus = new HttpGet(statusURL, baos);
+ getStatus.run();
+ Assert.assertNull("GET exception", get.getThrowable());
+ String status = baos.toString(StandardCharsets.UTF_8);
+ log.debug("status: " + status);
+
+ DoiStatusXmlReader statusReader = new DoiStatusXmlReader();
+ DoiStatus doiStatus = statusReader.read(new StringReader(status));
+
+ Assert.assertEquals("identifier mismatch", actualIdentifier, doiStatus.getIdentifier().getValue());
+ String expectedDataDirectory = String.format("%s/%s/data", TestUtil.DOI_PARENT_PATH, doiSuffix);
+ Assert.assertEquals("dataDirectory mismatch", expectedDataDirectory, doiStatus.getDataDirectory());
+ Title expectedTitle = expected.getTitles().get(0);
+ Assert.assertEquals("title mismatch", expectedTitle.getValue(), doiStatus.getTitle().getValue());
+ Assert.assertEquals("status mismatch", Status.DRAFT, doiStatus.getStatus());
+ Assert.assertEquals("journalRef mismatch", TEST_JOURNAL_REF, doiStatus.journalRef);
+
+ // Test writing to the data directory
+ String dataNodeName = String.format("%s/data", doiSuffix);
+ log.debug("write to data folder " + dataNodeName);
+ String fileName = "doi-test-write-file.txt";
+ String dataFileToWrite = dataNodeName + "/" + fileName;
+
+ VOSURI target = getVOSURI(dataFileToWrite);
+ DataNode dataNode = new DataNode(fileName);
+ log.debug("PUT target:" + target.getURI());
+ vosClient.createNode(target, dataNode);
+
+ Transfer transfer = new Transfer(target.getURI(), Direction.pushToVoSpace);
+ Protocol put = new Protocol(VOS.PROTOCOL_HTTPS_PUT);
+ transfer.getProtocols().add(put);
+
+ log.debug("file to be written: " + dataFileToWrite);
+ ClientTransfer clientTransfer = vosClient.createTransfer(transfer);
+ File testFile = new File("src/intTest/resources/" + fileName);
+ clientTransfer.setFile(testFile);
+ clientTransfer.run();
+
+ // Check that file is there.
+ String newFilePath = target.getPath();
+ vosClient.getNode(newFilePath);
+
+ // Test that read only subject CAN'T write to the same folder
+ String writeName = "doi-test-write-failed.txt";
+ final String writeFile = dataNodeName + "/" + writeName;
+
+ // Try to write to data directory as read only subject
+ Subject.doAs(readOnlySubject, (PrivilegedExceptionAction) () -> {
+ log.debug("write as read only subject");
+ try {
+ VOSURI target1 = getVOSURI(writeFile);
+ DataNode dataNode1 = new DataNode(writeName);
+ vosClient.createNode(target1, dataNode1);
+ } catch (
+ AccessControlException e) {
+ log.debug("expected exception: " + e.getMessage());
+ } catch (Exception e) {
+ Assert.fail("exception writing file: " + e.getMessage());
+ }
+ return null;
+ });
+ return actual;
+ }
+
+ void update(Resource expected, String doiSuffix) throws Exception {
+ // For DOI tests below
+ URL doiURL = new URL(String.format("%s/%s", doiServiceURL, doiSuffix));
+
+ // Only user editable fields are language, publicationYear, creators, titles
+ expected.language = new Language("en-US");
+ expected.getPublicationYear().setValue("2024");
+ Creator creator = new Creator(getCreatorName("foo, bar", true));
+ expected.getCreators().add(creator);
+ Title title = new Title("Updated Title");
+ title.titleType = TitleType.OTHER;
+ expected.getTitles().add(title);
+
+ // Update the DOI
+ Resource actual = doUpdateTest(expected, doiURL);
+ compareResource(expected, actual, true);
+
+ // remove updated properties
+ expected.getCreators().remove(creator);
+ expected.getTitles().remove(title);
+
+ // Update the DOI
+ actual = doUpdateTest(expected, doiURL);
+ compareResource(expected, actual, true);
+ }
+
+ void publish(Resource expected, String doiSuffix) throws Exception {
+ // For DOI tests below
+ URL doiURL = new URL(String.format("%s/%s", doiServiceURL, doiSuffix));
+
+ // verify the DOI containerNode properties
+ ContainerNode doiNode = getContainerNode(doiSuffix);
+ Assert.assertFalse("incorrect isPublic property",
+ doiNode.isPublic != null && doiNode.isPublic);
+ Assert.assertFalse("should have group read property",
+ doiNode.getReadOnlyGroup().isEmpty());
+ ContainerNode dataNode = getContainerNode(doiSuffix + "/data");
+ Assert.assertFalse("should have group write",
+ dataNode.getReadWriteGroup().isEmpty());
+
+ // add a file and a subdirectory with a file to the data directory
+ String testFile1 = "test-file-1.txt";
+ String testFile1Path = String.format("%s/data/%s", doiSuffix, testFile1);
+ DataNode testFileNode = createDataNode(testFile1Path, testFile1);
+
+ String subDir = "subDir";
+ String subDirPath = String.format("%s/data/%s", doiSuffix, subDir);
+ ContainerNode dataSubDirNode = createContainerNode(subDirPath, subDir);
+
+ String testFile2 = "test-file-2.txt";
+ String testFile2Path = String.format("%s/data/%s/%s", doiSuffix, subDir, testFile2);
+ DataNode testFile2Node = createDataNode(testFile2Path, testFile2);
+
+ // mint the document, DRAFT ==> LOCKING_DATA
+ doMintTest(doiURL);
+ doiNode = getContainerNode(doiSuffix);
+ dataNode = getContainerNode(doiSuffix + "/data");
+ dataSubDirNode = getContainerNode(doiSuffix + "/data/" + subDir);
+ Assert.assertEquals("incorrect status",
+ Status.LOCKING_DATA.getValue(), doiNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
+ verifyNodeProperties(doiNode, dataNode, dataSubDirNode);
+ log.debug("locking data");
+
+ // mint the document, ERROR_LOCKING_DATA ==> LOCKING_DATA
+ doiNode.getProperty(DoiAction.DOI_VOS_STATUS_PROP).setValue(Status.ERROR_LOCKING_DATA.getValue());
+ VOSURI vosuri = getVOSURI(doiNode.getName());
+ vosClient.setNode(vosuri, doiNode);
+ doMintTest(doiURL);
+ doiNode = getContainerNode(doiSuffix);
+ dataNode = getContainerNode(doiSuffix + "/data");
+ dataSubDirNode = getContainerNode(doiSuffix + "/data/" + subDir);
+ Assert.assertEquals("incorrect status",
+ Status.LOCKING_DATA.getValue(), doiNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
+ verifyNodeProperties(doiNode, dataNode, dataSubDirNode);
+ log.debug("locking data again");
+
+ // getStatus() changes LOCKING_DATA == > LOCKED_DATA
+ DoiStatus doiStatus = getStatus(doiURL);
+ Assert.assertEquals("identifier from DOI status is different",
+ expected.getIdentifier().getValue(), doiStatus.getIdentifier().getValue());
+ Assert.assertEquals("status is incorrect", Status.LOCKED_DATA, doiStatus.getStatus());
+ verifyLockedDataPropertyChanges(doiNode, dataNode, dataSubDirNode);
+ log.debug("locked data");
+
+ // mint the document, LOCKED_DATA == REGISTERING
+ doMintTest(doiURL);
+ doiNode = getContainerNode(doiSuffix);
+ dataNode = getContainerNode(doiSuffix + "/data");
+ dataSubDirNode = getContainerNode(doiSuffix + "/data/" + subDir);
+ Assert.assertEquals("incorrect status",
+ Status.MINTED.getValue(), doiNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
+ verifyMintedStatePropertyChanges(doiNode, dataNode, dataSubDirNode);
+ log.debug("registering");
+
+ // mint the document, ERROR_REGISTERING ==> REGISTERING
+ // the doiContainerNode doesn't have group read & write anymore, and is owned
+ // by doi admin, so changes to it must be done with that cert.
+ doiNode.getProperty(DoiAction.DOI_VOS_STATUS_PROP).setValue(Status.ERROR_REGISTERING.getValue());
+ ContainerNode doiParentNode = doiNode;
+ Subject.doAs(adminSubject, (PrivilegedExceptionAction) () -> {
+ VOSURI parentVOSURI = getVOSURI(doiParentNode.getName());
+ vosClient.setNode(parentVOSURI, doiParentNode);
+ return null;
+ });
+ log.debug("registering again");
+
+ doMintTest(doiURL);
+ doiNode = getContainerNode(doiSuffix);
+ dataNode = getContainerNode(doiSuffix + "/data");
+ dataSubDirNode = getContainerNode(doiSuffix + "/data/" + subDir);
+ Assert.assertEquals("incorrect status",
+ Status.MINTED.getValue(), doiNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
+ verifyMintedStatePropertyChanges(doiNode, dataNode, dataSubDirNode);
+
+ // getStatus() changes REGISTERING == > MINTED
+ doiStatus = getStatus(doiURL);
+ Assert.assertEquals("identifier from DOI status is different",
+ expected.getIdentifier().getValue(), doiStatus.getIdentifier().getValue());
+ Assert.assertEquals("status is incorrect", Status.MINTED, doiStatus.getStatus());
+
+ // verify the DOI containerNode properties
+ Assert.assertEquals("incorrect status", Status.MINTED.getValue(), doiNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
+ }
+
+ @Override
+ protected List getDates(boolean optionalAttributes) {
+ List dates = new ArrayList<>();
+ LocalDate localDate = LocalDate.now(ZoneId.of("UTC"));
+ DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
+ String createdDate = localDate.format(formatter);
+ Date date = new Date(createdDate, DateType.CREATED);
+ if (optionalAttributes) {
+ date.dateInformation = "The date the DOI was created";
+ }
+ dates.add(date);
+ return dates;
+ }
+
+ protected Resource doUpdateTest(Resource resource, URL doiURL) throws Exception {
+ String testXML = getResourceXML(resource);
+ String persistedXml = postDOI(doiURL, testXML, TEST_JOURNAL_REF);
+ DoiXmlReader reader = new DoiXmlReader();
+ return reader.read(persistedXml);
+ }
+
+ protected void doMintTest(URL doiURL) throws Exception {
+ URL mintURL = new URL(doiURL + "/" + DoiAction.MINT_ACTION);
+ postDOI(mintURL, null, null);
+ }
+
+ private DoiStatus getStatus(URL doiURL)
+ throws Exception {
+ URL statusURL = new URL(doiURL + "/" + DoiAction.STATUS_ACTION);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ HttpGet get = new HttpGet(statusURL, bos);
+ get.setRequestProperty("Accept", "text/xml");
+ get.run();
+ Assert.assertNull("GET exception", get.getThrowable());
+ DoiStatusXmlReader reader = new DoiStatusXmlReader();
+ return reader.read(new StringReader(bos.toString(StandardCharsets.UTF_8)));
+ }
+
+ private void verifyDataDirNodeProperties(ContainerNode dataContainerNode,
+ ContainerNode dataSubDirContainerNode) {
+ // verify the DOI data containerNode properties
+ Assert.assertTrue("should be public", dataContainerNode.isPublic != null && dataContainerNode.isPublic);
+ Assert.assertTrue("should not have group read", dataContainerNode.getReadOnlyGroup().isEmpty());
+ Assert.assertTrue("should not have group write", dataContainerNode.getReadWriteGroup().isEmpty());
+ Assert.assertTrue("incorrect lock property", dataContainerNode.isLocked != null && dataContainerNode.isLocked);
+
+ // verify the DOI data subDir containerNode properties
+ Assert.assertTrue("should be public", dataSubDirContainerNode.isPublic != null && dataSubDirContainerNode.isPublic);
+ Assert.assertTrue("should not have group read", dataSubDirContainerNode.getReadOnlyGroup().isEmpty());
+ Assert.assertTrue("should not have group write", dataSubDirContainerNode.getReadWriteGroup().isEmpty());
+ Assert.assertTrue("incorrect lock property", dataSubDirContainerNode.isLocked != null && dataSubDirContainerNode.isLocked);
+ }
+
+ private void verifyNodeProperties(ContainerNode doiContainerNode, ContainerNode dataContainerNode,
+ ContainerNode dataSubDirContainerNode) {
+ // verify the DOI containerNode properties
+ Assert.assertFalse("incorrect isPublic property", doiContainerNode.isPublic != null && doiContainerNode.isPublic);
+ Assert.assertFalse("should have group read", doiContainerNode.getReadWriteGroup().isEmpty());
+ Assert.assertFalse("should have group write", doiContainerNode.getReadWriteGroup().isEmpty());
+ Assert.assertFalse("incorrect lock property", doiContainerNode.isLocked != null && doiContainerNode.isLocked);
+
+ verifyDataDirNodeProperties(dataContainerNode, dataSubDirContainerNode);
+ }
+
+ private void verifyLockedDataPropertyChanges(ContainerNode doiContainerNode, ContainerNode dataContainerNode,
+ ContainerNode dataSubDirContainerNode) {
+ // verify the DOI containerNode properties
+ Assert.assertFalse("incorrect isPublic property", doiContainerNode.isPublic != null && doiContainerNode.isPublic);
+ Assert.assertFalse("should have group read", doiContainerNode.getReadWriteGroup().isEmpty());
+ Assert.assertFalse("should have group write", doiContainerNode.getReadWriteGroup().isEmpty());
+ Assert.assertFalse("incorrect lock property", doiContainerNode.isLocked != null && doiContainerNode.isLocked);
+
+ verifyDataDirNodeProperties(dataContainerNode, dataSubDirContainerNode);
+ }
+
+ private void verifyMintedStatePropertyChanges(ContainerNode doiContainerNode, ContainerNode dataContainerNode,
+ ContainerNode dataSubDirContainerNode) {
+ // verify the DOI containerNode properties
+ Assert.assertTrue("incorrect isPublic property", doiContainerNode.isPublic != null && doiContainerNode.isPublic);
+ Assert.assertTrue("should not have group read", doiContainerNode.getReadWriteGroup().isEmpty());
+ Assert.assertTrue("should not have group write", doiContainerNode.getReadWriteGroup().isEmpty());
+
+ verifyDataDirNodeProperties(dataContainerNode, dataSubDirContainerNode);
+ }
+
+}
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/MintTest.java b/doi/src/intTest/java/ca/nrc/cadc/doi/MintTest.java
deleted file mode 100644
index b272d0a..0000000
--- a/doi/src/intTest/java/ca/nrc/cadc/doi/MintTest.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
-************************************************************************
-******************* CANADIAN ASTRONOMY DATA CENTRE *******************
-************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
-*
-* (c) 2024. (c) 2024.
-* Government of Canada Gouvernement du Canada
-* National Research Council Conseil national de recherches
-* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
-* All rights reserved Tous droits réservés
-*
-* NRC disclaims any warranties, Le CNRC dénie toute garantie
-* expressed, implied, or énoncée, implicite ou légale,
-* statutory, of any kind with de quelque nature que ce
-* respect to the software, soit, concernant le logiciel,
-* including without limitation y compris sans restriction
-* any warranty of merchantability toute garantie de valeur
-* or fitness for a particular marchande ou de pertinence
-* purpose. NRC shall not be pour un usage particulier.
-* liable in any event for any Le CNRC ne pourra en aucun cas
-* damages, whether direct or être tenu responsable de tout
-* indirect, special or general, dommage, direct ou indirect,
-* consequential or incidental, particulier ou général,
-* arising from the use of the accessoire ou fortuit, résultant
-* software. Neither the name de l'utilisation du logiciel. Ni
-* of the National Research le nom du Conseil National de
-* Council of Canada nor the Recherches du Canada ni les noms
-* names of its contributors may de ses participants ne peuvent
-* be used to endorse or promote être utilisés pour approuver ou
-* products derived from this promouvoir les produits dérivés
-* software without specific prior de ce logiciel sans autorisation
-* written permission. préalable et particulière
-* par écrit.
-*
-* This file is part of the Ce fichier fait partie du projet
-* OpenCADC project. OpenCADC.
-*
-* OpenCADC is free software: OpenCADC est un logiciel libre ;
-* you can redistribute it and/or vous pouvez le redistribuer ou le
-* modify it under the terms of modifier suivant les termes de
-* the GNU Affero General Public la “GNU Affero General Public
-* License as published by the License” telle que publiée
-* Free Software Foundation, par la Free Software Foundation
-* either version 3 of the : soit la version 3 de cette
-* License, or (at your option) licence, soit (à votre gré)
-* any later version. toute version ultérieure.
-*
-* OpenCADC is distributed in the OpenCADC est distribué
-* hope that it will be useful, dans l’espoir qu’il vous
-* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
-* without even the implied GARANTIE : sans même la garantie
-* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
-* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
-* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
-* General Public License for Générale Publique GNU Affero
-* more details. pour plus de détails.
-*
-* You should have received Vous devriez avoir reçu une
-* a copy of the GNU Affero copie de la Licence Générale
-* General Public License along Publique GNU Affero avec
-* with OpenCADC. If not, see OpenCADC ; si ce n’est
-* . pas le cas, consultez :
-* .
-*
-* $Revision: 5 $
-*
-************************************************************************
-*/
-
-package ca.nrc.cadc.doi;
-
-import ca.nrc.cadc.doi.datacite.Resource;
-import ca.nrc.cadc.doi.io.DoiXmlReader;
-import ca.nrc.cadc.doi.status.DoiStatus;
-import ca.nrc.cadc.doi.status.DoiStatusXmlReader;
-import ca.nrc.cadc.doi.status.Status;
-import ca.nrc.cadc.net.HttpGet;
-import ca.nrc.cadc.util.Log4jInit;
-import ca.nrc.cadc.uws.ExecutionPhase;
-import java.io.ByteArrayOutputStream;
-import java.io.StringReader;
-import java.net.URI;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.security.PrivilegedExceptionAction;
-import java.util.concurrent.TimeUnit;
-import javax.security.auth.Subject;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.junit.Assert;
-import org.junit.Test;
-import org.opencadc.vospace.ContainerNode;
-import org.opencadc.vospace.DataNode;
-import org.opencadc.vospace.NodeProperty;
-import org.opencadc.vospace.VOSURI;
-import org.opencadc.vospace.client.ClientAbortThread;
-import org.opencadc.vospace.client.async.RecursiveSetNode;
-
-/**
- */
-public class MintTest extends IntTestBase {
- private static final Logger log = Logger.getLogger(MintTest.class);
-
- static {
- Log4jInit.setLevel("ca.nrc.cadc.doi", Level.INFO);
- }
-
- private static final URI DOI_VOS_IS_LOCKED_PROP = URI.create("ivo://cadc.nrc.ca/vospace/core#islocked");
-
- private ContainerNode doiParentNode;
-
- // test minting DOI instance
- @Test
- public void testMintingDocument() throws Exception {
- log.info("testMintingDocument");
- final Resource testResource = getTestResource(false, true, true);
- final String testXML = getResourceXML(testResource);
- Subject.doAs(readWriteSubject, (PrivilegedExceptionAction) () -> {
- // Create the test DOI document in VOSpace
- String persistedXML = postDOI(doiServiceURL, testXML, TEST_JOURNAL_REF);
- DoiXmlReader reader = new DoiXmlReader();
- Resource presistedResource = reader.read(persistedXML);
- String expectedIdentifier = presistedResource.getIdentifier().getValue();
- Assert.assertNotEquals("New identifier not received from doi service.", testResource.getIdentifier().getValue(), expectedIdentifier);
-
- // Get the DOI suffix from the identifier
- String doiSuffix = getDOISuffix(expectedIdentifier);
- try {
- // For DOI tests below
- URL doiURL = new URL(String.format("%s/%s", doiServiceURL, doiSuffix));
-
- // Verify that the DOI document was created successfully
- DoiStatus doiStatus = getStatus(doiURL);
- Assert.assertEquals("identifier from DOI status is different", expectedIdentifier,
- doiStatus.getIdentifier().getValue());
- Assert.assertEquals("status is incorrect", Status.DRAFT, doiStatus.getStatus());
- Assert.assertEquals("journalRef is incorrect", TEST_JOURNAL_REF, doiStatus.journalRef);
-
- // verify the DOI containerNode properties
- ContainerNode doiContainerNode = getContainerNode(doiSuffix);
- Assert.assertFalse("incorrect isPublic property", doiContainerNode.isPublic != null && doiContainerNode.isPublic);
- Assert.assertFalse("should have group read property", doiContainerNode.getReadOnlyGroup().isEmpty());
- ContainerNode dataContainerNode = getContainerNode(doiSuffix + "/data");
- Assert.assertFalse("should have group write", dataContainerNode.getReadWriteGroup().isEmpty());
-
- // add a file and a subdirectory with a file to the data directory
- String testFile1 = "test-file-1.txt";
- String testFile1Path = String.format("%s/data/%s", doiSuffix, testFile1);
- DataNode testFileNode = createDataNode(testFile1Path, testFile1);
-
- String subDir = "subDir";
- String subDirPath = String.format("%s/data/%s", doiSuffix, subDir);
- ContainerNode dataSubDirContainerNode = createContainerNode(subDirPath, subDir);
-
- String testFile2 = "test-file-2.txt";
- String testFile2Path = String.format("%s/data/%s/%s", doiSuffix, subDir, testFile2);
- DataNode testFile2Node = createDataNode(testFile2Path, testFile2);
-
- // mint the document, DRAFT ==> LOCKING_DATA
- doMintTest(doiURL);
- doiContainerNode = getContainerNode(doiSuffix);
- dataContainerNode = getContainerNode(doiSuffix + "/data");
- dataSubDirContainerNode = getContainerNode(doiSuffix + "/data/" + subDir);
- Assert.assertEquals("incorrect status", Status.LOCKING_DATA.getValue(), doiContainerNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
- verifyNodeProperties(doiContainerNode, dataContainerNode, dataSubDirContainerNode);
- log.debug("locking data");
-
- // mint the document, ERROR_LOCKING_DATA ==> LOCKING_DATA
- doiContainerNode.getProperty(DoiAction.DOI_VOS_STATUS_PROP).setValue(Status.ERROR_LOCKING_DATA.getValue());
- VOSURI vosuri = getVOSURI(doiContainerNode.getName());
- vosClient.setNode(vosuri, doiContainerNode);
- doMintTest(doiURL);
- doiContainerNode = getContainerNode(doiSuffix);
- dataContainerNode = getContainerNode(doiSuffix + "/data");
- dataSubDirContainerNode = getContainerNode(doiSuffix + "/data/" + subDir);
- Assert.assertEquals("incorrect status", Status.LOCKING_DATA.getValue(), doiContainerNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
- verifyNodeProperties(doiContainerNode, dataContainerNode, dataSubDirContainerNode);
- log.debug("locking data again");
-
- // getStatus() changes LOCKING_DATA == > LOCKED_DATA
- doiStatus = getStatus(doiURL);
- Assert.assertEquals("identifier from DOI status is different", expectedIdentifier, doiStatus.getIdentifier().getValue());
- Assert.assertEquals("status is incorrect", Status.LOCKED_DATA, doiStatus.getStatus());
- verifyLockedDataPropertyChanges(doiContainerNode, dataContainerNode, dataSubDirContainerNode);
- log.debug("locked data");
-
- // mint the document, LOCKED_DATA == REGISTERING
- doMintTest(doiURL);
- doiContainerNode = getContainerNode(doiSuffix);
- dataContainerNode = getContainerNode(doiSuffix + "/data");
- dataSubDirContainerNode = getContainerNode(doiSuffix + "/data/" + subDir);
- Assert.assertEquals("incorrect status", Status.MINTED.getValue(), doiContainerNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
- verifyMintedStatePropertyChanges(doiContainerNode, dataContainerNode, dataSubDirContainerNode);
- log.debug("registering");
-
- // mint the document, ERROR_REGISTERING ==> REGISTERING
- // the doiContainerNode doesn't have group read & write anymore, and is owned
- // by doiadmin, so changes to it must be done with that cert.
- doiContainerNode.getProperty(DoiAction.DOI_VOS_STATUS_PROP).setValue(Status.ERROR_REGISTERING.getValue());
- doiParentNode = doiContainerNode;
- Subject.doAs(adminSubject, (PrivilegedExceptionAction) () -> {
- VOSURI parentVOSURI = getVOSURI(doiParentNode.getName());
- vosClient.setNode(parentVOSURI, doiParentNode);
- return null;
- });
- log.debug("registering again");
-
- doMintTest(doiURL);
- doiContainerNode = getContainerNode(doiSuffix);
- dataContainerNode = getContainerNode(doiSuffix + "/data");
- dataSubDirContainerNode = getContainerNode(doiSuffix + "/data/" + subDir);
- Assert.assertEquals("incorrect status", Status.MINTED.getValue(), doiContainerNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
- verifyMintedStatePropertyChanges(doiContainerNode, dataContainerNode, dataSubDirContainerNode);
-
- // getStatus() changes REGISTERING == > MINTED
- doiStatus = getStatus(doiURL);
- Assert.assertEquals("identifier from DOI status is different", expectedIdentifier, doiStatus.getIdentifier().getValue());
- Assert.assertEquals("status is incorrect", Status.MINTED, doiStatus.getStatus());
-
- // verify the DOI containerNode properties
- Assert.assertEquals("incorrect status", Status.MINTED.getValue(), doiContainerNode.getPropertyValue(DoiAction.DOI_VOS_STATUS_PROP));
- } catch (Throwable e) {
- log.error("unexpected exception", e);
- Assert.fail("unexpected exception: " + e);
- } finally {
- // cannot delete a DOI when it is in 'MINTED' state, change its state to 'DRAFT'
- // node owner is doiadmin, and after minting the group permissions are removed, so
- // cleanup needs to be done as doiadmin, not the test subject
- Subject.doAs(adminSubject, (PrivilegedExceptionAction) () -> {
- ContainerNode doiContainerNode = getContainerNode(doiSuffix);
- VOSURI vosuri = getVOSURI(doiContainerNode.getName());
- doiContainerNode.getProperty(DoiAction.DOI_VOS_STATUS_PROP).setValue(Status.DRAFT.getValue());
- vosClient.setNode(vosuri, doiContainerNode);
-
- // unlock the data directory and delete the DOI
- setDataNodeRecursively(doiSuffix);
- cleanup(doiSuffix);
- return null;
- });
- }
- return presistedResource;
- });
- }
-
- private DoiStatus getStatus(URL doiURL)
- throws Exception {
- URL statusURL = new URL(doiURL + "/" + DoiAction.STATUS_ACTION);
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- HttpGet get = new HttpGet(statusURL, bos);
- get.setRequestProperty("Accept", "text/xml");
- get.run();
- Assert.assertNull("GET exception", get.getThrowable());
- DoiStatusXmlReader reader = new DoiStatusXmlReader();
- return reader.read(new StringReader(bos.toString(StandardCharsets.UTF_8)));
- }
-
- private void doMintTest(URL doiURL)
- throws Exception {
- URL mintURL = new URL(doiURL + "/" + DoiAction.MINT_ACTION);
- postDOI(mintURL, null, null);
- }
-
- private void verifyDataDirNodeProperties(ContainerNode dataContainerNode,
- ContainerNode dataSubDirContainerNode) {
- // verify the DOI data containerNode properties
- Assert.assertTrue("should be public", dataContainerNode.isPublic != null && dataContainerNode.isPublic);
- Assert.assertTrue("should not have group read", dataContainerNode.getReadOnlyGroup().isEmpty());
- Assert.assertTrue("should not have group write", dataContainerNode.getReadWriteGroup().isEmpty());
- Assert.assertTrue("incorrect lock property", dataContainerNode.isLocked != null && dataContainerNode.isLocked);
-
- // verify the DOI data subDir containerNode properties
- Assert.assertTrue("should be public", dataSubDirContainerNode.isPublic != null && dataSubDirContainerNode.isPublic);
- Assert.assertTrue("should not have group read", dataSubDirContainerNode.getReadOnlyGroup().isEmpty());
- Assert.assertTrue("should not have group write", dataSubDirContainerNode.getReadWriteGroup().isEmpty());
- Assert.assertTrue("incorrect lock property", dataSubDirContainerNode.isLocked != null && dataSubDirContainerNode.isLocked);
- }
-
- private void verifyNodeProperties(ContainerNode doiContainerNode, ContainerNode dataContainerNode,
- ContainerNode dataSubDirContainerNode) {
- // verify the DOI containerNode properties
- Assert.assertFalse("incorrect isPublic property", doiContainerNode.isPublic != null && doiContainerNode.isPublic);
- Assert.assertFalse("should have group read", doiContainerNode.getReadWriteGroup().isEmpty());
- Assert.assertFalse("should have group write", doiContainerNode.getReadWriteGroup().isEmpty());
- Assert.assertFalse("incorrect lock property", doiContainerNode.isLocked != null && doiContainerNode.isLocked);
-
- verifyDataDirNodeProperties(dataContainerNode, dataSubDirContainerNode);
- }
-
- private void verifyLockedDataPropertyChanges(ContainerNode doiContainerNode, ContainerNode dataContainerNode,
- ContainerNode dataSubDirContainerNode) {
- // verify the DOI containerNode properties
- Assert.assertFalse("incorrect isPublic property", doiContainerNode.isPublic != null && doiContainerNode.isPublic);
- Assert.assertFalse("should have group read", doiContainerNode.getReadWriteGroup().isEmpty());
- Assert.assertFalse("should have group write", doiContainerNode.getReadWriteGroup().isEmpty());
- Assert.assertFalse("incorrect lock property", doiContainerNode.isLocked != null && doiContainerNode.isLocked);
-
- verifyDataDirNodeProperties(dataContainerNode, dataSubDirContainerNode);
- }
-
- private void verifyMintedStatePropertyChanges(ContainerNode doiContainerNode, ContainerNode dataContainerNode,
- ContainerNode dataSubDirContainerNode) {
- // verify the DOI containerNode properties
- Assert.assertTrue("incorrect isPublic property", doiContainerNode.isPublic != null && doiContainerNode.isPublic);
- Assert.assertTrue("should not have group read", doiContainerNode.getReadWriteGroup().isEmpty());
- Assert.assertTrue("should not have group write", doiContainerNode.getReadWriteGroup().isEmpty());
-
- verifyDataDirNodeProperties(dataContainerNode, dataSubDirContainerNode);
- }
-
- private ContainerNode createContainerNode(String path, String name) throws Exception {
- ContainerNode node = new ContainerNode(name);
- VOSURI nodeURI = getVOSURI(path);
- return (ContainerNode) vosClient.createNode(nodeURI, node);
- }
-
- private DataNode createDataNode(String path, String name) throws Exception {
- DataNode node = new DataNode(name);
- VOSURI nodeURI = getVOSURI(path);
- return (DataNode) vosClient.createNode(nodeURI, node);
- }
-
- private void setDataNodeRecursively(String doiSuffix) throws Exception {
- Subject.doAs(adminSubject, (PrivilegedExceptionAction) () -> {
- VOSURI vosuri = getVOSURI(String.format("%s/data", doiSuffix));
- ContainerNode dataContainerNode = new ContainerNode("data");
- dataContainerNode.getProperties().add(new NodeProperty(DOI_VOS_IS_LOCKED_PROP, "false"));
- RecursiveSetNode recursiveSetNode = vosClient.createRecursiveSetNode(vosuri, dataContainerNode);
- URL jobURL = recursiveSetNode.getJobURL();
-
- // this is an async operation
- Thread abortThread = new ClientAbortThread(jobURL);
- Runtime.getRuntime().addShutdownHook(abortThread);
- recursiveSetNode.setMonitor(true);
- recursiveSetNode.run();
- Runtime.getRuntime().removeShutdownHook(abortThread);
-
- recursiveSetNode = new RecursiveSetNode(jobURL, dataContainerNode);
- recursiveSetNode.setSchemaValidation(false);
- ExecutionPhase phase = recursiveSetNode.getPhase(20);
- while (phase == ExecutionPhase.QUEUED || phase == ExecutionPhase.EXECUTING) {
- TimeUnit.SECONDS.sleep(1);
- phase = recursiveSetNode.getPhase();
- }
-
- Assert.assertSame("Failed to unlock test data directory, phase = " + phase, ExecutionPhase.COMPLETED, phase);
- return phase.getValue();
- });
- }
-
-}
\ No newline at end of file
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/TestUtil.java b/doi/src/intTest/java/ca/nrc/cadc/doi/TestUtil.java
index 4604bcd..8ecc46f 100644
--- a/doi/src/intTest/java/ca/nrc/cadc/doi/TestUtil.java
+++ b/doi/src/intTest/java/ca/nrc/cadc/doi/TestUtil.java
@@ -69,29 +69,77 @@
package ca.nrc.cadc.doi;
+import ca.nrc.cadc.util.FileUtil;
+import ca.nrc.cadc.util.Log4jInit;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.opencadc.vospace.VOSURI;
public class TestUtil {
+ private static final Logger log = Logger.getLogger(TestUtil.class);
+
+ static {
+ Log4jInit.setLevel("ca.nrc.cadc.doi", Level.INFO);
+ }
+
+ // ADMIN_CERT is the owner of the test DOI
+ static String ADMIN_CERT = "doi-admin.pem";
+
+ // AUTH_CERT has read/write access to the test DOI
+ static String AUTH_CERT = "doi-auth.pem";
+
+ // NO_AUTH_CERT has read only access to the test DOI
+ static String NO_AUTH_CERT = "doi-noauth.pem";
// resourceID for the local test DOI service
- public static URI DOI_RESOURCE_ID = URI.create("ivo://opencadc.org/doi");
+ static URI DOI_RESOURCE_ID = URI.create("ivo://opencadc.org/doi");
+
+ // VOSpace URI to the DOI parent node,
+ static URI VOSPACE_PARENT_URI = URI.create("vos://opencadc.org~vault/doi");
+
+ // following derived from VOSPACE_PARENT_URI
+ // resourceID for the local VOSpace service
+ static URI VOSPACE_RESOURCE_ID;
- // resourceID for the production DOI service
- public static URI PROD_DOI_RESOURCE_ID = URI.create("ivo://cadc.nrc.ca/doi");
+ // path for the DOI parent node in VOSpace
+ static String DOI_PARENT_PATH;
- // resourceID for the vault service (used to store the DOI metadata)
- public static URI VAULT_RESOURCE_ID = URI.create("ivo://opencadc.org/vault");
+ static {
- // ADMIN_CERT has full access to a test DOI
- public static String ADMIN_CERT = "doiadmin.pem";
+ try {
+ File opt = FileUtil.getFileFromResource("intTest.properties", TestUtil.class);
+ if (opt.exists()) {
+ Properties props = new Properties();
+ props.load(new FileReader(opt));
- // AUTH_CERT has read/write access to a test DOI
- public static String AUTH_CERT = "doi-auth.pem";
+ if (props.containsKey("doiResourceID")) {
+ DOI_RESOURCE_ID = URI.create(props.getProperty("doiResourceID").trim());
+ }
+ if (props.containsKey("vospaceParentUri")) {
+ VOSPACE_PARENT_URI = URI.create(props.getProperty("vospaceParentUri").trim());
+ }
+ }
+ }
+ catch (MissingResourceException | FileNotFoundException noFileException) {
+ log.debug("No intTest.properties supplied. Using defaults.");
+ } catch (IOException oops) {
+ throw new RuntimeException(oops.getMessage(), oops);
+ }
- // NO_AUTH_CERT has read only access to a test DOI
- public static String NO_AUTH_CERT = "doi-noauth.pem";
+ VOSURI vosURI = new VOSURI(VOSPACE_PARENT_URI);
+ VOSPACE_RESOURCE_ID = vosURI.getServiceURI();
+ DOI_PARENT_PATH = vosURI.getPath();
- // expected path for the DOI parent node
- public static String DOI_PARENT_PATH = "/AstroDataCitationDOI/CISTI.CANFAR";
+ log.debug(String.format("intTest config: %s %s %s %s",
+ DOI_RESOURCE_ID, VOSPACE_PARENT_URI, VOSPACE_RESOURCE_ID, DOI_PARENT_PATH));
+ }
-}
+ }
diff --git a/doi/src/intTest/java/ca/nrc/cadc/doi/UpdateTest.java b/doi/src/intTest/java/ca/nrc/cadc/doi/UpdateTest.java
deleted file mode 100644
index 8f9d665..0000000
--- a/doi/src/intTest/java/ca/nrc/cadc/doi/UpdateTest.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
-************************************************************************
-******************* CANADIAN ASTRONOMY DATA CENTRE *******************
-************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
-*
-* (c) 2024. (c) 2024.
-* Government of Canada Gouvernement du Canada
-* National Research Council Conseil national de recherches
-* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
-* All rights reserved Tous droits réservés
-*
-* NRC disclaims any warranties, Le CNRC dénie toute garantie
-* expressed, implied, or énoncée, implicite ou légale,
-* statutory, of any kind with de quelque nature que ce
-* respect to the software, soit, concernant le logiciel,
-* including without limitation y compris sans restriction
-* any warranty of merchantability toute garantie de valeur
-* or fitness for a particular marchande ou de pertinence
-* purpose. NRC shall not be pour un usage particulier.
-* liable in any event for any Le CNRC ne pourra en aucun cas
-* damages, whether direct or être tenu responsable de tout
-* indirect, special or general, dommage, direct ou indirect,
-* consequential or incidental, particulier ou général,
-* arising from the use of the accessoire ou fortuit, résultant
-* software. Neither the name de l'utilisation du logiciel. Ni
-* of the National Research le nom du Conseil National de
-* Council of Canada nor the Recherches du Canada ni les noms
-* names of its contributors may de ses participants ne peuvent
-* be used to endorse or promote être utilisés pour approuver ou
-* products derived from this promouvoir les produits dérivés
-* software without specific prior de ce logiciel sans autorisation
-* written permission. préalable et particulière
-* par écrit.
-*
-* This file is part of the Ce fichier fait partie du projet
-* OpenCADC project. OpenCADC.
-*
-* OpenCADC is free software: OpenCADC est un logiciel libre ;
-* you can redistribute it and/or vous pouvez le redistribuer ou le
-* modify it under the terms of modifier suivant les termes de
-* the GNU Affero General Public la “GNU Affero General Public
-* License as published by the License” telle que publiée
-* Free Software Foundation, par la Free Software Foundation
-* either version 3 of the : soit la version 3 de cette
-* License, or (at your option) licence, soit (à votre gré)
-* any later version. toute version ultérieure.
-*
-* OpenCADC is distributed in the OpenCADC est distribué
-* hope that it will be useful, dans l’espoir qu’il vous
-* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
-* without even the implied GARANTIE : sans même la garantie
-* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
-* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
-* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
-* General Public License for Générale Publique GNU Affero
-* more details. pour plus de détails.
-*
-* You should have received Vous devriez avoir reçu une
-* a copy of the GNU Affero copie de la Licence Générale
-* General Public License along Publique GNU Affero avec
-* with OpenCADC. If not, see OpenCADC ; si ce n’est
-* . pas le cas, consultez :
-* .
-*
-* $Revision: 5 $
-*
-************************************************************************
-*/
-
-package ca.nrc.cadc.doi;
-
-import ca.nrc.cadc.doi.datacite.Date;
-import ca.nrc.cadc.doi.datacite.DateType;
-import ca.nrc.cadc.doi.datacite.Language;
-import ca.nrc.cadc.doi.datacite.Resource;
-import ca.nrc.cadc.doi.io.DoiXmlReader;
-import ca.nrc.cadc.util.Log4jInit;
-import java.net.URL;
-import java.security.PrivilegedExceptionAction;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.List;
-import javax.security.auth.Subject;
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Persist a minimal instance
- */
-public class UpdateTest extends IntTestBase {
- private static final Logger log = Logger.getLogger(UpdateTest.class);
-
- static {
- Log4jInit.setLevel("ca.nrc.cadc.doi", Level.INFO);
- }
-
- @Override
- protected List getDates(boolean optionalAttributes) {
- List dates = new ArrayList<>();
- LocalDate localDate = LocalDate.now(ZoneId.of("UTC"));
- DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
- String createdDate = localDate.format(formatter);
- Date date = new Date(createdDate, DateType.CREATED);
- if (optionalAttributes) {
- date.dateInformation = "The date the DOI was created";
- }
- dates.add(date);
- return dates;
- }
-
- @Test
- public void updateDOITest() throws Exception {
- log.info("updateDOITest");
- Subject.doAs(readWriteSubject, (PrivilegedExceptionAction) () -> {
- // minimally populated required properties
- Resource expected = getTestResource(true, true, true);
- expected.language = new Language("en-US");
- List testDOIList = new ArrayList<>();
- try {
- Resource actual = doTest(expected);
-
- Assert.assertNotEquals("Identifier's should not match",
- expected.getIdentifier().getValue(), actual.getIdentifier().getValue());
- compareResource(expected, actual, false);
-
- // get the URL to the new DOI
- String doiSuffix = getDOISuffix(actual.getIdentifier().getValue());
- testDOIList.add(doiSuffix);
- URL doiURL = new URL(String.format("%s/%s", doiServiceURL, doiSuffix));
-
- // fully populated required resource
- Resource maxResource = getTestResource(false, true, true);
- updateResource(expected, maxResource);
- actual = doTest(expected);
- testDOIList.add(getDOISuffix(actual.getIdentifier().getValue()));
- compareResource(expected, actual);
-
- // back to minimally populated required resource
- Resource minResource = getTestResource(false, false, true);
- updateResource(expected, minResource);
- actual = doTest(expected);
- testDOIList.add(getDOISuffix(actual.getIdentifier().getValue()));
- compareResource(expected, actual);
-
- // update PublicationYear
- expected.getPublicationYear().setValue("2001");
- actual = doTest(expected);
- testDOIList.add(getDOISuffix(actual.getIdentifier().getValue()));
- compareResource(expected, actual);
-
- // update Language
- expected.language = new Language("en-GB");
- actual = doTest(expected);
- testDOIList.add(getDOISuffix(actual.getIdentifier().getValue()));
- compareResource(expected, actual);
- } finally {
- for (String doiSuffix : testDOIList) {
- cleanup(doiSuffix);
- }
- }
- return null;
- });
- }
-
- protected void updateResource(Resource destination, Resource source) {
- destination.getCreators().clear();
- destination.getCreators().addAll(source.getCreators());
-
- destination.getTitles().clear();
- destination.getTitles().addAll(source.getTitles());
-
- destination.getPublisher().publisherIdentifier = source.getPublisher().publisherIdentifier;
- destination.getPublisher().publisherIdentifierScheme = source.getPublisher().publisherIdentifierScheme;
- destination.getPublisher().schemeURI = source.getPublisher().schemeURI;
- destination.getPublisher().lang = source.getPublisher().lang;
-
- destination.getResourceType().value = source.getResourceType().value;
- }
-
- protected Resource doTest(Resource resource) throws Exception {
- String testXML = getResourceXML(resource);
- String persistedXml = postDOI(doiServiceURL, testXML, TEST_JOURNAL_REF);
- DoiXmlReader reader = new DoiXmlReader();
- return reader.read(persistedXml);
- }
-
- @Override
- protected void compareResource(Resource expected, Resource actual) {
- Assert.assertNotEquals("Identifier's should not match",
- expected.getIdentifier().getValue(), actual.getIdentifier().getValue());
- compareResource(expected, actual, false);
- }
-
-}
\ No newline at end of file
diff --git a/doi/src/main/java/ca/nrc/cadc/doi/DeleteAction.java b/doi/src/main/java/ca/nrc/cadc/doi/DeleteAction.java
index 96f128d..cdc0540 100644
--- a/doi/src/main/java/ca/nrc/cadc/doi/DeleteAction.java
+++ b/doi/src/main/java/ca/nrc/cadc/doi/DeleteAction.java
@@ -90,7 +90,7 @@ public DeleteAction() {
public void doAction() throws Exception {
super.init(true);
- // Do all subsequent work as doiadmin
+ // Do all subsequent work as doi admin
Subject.doAs(getAdminSubject(), (PrivilegedExceptionAction) () -> {
doActionImpl();
return null;
@@ -119,7 +119,7 @@ private void doActionImpl() throws Exception {
Integer numericID = Integer.parseInt(doiRequester);
Subject requestorSubject = acIdentMgr.toSubject(numericID);
if (!checkSubjectsMatch(callingSubject, requestorSubject)) {
- // if doiadmin is the calling user, it has permission to delete any of the DOIs as well
+ // if doi admin is the calling user, it has permission to delete any of the DOIs as well
if (!checkSubjectsMatch(callingSubject, getAdminSubject())) {
throw new AccessControlException("Not permitted to delete DOI");
}
diff --git a/doi/src/main/java/ca/nrc/cadc/doi/DoiAction.java b/doi/src/main/java/ca/nrc/cadc/doi/DoiAction.java
index 85a1fd8..206f47b 100644
--- a/doi/src/main/java/ca/nrc/cadc/doi/DoiAction.java
+++ b/doi/src/main/java/ca/nrc/cadc/doi/DoiAction.java
@@ -71,6 +71,8 @@
import ca.nrc.cadc.ac.client.GMSClient;
import ca.nrc.cadc.auth.AuthenticationUtil;
import ca.nrc.cadc.auth.SSLUtil;
+import ca.nrc.cadc.reg.Standards;
+import ca.nrc.cadc.reg.client.LocalAuthority;
import ca.nrc.cadc.rest.InlineContentHandler;
import ca.nrc.cadc.rest.RestAction;
import ca.nrc.cadc.util.MultiValuedProperties;
@@ -79,16 +81,14 @@
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.security.AccessControlException;
+import java.util.Set;
import javax.security.auth.Subject;
-import javax.security.auth.x500.X500Principal;
import org.apache.log4j.Logger;
import org.opencadc.vospace.VOSURI;
public abstract class DoiAction extends RestAction {
private static final Logger log = Logger.getLogger(DoiAction.class);
- public static final X500Principal DOIADMIN_X500 = new X500Principal("C=ca,O=hia,OU=cadc,CN=doiadmin_045");
-
public static final URI DOI_VOS_JOB_URL_PROP = URI.create("ivo://cadc.nrc.ca/vospace/doi#joburl");
public static final URI DOI_VOS_REQUESTER_PROP = URI.create("ivo://cadc.nrc.ca/vospace/doi#requester");
public static final URI DOI_VOS_STATUS_PROP = URI.create("ivo://cadc.nrc.ca/vospace/doi#status");
@@ -98,6 +98,7 @@ public abstract class DoiAction extends RestAction {
public static final String MINT_ACTION = "mint";
public static final String JOURNALREF_PARAM = "journalref";
public static final String DOI_GROUP_PREFIX = "DOI-";
+ public static final String TEST_DOI_GROUP_PREFIX = "TEST.DOI-";
protected Subject callingSubject;
protected Long callersNumericId;
@@ -130,10 +131,18 @@ protected void init(boolean authorize)
throws URISyntaxException, UnknownHostException {
// load doi properties
this.config = DoiInitAction.getConfig();
- this.vaultResourceID = URI.create(config.getFirstPropertyValue(DoiInitAction.VAULT_RESOURCE_ID_KEY));
- this.gmsResourceID = URI.create(config.getFirstPropertyValue(DoiInitAction.GMS_RESOURCE_ID_KEY));
+ this.vaultResourceID = DoiInitAction.getVospaceResourceID(config);
+ this.parentPath = DoiInitAction.getParentPath(config);
this.accountPrefix = config.getFirstPropertyValue(DoiInitAction.DATACITE_ACCOUNT_PREFIX_KEY);
- this.parentPath = config.getFirstPropertyValue(DoiInitAction.PARENT_PATH_KEY);
+
+ LocalAuthority localAuthority = new LocalAuthority();
+ Set gmsServices = localAuthority.getServiceURIs(Standards.GMS_SEARCH_10);
+ if (gmsServices.isEmpty()) {
+ throw new IllegalStateException("GMS service not found");
+ } else if (gmsServices.size() > 1) {
+ throw new IllegalStateException("multiple GMS services found");
+ }
+ this.gmsResourceID = gmsServices.iterator().next();
// get calling subject
callingSubject = AuthenticationUtil.getCurrentSubject();
@@ -152,7 +161,7 @@ protected void init(boolean authorize)
protected String getDoiFilename(String suffix) {
return String.format("%s%s.xml",
- config.getFirstPropertyValue(DoiInitAction.METADATA_FILE_PREFIX_KEY), suffix);
+ config.getFirstPropertyValue(DoiInitAction.METADATA_PREFIX_KEY), suffix);
}
protected VOSURI getVOSURI(String path) {
@@ -160,7 +169,6 @@ protected VOSURI getVOSURI(String path) {
}
protected GMSClient getGMSClient() {
- URI gmsResourceID = URI.create(config.getFirstPropertyValue(DoiInitAction.GMS_RESOURCE_ID_KEY));
return new GMSClient(gmsResourceID);
}
diff --git a/doi/src/main/java/ca/nrc/cadc/doi/DoiInitAction.java b/doi/src/main/java/ca/nrc/cadc/doi/DoiInitAction.java
index ee5bf60..6f48976 100644
--- a/doi/src/main/java/ca/nrc/cadc/doi/DoiInitAction.java
+++ b/doi/src/main/java/ca/nrc/cadc/doi/DoiInitAction.java
@@ -76,7 +76,6 @@
import ca.nrc.cadc.rest.InitAction;
import ca.nrc.cadc.util.MultiValuedProperties;
import ca.nrc.cadc.util.PropertiesReader;
-import ca.nrc.cadc.util.StringUtil;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
@@ -88,25 +87,23 @@
import org.apache.log4j.Logger;
import org.opencadc.vospace.ContainerNode;
import org.opencadc.vospace.Node;
+import org.opencadc.vospace.VOSURI;
import org.opencadc.vospace.client.VOSpaceClient;
public class DoiInitAction extends InitAction {
private static final Logger log = Logger.getLogger(DoiInitAction.class);
public static final String DOI_KEY = "ca.nrc.cadc.doi";
- public static final String VAULT_RESOURCE_ID_KEY = DOI_KEY + ".vaultResourceID";
- public static final String GMS_RESOURCE_ID_KEY = DOI_KEY + ".gmsResourceID";
- public static final String PARENT_PATH_KEY = DOI_KEY + ".parentPath";
- public static final String METADATA_FILE_PREFIX_KEY = DOI_KEY + ".metadataFilePrefix";
+ public static final String VOSPACE_PARENT_URI_KEY = DOI_KEY + ".vospaceParentUri";
+ public static final String METADATA_PREFIX_KEY = DOI_KEY + ".metaDataPrefix";
public static final String LANDING_URL_KEY = DOI_KEY + ".landingUrl";
public static final String DATACITE_MDS_URL_KEY = DOI_KEY + ".datacite.mdsUrl";
- public static final String DATACITE_ACCOUNT_PREFIX_KEY = DOI_KEY + ".datacite.accountPrefix";
public static final String DATACITE_MDS_USERNAME_KEY = DOI_KEY + ".datacite.username";
public static final String DATACITE_MDS_PASSWORD_KEY = DOI_KEY + ".datacite.password";
+ public static final String DATACITE_ACCOUNT_PREFIX_KEY = DOI_KEY + ".datacite.accountPrefix";
// optional properties
- public static final String TEST_RANDOM_NAME_KEY = DOI_KEY + ".test.randomName";
- public static final String TEST_GROUP_URI_KEY = DOI_KEY + ".test.groupURI";
+ public static final String RANDOM_TEST_ID_KEY = DOI_KEY + ".randomTestID";
@Override
public void doInit() {
@@ -118,6 +115,28 @@ public static MultiValuedProperties getConfig() {
return getConfig(false);
}
+ public static URI getVospaceResourceID(MultiValuedProperties props) {
+ String vospaceParentUri = props.getFirstPropertyValue(VOSPACE_PARENT_URI_KEY);
+ VOSURI vosURI;
+ try {
+ vosURI = new VOSURI(vospaceParentUri);
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("invalid VOSpace URI: " + vospaceParentUri);
+ }
+ return vosURI.getServiceURI();
+ }
+
+ public static String getParentPath(MultiValuedProperties props) {
+ String vospaceParentUri = props.getFirstPropertyValue(VOSPACE_PARENT_URI_KEY);
+ VOSURI vosURI;
+ try {
+ vosURI = new VOSURI(vospaceParentUri);
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("invalid VOSpace URI: " + vospaceParentUri);
+ }
+ return vosURI.getPath();
+ }
+
private static MultiValuedProperties getConfig(boolean verify) {
PropertiesReader reader = new PropertiesReader("doi.properties");
MultiValuedProperties props = reader.getAllProperties();
@@ -125,43 +144,24 @@ private static MultiValuedProperties getConfig(boolean verify) {
StringBuilder sb = new StringBuilder();
boolean ok = true;
- String vaultResourceID = props.getFirstPropertyValue(VAULT_RESOURCE_ID_KEY);
- sb.append(String.format("\n\t%s: ", VAULT_RESOURCE_ID_KEY));
- if (vaultResourceID == null) {
+ String parentUri = props.getFirstPropertyValue(VOSPACE_PARENT_URI_KEY);
+ sb.append(String.format("\n\t%s: ", VOSPACE_PARENT_URI_KEY));
+ if (parentUri == null) {
sb.append("MISSING");
ok = false;
- } else if (verify) {
- try {
- new URI(vaultResourceID);
- sb.append("OK");
- } catch (URISyntaxException e) {
- sb.append("INVALID VAULT RESOURCE ID: ").append(e.getMessage());
- ok = false;
- }
} else {
- sb.append("OK");
- }
-
- String gmsResourceID = props.getFirstPropertyValue(GMS_RESOURCE_ID_KEY);
- sb.append(String.format("\n\t%s: ", GMS_RESOURCE_ID_KEY));
- if (gmsResourceID == null) {
- sb.append("MISSING");
- ok = false;
- } else if (verify) {
try {
- new URI(gmsResourceID);
+ new VOSURI(parentUri);
sb.append("OK");
} catch (URISyntaxException e) {
- sb.append("INVALID GMS RESOURCE ID: ").append(e.getMessage());
+ sb.append("INVALID VOSPACE URI: ").append(e.getMessage());
ok = false;
}
- } else {
- sb.append("OK");
}
- String metadataFilePrefix = props.getFirstPropertyValue(METADATA_FILE_PREFIX_KEY);
- sb.append(String.format("\n\t%s: ", METADATA_FILE_PREFIX_KEY));
- if (metadataFilePrefix == null) {
+ String metaDataPrefix = props.getFirstPropertyValue(METADATA_PREFIX_KEY);
+ sb.append(String.format("\n\t%s: ", METADATA_PREFIX_KEY));
+ if (metaDataPrefix == null) {
sb.append("MISSING");
ok = false;
} else {
@@ -185,24 +185,6 @@ private static MultiValuedProperties getConfig(boolean verify) {
sb.append("OK");
}
- String parentPath = props.getFirstPropertyValue(PARENT_PATH_KEY);
- sb.append(String.format("\n\t%s: ", PARENT_PATH_KEY));
- if (parentPath == null) {
- sb.append("MISSING");
- ok = false;
- } else {
- sb.append("OK");
- }
-
- String accountPrefix = props.getFirstPropertyValue(DATACITE_ACCOUNT_PREFIX_KEY);
- sb.append(String.format("\n\t%s: ", DATACITE_ACCOUNT_PREFIX_KEY));
- if (accountPrefix == null) {
- sb.append("MISSING");
- ok = false;
- } else {
- sb.append("OK");
- }
-
String mdsEndpoint = props.getFirstPropertyValue(DATACITE_MDS_URL_KEY);
sb.append(String.format("\n\t%s: ", DATACITE_MDS_URL_KEY));
if (mdsEndpoint == null) {
@@ -238,92 +220,71 @@ private static MultiValuedProperties getConfig(boolean verify) {
sb.append("OK");
}
- // optional properties
- String testRandomName = props.getFirstPropertyValue(TEST_RANDOM_NAME_KEY);
- sb.append(String.format("\n\t%s: ", TEST_RANDOM_NAME_KEY));
- if (testRandomName == null) {
+ String accountPrefix = props.getFirstPropertyValue(DATACITE_ACCOUNT_PREFIX_KEY);
+ sb.append(String.format("\n\t%s: ", DATACITE_ACCOUNT_PREFIX_KEY));
+ if (accountPrefix == null) {
sb.append("MISSING");
+ ok = false;
} else {
sb.append("OK");
}
- String testGroupURI = props.getFirstPropertyValue(TEST_GROUP_URI_KEY);
- sb.append(String.format("\n\t%s: ", TEST_GROUP_URI_KEY));
- if (testGroupURI == null) {
+ // optional properties
+ String randomTestID = props.getFirstPropertyValue(RANDOM_TEST_ID_KEY);
+ sb.append(String.format("\n\t%s: ", RANDOM_TEST_ID_KEY));
+ if (randomTestID == null) {
sb.append("MISSING");
} else {
- if (verify) {
- try {
- new URI(testGroupURI);
- sb.append("OK");
- } catch (URISyntaxException e) {
- sb.append("INVALID URI");
- ok = false;
- }
- } else {
- sb.append("OK");
- }
+ sb.append("OK");
}
-
if (!ok) {
throw new IllegalStateException("incomplete config: " + sb);
}
return props;
}
- // check that the DOI parent node path, configured with the V0SPACE_PARENT_PATH_KEY property,
+ // check that the DOI parent node uri, configured with the VOSPACE_PARENT_URI_KEY property,
// exists and has the expected properties.
private static void checkParentFolders() {
- MultiValuedProperties config = getConfig();
- URI vospaceResourceID = URI.create(config.getFirstPropertyValue(DoiInitAction.VAULT_RESOURCE_ID_KEY));
- String parentPath = config.getFirstPropertyValue(PARENT_PATH_KEY);
Subject adminSubject = SSLUtil.createSubject(new File("/config/doiadmin.pem"));
adminSubject = AuthenticationUtil.augmentSubject(adminSubject);
String adminUsername = getUsername(adminSubject);
- // TODO is it necessary to check all nodes in the path for ownership and permissions,
- // or is it sufficient to check the last node?
+ MultiValuedProperties config = getConfig();
+ URI vospaceResourceID = DoiInitAction.getVospaceResourceID(config);
+ String parentPath = DoiInitAction.getParentPath(config);
VOSpaceClient vosClient = new VOSpaceClient(vospaceResourceID);
- String currentPath = "";
- String[] paths = parentPath.split("/");
- for (String path : paths) {
- // skip first empty path if parentPath begins with a /
- if (!StringUtil.hasText(path)) {
- continue;
- }
- currentPath = String.format("%s/%s", currentPath, path);
- Node node;
- try {
- node = vosClient.getNode(currentPath);
- } catch (ResourceNotFoundException e) {
- throw new IllegalStateException(String.format("node %s not found", path));
- } catch (Exception e) {
- throw new IllegalStateException(String.format("node %s error because %s", path, e.getMessage()));
- }
+ Node node;
+ try {
+ node = vosClient.getNode(parentPath);
+ } catch (ResourceNotFoundException e) {
+ throw new IllegalStateException(String.format("DOI parent node %s not found", parentPath));
+ } catch (Exception e) {
+ throw new IllegalStateException(String.format("DOI parent node %s error because %s", parentPath, e.getMessage()));
+ }
- // confirm it's a ContainerNode
- if (!(node instanceof ContainerNode)) {
- throw new IllegalStateException(String.format("node %s is not a ContainerNode", path));
- }
- ContainerNode containerNode = (ContainerNode) node;
+ // confirm it's a ContainerNode
+ if (!(node instanceof ContainerNode)) {
+ throw new IllegalStateException(String.format("DOI parent node %s is not a ContainerNode", parentPath));
+ }
+ ContainerNode containerNode = (ContainerNode) node;
- // check node owner
- String ownerID = containerNode.ownerDisplay;
- if (!adminUsername.equals(ownerID)) {
- throw new IllegalStateException(String.format("node %s owner %s doesn't match configured admin user %s", path, ownerID, adminUsername));
- }
+ // check node owner
+ String ownerID = containerNode.ownerDisplay;
+ if (!adminUsername.equals(ownerID)) {
+ throw new IllegalStateException(String.format("DOI parent node %s owner %s doesn't match configured admin user %s", parentPath, ownerID, adminUsername));
+ }
- // check node has public access
- if (!containerNode.isPublic) {
- throw new IllegalStateException(String.format("node %s must have isPublic set to true", path));
- }
+ // check node has public access
+ if (!containerNode.isPublic) {
+ throw new IllegalStateException(String.format("DOI parent node %s must have isPublic set to true", parentPath));
+ }
- // check inheritPermissions is true (does inheritPermissions need to be true?)
- if (!containerNode.inheritPermissions) {
- throw new IllegalStateException(String.format("node %s must have inheritPermissions set to true", path));
- }
+ // check inheritPermissions is true
+ if (!containerNode.inheritPermissions) {
+ throw new IllegalStateException(String.format("DOI parent node %s must have inheritPermissions set to true", parentPath));
}
}
diff --git a/doi/src/main/java/ca/nrc/cadc/doi/GetAction.java b/doi/src/main/java/ca/nrc/cadc/doi/GetAction.java
index 68537f5..fcfa2a5 100644
--- a/doi/src/main/java/ca/nrc/cadc/doi/GetAction.java
+++ b/doi/src/main/java/ca/nrc/cadc/doi/GetAction.java
@@ -237,7 +237,7 @@ private List getOwnedDOIList() throws Exception {
ContainerNode doiRootNode = vospaceDoiClient.getContainerNode("");
if (doiRootNode != null) {
for (Node childNode : doiRootNode.getNodes()) {
- // TODO: configure doiadmin viewing of all nodes
+ // TODO: configure doi admin viewing of all nodes
NodeProperty requester = childNode.getProperty(DOI_VOS_REQUESTER_PROP);
if (requester != null && requester.getValue() != null) {
try {
diff --git a/doi/src/main/java/ca/nrc/cadc/doi/PostAction.java b/doi/src/main/java/ca/nrc/cadc/doi/PostAction.java
index c6ed3be..956250d 100644
--- a/doi/src/main/java/ca/nrc/cadc/doi/PostAction.java
+++ b/doi/src/main/java/ca/nrc/cadc/doi/PostAction.java
@@ -86,7 +86,6 @@
import ca.nrc.cadc.net.OutputStreamWrapper;
import ca.nrc.cadc.net.ResourceNotFoundException;
import ca.nrc.cadc.util.Base64;
-import ca.nrc.cadc.util.StringUtil;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -133,7 +132,7 @@ public PostAction() {
public void doAction() throws Exception {
super.init(true);
- // Do DOI creation work as doiadmin
+ // Do DOI creation work as doi admin
Subject.doAs(getAdminSubject(), (PrivilegedExceptionAction) () -> {
if (doiAction != null) {
performDoiAction();
@@ -494,7 +493,7 @@ private void verifyImmutableFields(Resource r1, Resource r2) {
String msg = String.format("namespace update is not allowed, expected: %s, actual: %s",
r2.getNamespace(), r1.getNamespace());
throw new IllegalArgumentException(msg);
- } else if (!r1.getPublisher().equals(r2.getPublisher())) {
+ } else if (!r1.getPublisher().getValue().equals(r2.getPublisher().getValue())) {
String msg = String.format("software error, publisher is different, expected: %s, actual: %s",
r2.getPublisher(), r1.getPublisher());
throw new IllegalArgumentException(msg);
@@ -523,7 +522,7 @@ private void verifyNull(Object o1, Object o2, String field) {
}
private void verifyIdentifier(Identifier i1, Identifier i2) {
- if (!i1.equals(i2)) {
+ if (!i1.getValue().equals(i2.getValue()) && !i1.getIdentifierType().equals(i2.getIdentifierType())) {
String msg = String.format("identifier update is not allowed, expected: %s, actual: %s",
i2, i1);
throw new IllegalArgumentException(msg);
@@ -543,7 +542,7 @@ private void verifyResourceType(ResourceType rt1, ResourceType rt2) {
private void setPermissions(Node node, GroupURI doiGroup) {
// Before completion, directory is visible in AstroDataCitationDOI directory, but not readable
- // except by doiadmin and calling user's group
+ // except by doi admin and calling user's group
node.isPublic = false;
// All folders will be only readable by requester
@@ -560,14 +559,14 @@ private void createDOI() throws Exception {
throw new IllegalArgumentException("No content");
}
- boolean randomName = Boolean.parseBoolean(config.getFirstPropertyValue(DoiInitAction.TEST_RANDOM_NAME_KEY));
+ boolean randomTestID = Boolean.parseBoolean(config.getFirstPropertyValue(DoiInitAction.RANDOM_TEST_ID_KEY));
String nextDoiSuffix;
- if (randomName) {
+ if (randomTestID) {
nextDoiSuffix = getRandomDOISuffix();
log.warn("Random DOI suffix: " + nextDoiSuffix);
} else {
- // Determine next DOI number
- // Note: The generated DOI number is the suffix which should be case insensitive.
+ // Determine next DOI ID
+ // Note: The generated DOI ID is the suffix which should be case insensitive.
// Since we are using a number, it does not matter. However if we decide
// to use a String, we should only generate either a lowercase or an
// uppercase String. (refer to https://support.datacite.org/docs/doi-basics)
@@ -575,10 +574,10 @@ private void createDOI() throws Exception {
log.debug("Next DOI suffix: " + nextDoiSuffix);
}
- // update the template with the new DOI number
+ // Update the resource with the DOI ID
assignIdentifier(resource.getIdentifier(), accountPrefix + "/" + nextDoiSuffix);
- //Add a Created date to the Resource object
+ // Add a Created date to the Resource object
LocalDate localDate = LocalDate.now();
String createdDate = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
Date doiDate = new Date(createdDate, DateType.CREATED);
@@ -586,17 +585,16 @@ private void createDOI() throws Exception {
resource.dates = new ArrayList<>();
resource.dates.add(doiDate);
- // Create the group that is able to administer the DOI process, use configured test GroupURI if found
- GroupURI guri;
- String configuredGroupUri = config.getFirstPropertyValue(DoiInitAction.TEST_GROUP_URI_KEY);
- if (StringUtil.hasText(configuredGroupUri)) {
- guri = new GroupURI(URI.create(configuredGroupUri));
- log.warn("Configured DOI group: " + guri);
+ // Create the group that is able to administer the DOI process
+ String groupName;
+ if (randomTestID) {
+ groupName = TEST_DOI_GROUP_PREFIX + nextDoiSuffix;
} else {
- guri = createDoiGroup(nextDoiSuffix);
- log.debug("Created DOI group: " + guri);
+ groupName = DOI_GROUP_PREFIX + nextDoiSuffix;
}
-
+ GroupURI guri = createDoiGroup(groupName);
+ log.debug("Created DOI group: " + guri);
+
// Create the VOSpace area for DOI work
ContainerNode doiFolder = createDOIDirectory(guri, nextDoiSuffix);
@@ -621,8 +619,7 @@ private void createDOI() throws Exception {
private GroupURI createDoiGroup(String groupName) throws Exception {
// Create group to use for applying permissions
- String gmsResourceID = config.getFirstPropertyValue(DoiInitAction.GMS_RESOURCE_ID_KEY);
- String group = String.format("%s?%s%s", gmsResourceID, DOI_GROUP_PREFIX, groupName);
+ String group = String.format("%s?%s", gmsResourceID, groupName);
GroupURI guri = new GroupURI(URI.create(group));
log.debug("creating group: " + guri);
@@ -639,7 +636,6 @@ private GroupURI createDoiGroup(String groupName) throws Exception {
// expose it as a server error
throw new RuntimeException(gaeex);
}
-
log.debug("doi group created: " + guri);
return guri;
}
@@ -664,7 +660,7 @@ private ContainerNode createDOIDirectory(GroupURI guri, String folderName)
ContainerNode newFolder = new ContainerNode(folderName);
// Before completion, directory is visible in AstroDataCitationDOI directory,
- // but not readable except by doiadmin and calling user's group
+ // but not readable except by doi admin and calling user's group
setPermissions(newFolder, guri);
newFolder.getProperties().addAll(properties);
diff --git a/doi/src/main/java/ca/nrc/cadc/doi/ServiceAvailability.java b/doi/src/main/java/ca/nrc/cadc/doi/ServiceAvailability.java
index 88cf3e7..01cefdc 100644
--- a/doi/src/main/java/ca/nrc/cadc/doi/ServiceAvailability.java
+++ b/doi/src/main/java/ca/nrc/cadc/doi/ServiceAvailability.java
@@ -112,7 +112,7 @@ public Availability getStatus() {
String note = "service is accepting requests";
try {
MultiValuedProperties config = DoiInitAction.getConfig();
- URI vaultResourceID = URI.create(config.getFirstPropertyValue(DoiInitAction.VAULT_RESOURCE_ID_KEY));
+ URI vaultResourceID = DoiInitAction.getVospaceResourceID(config);
log.debug("vault resourceID: " + vaultResourceID);
// check other services we depend on (vault, gms, datacite)