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.integration.http.api; 019 020import static org.fcrepo.kernel.api.FedoraTypes.FCR_TOMBSTONE; 021import static org.junit.Assert.assertEquals; 022 023import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; 024import static javax.ws.rs.core.HttpHeaders.LINK; 025import static javax.ws.rs.core.Response.Status.BAD_REQUEST; 026import static javax.ws.rs.core.Response.Status.CREATED; 027import static javax.ws.rs.core.Response.Status.GONE; 028import static javax.ws.rs.core.Response.Status.METHOD_NOT_ALLOWED; 029import static javax.ws.rs.core.Response.Status.NOT_FOUND; 030import static javax.ws.rs.core.Response.Status.NO_CONTENT; 031import static javax.ws.rs.core.Response.Status.OK; 032 033import javax.ws.rs.core.Link; 034 035import java.io.IOException; 036import java.net.URI; 037import java.util.Collection; 038 039import org.apache.commons.codec.Charsets; 040import org.apache.http.HttpEntity; 041import org.apache.http.HttpResponse; 042import org.apache.http.client.methods.CloseableHttpResponse; 043import org.apache.http.client.methods.HttpDelete; 044import org.apache.http.client.methods.HttpGet; 045import org.apache.http.client.methods.HttpPost; 046import org.apache.http.client.methods.HttpPut; 047import org.apache.http.client.methods.HttpUriRequest; 048import org.apache.http.entity.StringEntity; 049import org.junit.Test; 050import org.springframework.test.context.TestExecutionListeners; 051 052/** 053 * Tests related to tombstone stuff. 054 * 055 * @author whikloj 056 */ 057@TestExecutionListeners( 058 listeners = { TestIsolationExecutionListener.class }, 059 mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) 060public class FedoraTombstonesIT extends AbstractResourceIT { 061 062 private Link getTombstoneLink(final HttpResponse res) { 063 final Collection<String> headers = getLinkHeaders(res); 064 return headers.stream().map(Link::valueOf).filter(t -> t.getRel().equals("hasTombstone")).findFirst() 065 .orElse(null); 066 } 067 068 private Link getTombstoneLink(final HttpUriRequest req) throws IOException { 069 try (final CloseableHttpResponse response = execute(req)) { 070 return getTombstoneLink(response); 071 } 072 } 073 074 @Test 075 public void testDeleteObjectAndTombstone() throws IOException { 076 final String id = getRandomUniqueId(); 077 createObjectAndClose(id); 078 assertEquals(NO_CONTENT.getStatusCode(), getStatus(new HttpDelete(serverAddress + id))); 079 assertDeleted(id); 080 final HttpGet httpGet = getObjMethod(id); 081 final Link tombstone = getTombstoneLink(httpGet); 082 assertEquals("hasTombstone", tombstone.getRel()); 083 assertEquals(NO_CONTENT.getStatusCode(), getStatus(new HttpDelete(tombstone.getUri()))); 084 assertEquals(NOT_FOUND.getStatusCode(), getStatus(httpGet)); 085 } 086 087 @Test 088 public void testTrailingSlashTombstoneLink() throws IOException { 089 final String id = getRandomUniqueId(); 090 final URI expectedTombstone = URI.create(serverAddress + id + "/fcr:tombstone"); 091 createObjectAndClose(id); 092 assertEquals(NO_CONTENT.getStatusCode(), getStatus(new HttpDelete(serverAddress + id))); 093 assertDeleted(id); 094 final HttpGet get1 = getObjMethod(id); 095 final Link tombstone = getTombstoneLink(get1); 096 assertEquals("hasTombstone", tombstone.getRel()); 097 assertEquals(expectedTombstone, tombstone.getUri()); 098 // Now with a trailing slash 099 final HttpGet get2 = getObjMethod(id + "/"); 100 final Link tombstone2 = getTombstoneLink(get2); 101 assertEquals("hasTombstone", tombstone2.getRel()); 102 assertEquals(expectedTombstone, tombstone2.getUri()); 103 } 104 105 @Test 106 public void testDisallowedMethods() throws Exception { 107 final String id = getRandomUniqueId(); 108 final HttpPut putMethod = putObjMethod(id); 109 final HttpEntity body = new StringEntity("<> <http://example.org#title> 'a title'", Charsets.UTF_8); 110 putMethod.setEntity(body); 111 putMethod.setHeader(CONTENT_TYPE, "text/turtle"); 112 assertEquals(CREATED.getStatusCode(), getStatus(putMethod)); 113 114 final HttpGet getContainer = getObjMethod(id); 115 assertEquals(OK.getStatusCode(), getStatus(getContainer)); 116 117 final HttpDelete deleteContainer = deleteObjMethod(id); 118 assertEquals(NO_CONTENT.getStatusCode(), getStatus(deleteContainer)); 119 120 final HttpGet getContainer2 = getObjMethod(id); 121 final Link tombstoneUri; 122 try (final CloseableHttpResponse res = execute(getContainer2)) { 123 assertEquals(GONE.getStatusCode(), getStatus(res)); 124 tombstoneUri = getTombstoneLink(res); 125 } 126 127 final HttpGet getTombstone = new HttpGet(tombstoneUri.getUri()); 128 assertEquals(METHOD_NOT_ALLOWED.getStatusCode(), getStatus(getTombstone)); 129 130 final HttpPut putTombstone = new HttpPut(tombstoneUri.getUri()); 131 assertEquals(METHOD_NOT_ALLOWED.getStatusCode(), getStatus(putTombstone)); 132 133 final HttpPost postTombstone = new HttpPost(tombstoneUri.getUri()); 134 assertEquals(METHOD_NOT_ALLOWED.getStatusCode(), getStatus(postTombstone)); 135 136 final HttpDelete deleteTombstone = new HttpDelete(tombstoneUri.getUri()); 137 assertEquals(NO_CONTENT.getStatusCode(), getStatus(deleteTombstone)); 138 } 139 140 @Test 141 public void testPurgingGrandchildren() throws Exception { 142 final HttpPost post = postObjMethod(); 143 post.setHeader(LINK, "<http://fedora.info/definitions/v4/repository#ArchivalGroup>;rel=\"type\""); 144 final String agPath; 145 try (final CloseableHttpResponse response = execute(post)) { 146 assertEquals(CREATED.getStatusCode(), getStatus(response)); 147 agPath = getLocation(response); 148 } 149 150 final HttpGet get1 = new HttpGet(agPath); 151 assertEquals(OK.getStatusCode(), getStatus(get1)); 152 153 final HttpPost postChild = new HttpPost(agPath); 154 final String childPath; 155 try (final CloseableHttpResponse response = execute(postChild)) { 156 assertEquals(CREATED.getStatusCode(), getStatus(response)); 157 childPath = getLocation(response); 158 } 159 160 final HttpGet get2 = new HttpGet(childPath); 161 assertEquals(OK.getStatusCode(), getStatus(get2)); 162 163 final HttpPost postGrandChild = new HttpPost(childPath); 164 final String grandchildPath; 165 try (final CloseableHttpResponse response = execute(postGrandChild)) { 166 assertEquals(CREATED.getStatusCode(), getStatus(response)); 167 grandchildPath = getLocation(response); 168 } 169 170 final HttpGet get3 = new HttpGet(grandchildPath); 171 assertEquals(OK.getStatusCode(), getStatus(get3)); 172 173 final HttpDelete deleteChild = new HttpDelete(childPath); 174 assertEquals(NO_CONTENT.getStatusCode(), getStatus(deleteChild)); 175 176 final HttpGet get4 = new HttpGet(childPath); 177 assertEquals(GONE.getStatusCode(), getStatus(get4)); 178 179 final HttpGet get5 = new HttpGet(grandchildPath); 180 assertEquals(GONE.getStatusCode(), getStatus(get5)); 181 182 final HttpDelete purgeGrandchild = new HttpDelete(grandchildPath + "/" + FCR_TOMBSTONE); 183 assertEquals(NO_CONTENT.getStatusCode(), getStatus(purgeGrandchild)); 184 185 final HttpGet get6 = new HttpGet(grandchildPath); 186 assertEquals(NOT_FOUND.getStatusCode(), getStatus(get6)); 187 188 final HttpDelete purgeChild = new HttpDelete(childPath + "/" + FCR_TOMBSTONE); 189 assertEquals(NO_CONTENT.getStatusCode(), getStatus(purgeChild)); 190 191 final HttpGet get7 = new HttpGet(agPath); 192 assertEquals(OK.getStatusCode(), getStatus(get7)); 193 194 final HttpDelete purgeBeforeDelete = new HttpDelete(agPath + "/" + FCR_TOMBSTONE); 195 assertEquals(NOT_FOUND.getStatusCode(), getStatus(purgeBeforeDelete)); 196 197 final HttpDelete deleteAg = new HttpDelete(agPath); 198 assertEquals(NO_CONTENT.getStatusCode(), getStatus(deleteAg)); 199 200 final HttpGet get8 = new HttpGet(agPath); 201 assertEquals(GONE.getStatusCode(), getStatus(get8)); 202 203 final HttpDelete finalPurge = new HttpDelete(agPath + "/" + FCR_TOMBSTONE); 204 assertEquals(NO_CONTENT.getStatusCode(), getStatus(finalPurge)); 205 206 final HttpGet finalGet = new HttpGet(agPath); 207 assertEquals(NOT_FOUND.getStatusCode(), getStatus(finalGet)); 208 } 209 210 @Test 211 public void testNoChildrenOfTombstone() throws Exception { 212 final String uri; 213 try (final var response = execute(postObjMethod())) { 214 assertEquals(CREATED.getStatusCode(), getStatus(response)); 215 uri = getLocation(response); 216 } 217 assertEquals(NO_CONTENT.getStatusCode(), getStatus(new HttpDelete(uri))); 218 assertEquals(GONE.getStatusCode(), getStatus(new HttpPut(uri))); 219 assertEquals(BAD_REQUEST.getStatusCode(), getStatus(new HttpPut(uri + "/" + getRandomUniqueId()))); 220 assertEquals(BAD_REQUEST.getStatusCode(), getStatus(new HttpPut(uri + "/" + getRandomUniqueId() + "/" + 221 getRandomUniqueId()))); 222 } 223}