001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.kernel.impl.services; 019 020import static java.util.Arrays.asList; 021import static java.util.Collections.singleton; 022import static org.fcrepo.kernel.api.RdfLexicon.BASIC_CONTAINER; 023import static org.fcrepo.kernel.api.RdfLexicon.CONTAINER; 024import static org.fcrepo.kernel.api.RdfLexicon.CREATED_BY; 025import static org.fcrepo.kernel.api.RdfLexicon.CREATED_DATE; 026import static org.fcrepo.kernel.api.RdfLexicon.DEFAULT_INTERACTION_MODEL; 027import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_RESOURCE; 028import static org.fcrepo.kernel.api.RdfLexicon.LAST_MODIFIED_BY; 029import static org.fcrepo.kernel.api.RdfLexicon.LAST_MODIFIED_DATE; 030import static org.fcrepo.kernel.api.RdfLexicon.MEMENTO_TYPE; 031import static org.fcrepo.kernel.api.RdfLexicon.NON_RDF_SOURCE; 032import static org.fcrepo.kernel.api.RdfLexicon.RESOURCE; 033import static org.junit.Assert.assertEquals; 034import static org.junit.Assert.assertNotEquals; 035import static org.junit.Assert.assertTrue; 036import static org.mockito.Mockito.times; 037import static org.mockito.Mockito.verify; 038import static org.mockito.Mockito.when; 039import static org.springframework.test.util.ReflectionTestUtils.setField; 040 041import java.io.File; 042import java.net.URI; 043import java.nio.charset.StandardCharsets; 044import java.nio.file.Files; 045import java.time.Instant; 046import java.util.ArrayList; 047import java.util.Arrays; 048import java.util.Calendar; 049import java.util.Collection; 050import java.util.Date; 051import java.util.List; 052import java.util.UUID; 053 054import javax.inject.Inject; 055 056import org.fcrepo.config.FedoraPropsConfig; 057import org.fcrepo.config.ServerManagedPropsMode; 058import org.fcrepo.kernel.api.ContainmentIndex; 059import org.fcrepo.kernel.api.Transaction; 060import org.fcrepo.kernel.api.exception.InteractionModelViolationException; 061import org.fcrepo.kernel.api.exception.RequestWithAclLinkHeaderException; 062import org.fcrepo.kernel.api.identifiers.FedoraId; 063import org.fcrepo.kernel.api.models.ExternalContent; 064import org.fcrepo.kernel.api.models.FedoraResource; 065import org.fcrepo.kernel.api.models.ResourceHeaders; 066import org.fcrepo.kernel.api.observer.EventAccumulator; 067import org.fcrepo.kernel.api.operations.CreateRdfSourceOperation; 068import org.fcrepo.kernel.api.operations.NonRdfSourceOperation; 069import org.fcrepo.kernel.api.operations.NonRdfSourceOperationFactory; 070import org.fcrepo.kernel.api.operations.RdfSourceOperation; 071import org.fcrepo.kernel.api.operations.RdfSourceOperationFactory; 072import org.fcrepo.kernel.api.operations.ResourceOperation; 073import org.fcrepo.kernel.api.services.MembershipService; 074import org.fcrepo.kernel.api.services.ReferenceService; 075import org.fcrepo.kernel.impl.TestTransactionHelper; 076import org.fcrepo.kernel.impl.operations.CreateNonRdfSourceOperation; 077import org.fcrepo.kernel.impl.operations.NonRdfSourceOperationFactoryImpl; 078import org.fcrepo.kernel.impl.operations.RdfSourceOperationFactoryImpl; 079import org.fcrepo.persistence.api.PersistentStorageSession; 080import org.fcrepo.persistence.api.PersistentStorageSessionManager; 081import org.fcrepo.persistence.api.exceptions.PersistentItemNotFoundException; 082import org.fcrepo.search.api.SearchIndex; 083 084import org.apache.commons.io.FileUtils; 085import org.apache.jena.datatypes.xsd.XSDDateTime; 086import org.apache.jena.rdf.model.Model; 087import org.apache.jena.rdf.model.ModelFactory; 088import org.flywaydb.test.annotation.FlywayTest; 089import org.junit.Before; 090import org.junit.Rule; 091import org.junit.Test; 092import org.junit.rules.TemporaryFolder; 093import org.junit.runner.RunWith; 094import org.mockito.ArgumentCaptor; 095import org.mockito.ArgumentMatchers; 096import org.mockito.Captor; 097import org.mockito.InjectMocks; 098import org.mockito.Mock; 099import org.mockito.MockitoAnnotations; 100import org.springframework.test.context.ContextConfiguration; 101import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 102 103/** 104 * @author bseeger 105 */ 106@RunWith(SpringJUnit4ClassRunner.class) 107@ContextConfiguration("/containmentIndexTest.xml") 108public class CreateResourceServiceImplTest { 109 110 private static final List<String> STRING_TYPES_NOT_VALID = Arrays.asList(MEMENTO_TYPE, RESOURCE.toString(), 111 FEDORA_RESOURCE.toString()); 112 113 private static final List<String> STRING_TYPES_VALID = Arrays.asList(MEMENTO_TYPE, CONTAINER.toString(), 114 BASIC_CONTAINER.toString()); 115 116 private final String defaultInteractionModel = DEFAULT_INTERACTION_MODEL.toString(); 117 118 @Rule 119 public TemporaryFolder tempFolder = new TemporaryFolder(); 120 121 @Mock 122 private PersistentStorageSessionManager psManager; 123 124 private RdfSourceOperationFactory rdfSourceOperationFactory; 125 126 private NonRdfSourceOperationFactory nonRdfSourceOperationFactory; 127 128 @Inject 129 private ContainmentIndex containmentIndex; 130 131 @Mock 132 private SearchIndex searchIndex; 133 134 @Mock 135 private PersistentStorageSession psSession; 136 137 @Mock 138 private ResourceHeaders resourceHeaders; 139 140 @Mock 141 private ExternalContent extContent; 142 143 @Mock 144 private FedoraResource fedoraResource; 145 146 private Transaction transaction; 147 148 @Mock 149 private ReferenceService referenceService; 150 151 @Mock 152 private MembershipService membershipService; 153 154 @Captor 155 private ArgumentCaptor<ResourceOperation> operationCaptor; 156 157 @Mock 158 private EventAccumulator eventAccumulator; 159 160 @InjectMocks 161 private CreateResourceServiceImpl createResourceService; 162 163 private static final String TX_ID = "tx1234"; 164 165 private static final String USER_PRINCIPAL = "fedoraUser"; 166 167 private static final String FILENAME = "file.html"; 168 169 private static final String CONTENT_TYPE = "text/html"; 170 171 private static final Long CONTENT_SIZE = 100L; 172 173 private static final String EXTERNAL_CONTENT_TYPE = "text/plain"; 174 175 private final Model model = ModelFactory.createDefaultModel(); 176 177 private static final Collection<URI> DIGESTS = singleton(URI.create("urn:sha1:12345abced")); 178 179 private static final List<FedoraId> cleanupList = new ArrayList<>() ; 180 181 private final FedoraId rootId = FedoraId.getRepositoryRootId(); 182 183 private FedoraPropsConfig propsConfig = new FedoraPropsConfig(); 184 185 @Before 186 @FlywayTest 187 public void setUp() { 188 MockitoAnnotations.openMocks(this); 189 rdfSourceOperationFactory = new RdfSourceOperationFactoryImpl(); 190 setField(createResourceService, "rdfSourceOperationFactory", rdfSourceOperationFactory); 191 nonRdfSourceOperationFactory = new NonRdfSourceOperationFactoryImpl(); 192 setField(createResourceService, "nonRdfSourceOperationFactory", nonRdfSourceOperationFactory); 193 setField(createResourceService, "containmentIndex", containmentIndex); 194 setField(createResourceService, "searchIndex", searchIndex); 195 setField(createResourceService, "eventAccumulator", eventAccumulator); 196 setField(createResourceService, "referenceService", referenceService); 197 setField(createResourceService, "membershipService", membershipService); 198 setField(createResourceService, "fedoraPropsConfig", propsConfig); 199 propsConfig.setServerManagedPropsMode(ServerManagedPropsMode.STRICT); 200 when(psManager.getSession(ArgumentMatchers.any())).thenReturn(psSession); 201 transaction = TestTransactionHelper.mockTransaction(TX_ID, false); 202 } 203 204 /** 205 * Test trying to add a child to a non-existant parent. 206 * We recursive to repository root for a parent, so this is now just creating a ghost node? 207 */ 208 @Test 209 public void testNoParentRdf() throws Exception { 210 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 211 final FedoraId childId = fedoraId.resolve("child"); 212 when(psSession.getHeaders(fedoraId, null)).thenThrow(PersistentItemNotFoundException.class); 213 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, model); 214 verify(transaction).lockResource(childId); 215 } 216 217 @Test 218 public void testParentAg() { 219 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 220 final FedoraId childId = fedoraId.resolve("child"); 221 containmentIndex.addContainedBy(transaction, FedoraId.getRepositoryRootId(), fedoraId); 222 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 223 when(resourceHeaders.isArchivalGroup()).thenReturn(true); 224 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, model); 225 verify(transaction).lockResource(fedoraId); 226 verify(transaction).lockResource(childId); 227 } 228 229 @Test 230 public void testParentContainerInAg() { 231 final FedoraId agId = FedoraId.create(UUID.randomUUID().toString()); 232 final FedoraId fedoraId = agId.resolve(UUID.randomUUID().toString()); 233 final FedoraId childId = fedoraId.resolve("child"); 234 containmentIndex.addContainedBy(transaction, agId, fedoraId); 235 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 236 when(resourceHeaders.isArchivalGroup()).thenReturn(false); 237 when(resourceHeaders.getArchivalGroupId()).thenReturn(agId); 238 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, model); 239 verify(transaction).lockResource(agId); 240 verify(transaction).lockResource(childId); 241 } 242 243 /** 244 * Test creating a RDFSource with a NonRDFSource parent. 245 */ 246 @Test(expected = InteractionModelViolationException.class) 247 public void testParentIsBinaryRdf() throws Exception { 248 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 249 final FedoraId childId = fedoraId.resolve("child"); 250 containmentIndex.addContainedBy(transaction, rootId, fedoraId); 251 when(resourceHeaders.getInteractionModel()).thenReturn(NON_RDF_SOURCE.toString()); 252 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 253 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, model); 254 } 255 256 /** 257 * Test creating a NonRDFSource with a NonRDFSource parent. 258 */ 259 @Test(expected = InteractionModelViolationException.class) 260 public void testParentIsBinary() throws Exception { 261 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 262 final FedoraId childId = fedoraId.resolve("child"); 263 containmentIndex.addContainedBy(transaction, rootId, fedoraId); 264 when(resourceHeaders.getInteractionModel()).thenReturn(NON_RDF_SOURCE.toString()); 265 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 266 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, FILENAME, CONTENT_SIZE, null, 267 DIGESTS, null, null); 268 } 269 270 /** 271 * Test creating an external NonRDFSource with a NonRDFSource parent. 272 * TODO: put/post to a binary parent is tested above, might be a duplicate. 273 */ 274 @Test(expected = InteractionModelViolationException.class) 275 public void testParentIsExternal() throws Exception { 276 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 277 final FedoraId childId = fedoraId.resolve("child"); 278 containmentIndex.addContainedBy(transaction, rootId, fedoraId); 279 when(resourceHeaders.getInteractionModel()).thenReturn(NON_RDF_SOURCE.toString()); 280 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 281 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, FILENAME, CONTENT_SIZE, null, 282 DIGESTS, null, extContent); 283 } 284 285 /** 286 * Test creating a RDFSource with a RDFSource parent. 287 */ 288 @Test 289 public void testParentIsRdf() throws Exception { 290 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 291 final FedoraId childId = fedoraId.resolve("child"); 292 containmentIndex.addContainedBy(transaction, rootId, fedoraId); 293 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 294 when(resourceHeaders.getInteractionModel()).thenReturn(BASIC_CONTAINER.toString()); 295 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, model); 296 cleanupList.add(fedoraId); 297 verify(psSession).persist(operationCaptor.capture()); 298 final FedoraId persistedId = operationCaptor.getValue().getResourceId(); 299 assertNotEquals(fedoraId, persistedId); 300 assertTrue(persistedId.getFullId().startsWith(fedoraId.getFullId())); 301 assertEquals(1, containmentIndex.getContains(transaction, fedoraId).count()); 302 303 verify(transaction).lockResource(childId); 304 } 305 306 /** 307 * Test creating a NonRDFSource with a RDFSource parent. 308 */ 309 @Test 310 public void testParentIsRdfBinary() throws Exception { 311 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 312 final FedoraId childId = fedoraId.resolve("child"); 313 containmentIndex.addContainedBy(transaction, rootId, fedoraId); 314 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 315 when(resourceHeaders.getInteractionModel()).thenReturn(BASIC_CONTAINER.toString()); 316 createResourceService.perform(transaction, USER_PRINCIPAL, childId, CONTENT_TYPE, 317 FILENAME, CONTENT_SIZE, null, DIGESTS, null, null); 318 cleanupList.add(fedoraId); 319 verify(psSession, times(2)).persist(operationCaptor.capture()); 320 final List<ResourceOperation> operations = operationCaptor.getAllValues(); 321 final var operation = getOperation(operations, CreateNonRdfSourceOperation.class); 322 final FedoraId persistedId = operation.getResourceId(); 323 assertNotEquals(fedoraId, persistedId); 324 assertTrue(persistedId.getFullId().startsWith(fedoraId.getFullId())); 325 assertBinaryPropertiesPresent(operation); 326 assertEquals(fedoraId, operation.getParentId()); 327 assertEquals(1, containmentIndex.getContains(transaction, fedoraId).count()); 328 verify(transaction).lockResource(childId); 329 } 330 331 /** 332 * Test setting some system properties only accessible in relaxed mode. 333 */ 334 @Test 335 public void testRdfSetRelaxedProperties_Post() throws Exception { 336 final var createdDate = Instant.parse("2019-11-12T10:00:30.0Z"); 337 final var calendar = Calendar.getInstance(); 338 calendar.setTime(Date.from(createdDate)); 339 final var createdDateXsd = new XSDDateTime(calendar); 340 final var lastModifiedDate = Instant.parse("2019-11-12T14:11:05.0Z"); 341 calendar.setTime(Date.from(lastModifiedDate)); 342 final var lastModifiedDateXsd = new XSDDateTime(calendar); 343 final String relaxedUser = "relaxedUser"; 344 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 345 final FedoraId childId = fedoraId.resolve("testSlug"); 346 containmentIndex.addContainedBy(transaction, rootId, fedoraId); 347 348 final var resc = model.getResource(childId.getFullId()); 349 resc.addLiteral(LAST_MODIFIED_DATE, lastModifiedDateXsd); 350 resc.addLiteral(LAST_MODIFIED_BY, relaxedUser); 351 resc.addLiteral(CREATED_DATE, createdDateXsd); 352 resc.addLiteral(CREATED_BY, relaxedUser); 353 354 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 355 when(psSession.getHeaders(childId, null)).thenReturn(resourceHeaders); 356 357 when(resourceHeaders.getInteractionModel()).thenReturn(BASIC_CONTAINER.toString()); 358 propsConfig.setServerManagedPropsMode(ServerManagedPropsMode.RELAXED); 359 createResourceService.perform(transaction, USER_PRINCIPAL, childId, null, model); 360 cleanupList.add(fedoraId); 361 362 verify(psSession).persist(operationCaptor.capture()); 363 364 final var operation = operationCaptor.getValue(); 365 final FedoraId persistedId = operation.getResourceId(); 366 assertNotEquals(fedoraId, persistedId); 367 assertTrue(persistedId.getFullId().startsWith(fedoraId.getFullId())); 368 assertEquals(persistedId, childId); 369 370 final var rdfOp = (RdfSourceOperation) operation; 371 assertEquals(relaxedUser, rdfOp.getCreatedBy()); 372 assertEquals(relaxedUser, rdfOp.getLastModifiedBy()); 373 assertEquals(createdDate, rdfOp.getCreatedDate()); 374 assertEquals(lastModifiedDate, rdfOp.getLastModifiedDate()); 375 assertEquals(1, containmentIndex.getContains(transaction, fedoraId).count()); 376 verify(transaction).lockResource(childId); 377 } 378 379 /** 380 * This test now seems to ensure that the createResourceService will overwrite an existing object 381 * TODO: Review expectations 382 */ 383 @Test 384 public void testWithBinary() throws Exception { 385 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 386 final FedoraId childId = fedoraId.resolve("testSlug"); 387 containmentIndex.addContainedBy(transaction, FedoraId.getRepositoryRootId(), fedoraId); 388 containmentIndex.commitTransaction(transaction); 389 when(psSession.getHeaders(fedoraId, null)).thenReturn(resourceHeaders); 390 when(psSession.getHeaders(childId, null)).thenReturn(resourceHeaders); 391 when(resourceHeaders.getInteractionModel()).thenReturn(BASIC_CONTAINER.toString()); 392 createResourceService.perform(transaction, USER_PRINCIPAL, childId, 393 CONTENT_TYPE, FILENAME, CONTENT_SIZE, null, DIGESTS, null, null); 394 cleanupList.add(fedoraId); 395 verify(psSession, times(2)).persist(operationCaptor.capture()); 396 final List<ResourceOperation> operations = operationCaptor.getAllValues(); 397 final var operation = getOperation(operations, CreateNonRdfSourceOperation.class); 398 final FedoraId persistedId = operation.getResourceId(); 399 assertNotEquals(fedoraId, persistedId); 400 assertEquals(childId, persistedId); 401 assertTrue(persistedId.getFullId().startsWith(fedoraId.getFullId())); 402 assertBinaryPropertiesPresent(operation); 403 assertEquals(fedoraId, operation.getParentId()); 404 405 final var descOperation = getOperation(operations, CreateRdfSourceOperation.class); 406 assertEquals(persistedId.asDescription(), descOperation.getResourceId()); 407 assertEquals(1, containmentIndex.getContains(transaction, fedoraId).count()); 408 verify(transaction).lockResource(childId); 409 } 410 411 @Test 412 public void testSendingValidInteractionModel() { 413 // If you provide a valid interaction model, you should always get it back. 414 final String expected = BASIC_CONTAINER.toString(); 415 final String model1 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, false, false, false); 416 assertEquals(expected, model1); 417 final String model2 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, false, false, true); 418 assertEquals(expected, model2); 419 final String model3 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, false, true, false); 420 assertEquals(expected, model3); 421 final String model4 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, false, true, true); 422 assertEquals(expected, model4); 423 final String model5 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, true, false, false); 424 assertEquals(expected, model5); 425 final String model6 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, true, false, true); 426 assertEquals(expected, model6); 427 final String model7 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, true, true, false); 428 assertEquals(expected, model7); 429 final String model8 = createResourceService.determineInteractionModel(STRING_TYPES_VALID, true, true, true); 430 assertEquals(expected, model8); 431 } 432 433 @Test 434 public void testSendingInvalidInteractionModelIsNotRdf() { 435 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, false, false, 436 false); 437 assertEquals(defaultInteractionModel, model); 438 } 439 440 @Test 441 public void testNotRdfNoContentIsExternal() { 442 final String expected = NON_RDF_SOURCE.toString(); 443 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, false, false, 444 true); 445 assertEquals(expected, model); 446 } 447 448 @Test 449 public void testNotRdfContentPresentNotExternal() { 450 final String expected = NON_RDF_SOURCE.toString(); 451 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, false, true, 452 false); 453 assertEquals(expected, model); 454 } 455 456 @Test 457 public void testNotRdfContentPresentIsExternal() { 458 final String expected = NON_RDF_SOURCE.toString(); 459 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, false, true, true); 460 assertEquals(expected, model); 461 } 462 463 @Test 464 public void testIsRdfNoContentNotExternal() { 465 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, true, false, 466 false); 467 assertEquals(defaultInteractionModel, model); 468 } 469 470 @Test 471 public void testIsRdfNoContentIsExternal() { 472 final String expected = NON_RDF_SOURCE.toString(); 473 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, true, false, true); 474 assertEquals(expected, model); 475 } 476 477 @Test 478 public void testIsRdfHasContentNotExternal() { 479 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, true, true, false); 480 assertEquals(defaultInteractionModel, model); 481 } 482 483 @Test 484 public void testIsRdfHasContentIsExternal() { 485 final String expected = NON_RDF_SOURCE.toString(); 486 final String model = createResourceService.determineInteractionModel(STRING_TYPES_NOT_VALID, true, true, true); 487 assertEquals(expected, model); 488 } 489 490 @Test(expected = RequestWithAclLinkHeaderException.class) 491 public void testCheckAclLinkHeaderFailDblQ() throws Exception { 492 final List<String> links = Arrays.asList("<" + NON_RDF_SOURCE.toString() + ">; rel=\"type\"", 493 "<http" + "://example.org/some/location/image.tiff>; " + "rel=\"http://fedora" + 494 ".info/definitions/fcrepo#ExternalContent\"; " + "handling=\"proxy\"; type=\"image/tiff\"", 495 "<http" + "://example.org/some/otherlocation>; rel=\"acl\""); 496 createResourceService.checkAclLinkHeader(links); 497 } 498 499 @Test(expected = RequestWithAclLinkHeaderException.class) 500 public void testCheckAclLinkHeaderFailSingleQ() throws Exception { 501 final List<String> links = Arrays.asList("<" + NON_RDF_SOURCE.toString() + ">; rel=\"type\"", 502 "<http" + "://example.org/some/location/image.tiff>; " + "rel=\"http://fedora" + 503 ".info/definitions/fcrepo#ExternalContent\"; " + "handling=\"proxy\"; type=\"image/tiff\"", 504 "<http" + "://example.org/some/otherlocation>; rel='acl'"); 505 createResourceService.checkAclLinkHeader(links); 506 } 507 508 @Test(expected = RequestWithAclLinkHeaderException.class) 509 public void testCheckAclLinkHeaderFailNoQ() throws Exception { 510 final List<String> links = Arrays.asList("<" + NON_RDF_SOURCE.toString() + ">; rel=\"type\"", 511 "<http" + "://example.org/some/location/image.tiff>; " + "rel=\"http://fedora" + 512 ".info/definitions/fcrepo#ExternalContent\"; " + "handling=\"proxy\"; type=\"image/tiff\"", 513 "<http" + "://example.org/some/otherlocation>; rel=acl"); 514 createResourceService.checkAclLinkHeader(links); 515 } 516 517 @Test 518 public void testCheckAclLinkHeaderSuccess() throws Exception { 519 final List<String> links = Arrays.asList("<" + NON_RDF_SOURCE.toString() + ">; rel=\"type\"", 520 "<http" + "://example.org/some/location/image.tiff>; " + "rel=\"http://fedora" + 521 ".info/definitions/fcrepo#ExternalContent\"; " + "handling=\"proxy\"; type=\"image/tiff\""); 522 createResourceService.checkAclLinkHeader(links); 523 } 524 525 @Test 526 public void testCopyExternalBinary() throws Exception { 527 final var realDigests = asList(URI.create("urn:sha1:94e66df8cd09d410c62d9e0dc59d3a884e458e05")); 528 529 tempFolder.create(); 530 final File externalFile = tempFolder.newFile(); 531 final String contentString = "some content"; 532 FileUtils.write(externalFile, contentString, StandardCharsets.UTF_8); 533 final URI uri = externalFile.toURI(); 534 when(extContent.fetchExternalContent()).thenReturn(Files.newInputStream(externalFile.toPath())); 535 when(extContent.getURI()).thenReturn(uri); 536 when(extContent.isCopy()).thenReturn(true); 537 when(extContent.getHandling()).thenReturn(ExternalContent.COPY); 538 when(extContent.getContentType()).thenReturn("text/plain"); 539 540 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 541 final FedoraId childId = fedoraId.resolve("child"); 542 543 createResourceService.perform(transaction, USER_PRINCIPAL, childId, 544 CONTENT_TYPE, FILENAME, contentString.length(), null, realDigests, null, extContent); 545 546 verify(psSession, times(2)).persist(operationCaptor.capture()); 547 final List<ResourceOperation> operations = operationCaptor.getAllValues(); 548 final var operation = getOperation(operations, CreateNonRdfSourceOperation.class); 549 final FedoraId persistedId = operation.getResourceId(); 550 assertNotEquals(fedoraId, persistedId); 551 assertTrue(persistedId.getFullId().startsWith(fedoraId.getFullId())); 552 assertBinaryPropertiesPresent(operation, "text/plain", FILENAME, contentString.length(), realDigests); 553 verify(transaction).lockResource(childId); 554 } 555 556 @Test 557 public void testProxyExternalBinary() throws Exception { 558 final var realDigests = asList(URI.create("urn:sha1:94e66df8cd09d410c62d9e0dc59d3a884e458e05")); 559 560 tempFolder.create(); 561 final File externalFile = tempFolder.newFile(); 562 final String contentString = "some content"; 563 FileUtils.write(externalFile, contentString, StandardCharsets.UTF_8); 564 final URI uri = externalFile.toURI(); 565 when(extContent.fetchExternalContent()).thenReturn(Files.newInputStream(externalFile.toPath())); 566 when(extContent.getURI()).thenReturn(uri); 567 when(extContent.getHandling()).thenReturn(ExternalContent.PROXY); 568 when(extContent.getContentType()).thenReturn(EXTERNAL_CONTENT_TYPE); 569 570 final FedoraId fedoraId = FedoraId.create(UUID.randomUUID().toString()); 571 final FedoraId childId = fedoraId.resolve("child"); 572 573 createResourceService.perform(transaction, USER_PRINCIPAL, childId, 574 CONTENT_TYPE, FILENAME, contentString.length(), null, realDigests, null, extContent); 575 576 verify(psSession, times(2)).persist(operationCaptor.capture()); 577 final List<ResourceOperation> operations = operationCaptor.getAllValues(); 578 final var operation = getOperation(operations, CreateNonRdfSourceOperation.class); 579 final FedoraId persistedId = operation.getResourceId(); 580 assertNotEquals(fedoraId, persistedId); 581 assertTrue(persistedId.getFullId().startsWith(fedoraId.getFullId())); 582 assertBinaryPropertiesPresent(operation, EXTERNAL_CONTENT_TYPE, FILENAME, contentString.length(), 583 realDigests); 584 585 assertExternalBinaryPropertiesPresent(operation, uri, ExternalContent.PROXY); 586 verify(transaction).lockResource(childId); 587 } 588 589 private void assertBinaryPropertiesPresent(final ResourceOperation operation, final String exMimetype, 590 final String exFilename, final long exContentSize, final Collection<URI> exDigests) { 591 final var nonRdfOperation = (NonRdfSourceOperation) operation; 592 assertEquals(exContentSize, nonRdfOperation.getContentSize()); 593 assertEquals(exFilename, nonRdfOperation.getFilename()); 594 assertEquals(exMimetype, nonRdfOperation.getMimeType()); 595 assertTrue(exDigests.containsAll(nonRdfOperation.getContentDigests())); 596 } 597 598 private void assertBinaryPropertiesPresent(final ResourceOperation operation) { 599 assertBinaryPropertiesPresent(operation, CONTENT_TYPE, FILENAME, CONTENT_SIZE, DIGESTS); 600 } 601 602 private void assertExternalBinaryPropertiesPresent(final ResourceOperation operation, final URI exExternalUri, 603 final String exHandling) { 604 final var nonRdfOperation = (NonRdfSourceOperation) operation; 605 assertEquals(exExternalUri, nonRdfOperation.getContentUri()); 606 assertEquals(exHandling, nonRdfOperation.getExternalHandling()); 607 } 608 609 private <T extends ResourceOperation> T getOperation(final List<ResourceOperation> operations, 610 final Class<T> clazz) { 611 return clazz.cast(operations.stream() 612 .filter(o -> (clazz.isInstance(o))) 613 .findFirst() 614 .get()); 615 } 616 617}