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}