package com.constellio.app.api.cmis.accept; import static com.constellio.data.conf.HashingEncoding.BASE64; import static com.constellio.model.entities.security.global.UserCredentialStatus.ACTIVE; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.chemistry.opencmis.client.api.CmisObject; import org.apache.chemistry.opencmis.client.api.Document; import org.apache.chemistry.opencmis.client.api.Folder; import org.apache.chemistry.opencmis.client.api.Session; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.enums.VersioningState; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; import org.joda.time.LocalDateTime; import org.junit.Before; import org.junit.Test; import com.constellio.model.entities.Taxonomy; import com.constellio.model.entities.records.Content; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.entities.security.global.UserCredential; import com.constellio.model.services.contents.ContentManager; import com.constellio.model.services.records.RecordServices; import com.constellio.model.services.records.RecordServicesException; import com.constellio.model.services.schemas.MetadataSchemasManager; import com.constellio.model.services.taxonomies.TaxonomiesManager; import com.constellio.model.services.users.UserServices; import com.constellio.sdk.tests.ConstellioTest; import com.constellio.sdk.tests.TestRecord; import com.constellio.sdk.tests.annotations.DriverTest; import com.constellio.sdk.tests.schemas.TestsSchemasSetup; import com.constellio.sdk.tests.schemas.TestsSchemasSetup.ZeSchemaMetadatas; @DriverTest public class CmisMultivalueContentManagementAcceptTest extends ConstellioTest { private final String PDF_MIMETYPE = "application/pdf"; private final String DOCX_MIMETYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; LocalDateTime firstDocumentModificationOClock = new LocalDateTime(); LocalDateTime documentCreationOClock = new LocalDateTime().plusHours(1); LocalDateTime firstDocumentCheckOutOClock = new LocalDateTime().plusHours(2); LocalDateTime firstDocumentCheckOutCheckInOClock = new LocalDateTime().plusHours(4); LocalDateTime deleteContentOClock = new LocalDateTime().plusHours(8); ContentManager contentManager; RecordServices recordServices; TestsSchemasSetup schemas = new TestsSchemasSetup(zeCollection); ZeSchemaMetadatas zeSchema = schemas.new ZeSchemaMetadatas(); User bob; User alice; String bobId; String aliceId; String zeRecord = "zeRecord"; Session session; String currentUserId; String zeRecordContentId; private long pdf1Length = 170039L; private long pdf2Length = 167347L; private long docx1Length = 27055L; private long docx2Length = 27325L; private String pdf1Hash = "KN8RjbrnBgq1EDDV2U71a6/6gd4="; private String pdf2Hash = "T+4zq4cGP/tXkdJp/qz1WVWYhoQ="; private String docx1Hash = "Fss7pKBafi8ok5KaOwEpmNdeGCE="; private String docx2Hash = "TIKwSvHOXHOOtRd1K9t2fm4TQ4I="; private String aliceToken, bobToken; @Before public void setUp() throws Exception { givenHashingEncodingIs(BASE64); givenTimeIs(firstDocumentModificationOClock); defineSchemasManager().using(schemas.withAMultivalueContentMetadata()); CmisAcceptanceTestSetup.allSchemaTypesSupported(getAppLayerFactory()); MetadataSchemasManager metadataSchemasManager = getModelLayerFactory().getMetadataSchemasManager(); TaxonomiesManager taxonomiesManager = getModelLayerFactory().getTaxonomiesManager(); Taxonomy taxonomy = Taxonomy.createPublic("taxo", "taxo", zeCollection, asList("zeSchemaType")); taxonomiesManager.addTaxonomy(taxonomy, metadataSchemasManager); taxonomiesManager.setPrincipalTaxonomy(taxonomy, metadataSchemasManager); UserServices userServices = getModelLayerFactory().newUserServices(); userServices.addUpdateUserCredential(userServices.createUserCredential( "bob", "bob", "gratton", "bob@doculibre.com", new ArrayList<String>(), asList(zeCollection), ACTIVE, null, Arrays.asList(""), null).withServiceKey("bob_key").withSystemAdminPermission()); userServices.addUpdateUserCredential(userServices.createUserCredential( "alice", "alice", "wonderland", "alice@doculibre.com", new ArrayList<String>(), asList(zeCollection), ACTIVE, null, Arrays.asList(""), null).withServiceKey("alice_key").withSystemAdminPermission()); alice = userServices.getUserInCollection("alice", zeCollection); bob = userServices.getUserInCollection("bob", zeCollection); aliceToken = userServices.generateToken("alice"); bobToken = userServices.generateToken("bob"); aliceId = alice.getId(); bobId = bob.getId(); recordServices = getModelLayerFactory().newRecordServices(); contentManager = getModelLayerFactory().getContentManager(); alice.setCollectionDeleteAccess(true); bob.setCollectionDeleteAccess(true); alice.setCollectionWriteAccess(true); bob.setCollectionWriteAccess(true); recordServices.update(alice.getWrappedRecord()); recordServices.update(bob.getWrappedRecord()); CmisAcceptanceTestSetup.giveUseCMISPermissionToUsers(getModelLayerFactory()); } @Test public void givenMultiValueContentThenCanManageItWithCmis() throws Exception { givenCurrentUserIsAlice(); givenRecord(); session = sessionFor(alice); Folder folder = (Folder) session.getObject(zeRecord); assertThat(getContentsOf(zeRecord)).isEmpty(); givenTimeIs(documentCreationOClock); givenCurrentUserIsAlice(); _1_documentCreation(folder); givenTimeIs(firstDocumentModificationOClock); givenCurrentUserIsBob(); folder = (Folder) session.getObject(zeRecord); _2_addingAnotherDocument(folder); givenTimeIs(firstDocumentCheckOutOClock); givenCurrentUserIsAlice(); _3_firstDocumentCheckOut(); givenTimeIs(firstDocumentCheckOutCheckInOClock); givenCurrentUserIsAlice(); _5_firstDocumentCheckOutCheckIn(); givenTimeIs(deleteContentOClock); givenCurrentUserIsBob(); _9_deleteOneContent(); } private void _1_documentCreation(Folder folder) throws IOException { Map<String, Object> properties = new HashMap<>(); properties.put("metadata", "contentMetadata"); properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); Document cmisContent1 = folder.createDocument(properties, pdf1ContentStream(), VersioningState.MAJOR); Document cmisContent2 = folder.createDocument(properties, docx1ContentStream(), VersioningState.MAJOR); List<Content> contents = recordServices.getDocumentById(zeRecord).getList(zeSchema.contentMetadata()); String firstContentId = contents.get(0).getId(); String secondContentId = contents.get(1).getId(); assertThat(cmisContent1.getId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + firstContentId + "_1.0"); assertThat(cmisContent2.getId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + secondContentId + "_1.0"); List<Document> cmisContents = getContentsOf(zeRecord); assertThatContentStreamIsSameAs(cmisContents.get(0), pdf1ContentStream(), pdf1Hash); assertThatVersionCreationInfoAre(cmisContents.get(0), aliceId, documentCreationOClock); assertThat(cmisContents.get(0).getId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + firstContentId + "_1.0"); assertThat(cmisContents.get(0).getProperty(PropertyIds.PARENT_ID).getValue()).isEqualTo(zeRecord); assertThatContentStreamIsSameAs(cmisContents.get(1), docx1ContentStream(), docx1Hash); assertThatVersionCreationInfoAre(cmisContents.get(1), aliceId, documentCreationOClock); assertThat(cmisContents.get(1).getId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + secondContentId + "_1.0"); assertThat(cmisContents.get(1).getProperty(PropertyIds.PARENT_ID).getValue()).isEqualTo(zeRecord); } private void _2_addingAnotherDocument(Folder folder) throws IOException { Map<String, Object> properties = new HashMap<>(); properties.put("metadata", "contentMetadata"); properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); Document cmisContent1 = folder.createDocument(properties, pdf2ContentStream(), VersioningState.MAJOR); List<Content> contents = recordServices.getDocumentById(zeRecord).getList(zeSchema.contentMetadata()); zeRecordContentId = contents.get(2).getId(); assertThat(cmisContent1.getId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + zeRecordContentId + "_1.0"); List<Document> cmisContents = getContentsOf(zeRecord); assertThat(cmisContents).hasSize(3); assertThat(cmisContent1.getId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + zeRecordContentId + "_1.0"); assertThatContentStreamIsSameAs(cmisContents.get(2), pdf2ContentStream(), pdf2Hash); assertThatVersionCreationInfoAre(cmisContents.get(2), bobId, firstDocumentModificationOClock); assertThat(cmisContents.get(2).getId()).isEqualTo( "content_" + zeRecord + "_contentMetadata_" + zeRecordContentId + "_1.0"); assertThat(cmisContents.get(2).getProperty(PropertyIds.PARENT_ID).getValue()).isEqualTo(zeRecord); } private void _3_firstDocumentCheckOut() throws IOException { Document cmisContent = getContentsOf(zeRecord).get(2); String privateWorkingCopyId = cmisContent.checkOut().getId(); assertThat(privateWorkingCopyId).isEqualTo("content_" + zeRecord + "_contentMetadata_" + zeRecordContentId + "_co"); //Therefore, until it is checked in (using the checkIn service), the PWC MUST NOT be considered // the latest or latest major version in the version series. That is, the values of the // cmis:isLatestVersion and cmis:isLatestMajorVersion properties MUST be FALSE. cmisContent = getContentsOf(zeRecord).get(2); assertThatContentStreamIsSameAs(cmisContent, pdf2ContentStream(), pdf2Hash); assertThatVersionCreationInfoAre(cmisContent, bobId, firstDocumentModificationOClock); assertThat(cmisContent.getId()).isEqualTo(privateWorkingCopyId); assertThat(cmisContent.getProperty(PropertyIds.PARENT_ID).getValue()).isEqualTo(zeRecord); assertThat(cmisContent.getVersionLabel()).isEqualTo("1.0"); assertThat(cmisContent.getVersionSeriesCheckedOutBy()).isEqualTo(aliceId); assertThat(cmisContent.getVersionSeriesCheckedOutId()).isEqualTo(privateWorkingCopyId); assertThat(cmisContent.getVersionSeriesId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + zeRecordContentId); assertThat(cmisContent.isLatestMajorVersion()).isFalse(); assertThat(cmisContent.isLatestVersion()).isFalse(); assertThat(cmisContent.isMajorVersion()).isFalse(); //assertThat(cmisContent.isPrivateWorkingCopy()).isTrue(); assertThat(cmisContent.isVersionSeriesCheckedOut()).isTrue(); assertThat(cmisContent.getPropertyValue("metadata")).isEqualTo("contentMetadata"); List<Document> allVersions = cmisContent.getAllVersions(); assertThat(allVersions).hasSize(2); assertThatContentStreamIsSameAs(allVersions.get(0), pdf2ContentStream(), pdf2Hash); assertThatVersionCreationInfoAre(allVersions.get(0), bobId, firstDocumentModificationOClock); assertThat(allVersions.get(0).getId()).isEqualTo(privateWorkingCopyId); assertThat(allVersions.get(0).getPropertyValue(PropertyIds.PARENT_ID)).isEqualTo(zeRecord); try { cmisContent.checkOut(); fail("Bob was able to check out a document which is already checked out by alice"); } catch (Exception e) { //OK } } private void _5_firstDocumentCheckOutCheckIn() throws IOException { Document cmisContent = getContentsOf(zeRecord).get(2); String versionId = cmisContent.checkIn(true, new HashMap<String, Object>(), null, null).getId(); assertThat(versionId).isEqualTo("content_" + zeRecord + "_contentMetadata_" + zeRecordContentId + "_1.0"); cmisContent = getContentsOf(zeRecord).get(2); assertThatContentStreamIsSameAs(cmisContent, pdf2ContentStream(), pdf2Hash); assertThatVersionCreationInfoAre(cmisContent, bobId, firstDocumentModificationOClock); assertThat(cmisContent.getId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + zeRecordContentId + "_1.0"); assertThat(cmisContent.getPropertyValue(PropertyIds.PARENT_ID)).isEqualTo(zeRecord); assertThat(cmisContent.getVersionLabel()).isEqualTo("1.0"); assertThat(cmisContent.getVersionSeriesCheckedOutBy()).isNull(); assertThat(cmisContent.getVersionSeriesCheckedOutId()).isNull(); assertThat(cmisContent.getVersionSeriesId()).isEqualTo("content_" + zeRecord + "_contentMetadata_" + zeRecordContentId); assertThat(cmisContent.isLatestMajorVersion()).isTrue(); assertThat(cmisContent.isLatestVersion()).isTrue(); assertThat(cmisContent.isMajorVersion()).isTrue(); //assertThat(cmisContent.isPrivateWorkingCopy()).isFalse(); assertThat(cmisContent.isVersionSeriesCheckedOut()).isFalse(); assertThat(cmisContent.getPropertyValue("metadata")).isEqualTo("contentMetadata"); } private void _9_deleteOneContent() { List<Document> cmisContents = getContentsOf(zeRecord); cmisContents.get(2).deleteAllVersions(); assertThat(getContentsOf(zeRecord)).hasSize(2); } private ContentStream pdf1ContentStream() throws IOException { String filename = "pdf1.pdf"; BigInteger length = BigInteger.valueOf(pdf1Length); String mimetype = PDF_MIMETYPE; InputStream stream = getTestResourceInputStreamFactory("pdf1.pdf").create(SDK_STREAM); return new ContentStreamImpl(filename, length, mimetype, stream); } private ContentStream pdf2ContentStream() throws IOException { String filename = "pdf2.pdf"; BigInteger length = BigInteger.valueOf(pdf2Length); String mimetype = PDF_MIMETYPE; InputStream stream = getTestResourceInputStreamFactory("pdf2.pdf").create(SDK_STREAM); return new ContentStreamImpl(filename, length, mimetype, stream); } private ContentStream docx1ContentStream() throws IOException { String filename = "docx1.docx"; BigInteger length = BigInteger.valueOf(docx1Length); String mimetype = DOCX_MIMETYPE; InputStream stream = getTestResourceInputStreamFactory("docx1.docx").create(SDK_STREAM); return new ContentStreamImpl(filename, length, mimetype, stream); } private void givenCurrentUserIsAlice() { session = sessionFor(alice); } private void givenCurrentUserIsBob() { session = sessionFor(bob); } private void assertThatVersionCreationInfoAre(Document document, String userId, LocalDateTime dateTime) { assertThat(document.getCreationDate().getTime()).isEqualToIgnoringMillis(dateTime.toDate()); assertThat(document.getCreatedBy()).isEqualTo(userId); assertThat(document.getLastModificationDate().getTime()).isEqualToIgnoringMillis(dateTime.toDate()); assertThat(document.getLastModifiedBy()).isEqualTo(userId); } private void assertThatContentStreamIsSameAs(Document document, ContentStream stream, String hash) { ContentStream contentStream = document.getContentStream(); assertThat(contentStream.getMimeType()).isEqualTo(stream.getMimeType()); // OK assertThat(contentStream.getFileName()).isEqualTo(stream.getFileName()); // OK assertThat(document.getContentStreamLength()).isEqualTo(stream.getLength()); // ContentStream lenghts are accessible in the document, not needed in the stream itself. assertThat(contentStream.getBigLength()).isNull(); assertThat(contentStream.getLength()).isEqualTo(-1); assertThat(contentStream.getStream()).hasContentEqualTo(stream.getStream()); // OK // All of those are MISSING assertThat(document.getContentStreamId()).isEqualTo(hash); assertThat(document.getContentStreamFileName()).isEqualTo(stream.getFileName()); assertThat(document.getContentStreamLength()).isEqualTo(stream.getLength()); assertThat(document.getContentStreamMimeType()).isEqualTo(stream.getMimeType()); } private List<Document> getContentsOf(String id) { List<Document> objectList = new ArrayList<>(); Folder folder = (Folder) session.getObject(id); Iterator<CmisObject> iterator = folder.getChildren().iterator(); while (iterator.hasNext()) { CmisObject object = iterator.next(); if (object instanceof Document) { objectList.add((Document) object); } } return objectList; } private String givenRecord() throws RecordServicesException { Record record = new TestRecord(zeSchema, "zeRecord"); recordServices.add(record); return record.getId(); } private Session sessionFor(User user) { UserCredential userCredential = getModelLayerFactory().newUserServices().getUserCredential(user.getUsername()); currentUserId = user.getUsername(); String token = user.getUsername().equals("alice") ? aliceToken : bobToken; return newCmisSessionBuilder().authenticatedBy(userCredential.getServiceKey(), token).onCollection(zeCollection).build(); } }