View Javadoc

1   /*
2    * Copyright 2015 Data Archiving and Networked Services (an institute of
3    * Koninklijke Nederlandse Akademie van Wetenschappen), King's College London,
4    * Georg-August-Universitaet Goettingen Stiftung Oeffentlichen Rechts
5    *
6    * Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
7    * the European Commission - subsequent versions of the EUPL (the "Licence");
8    * You may not use this work except in compliance with the Licence.
9    * You may obtain a copy of the Licence at:
10   *
11   * https://joinup.ec.europa.eu/software/page/eupl
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the Licence is distributed on an "AS IS" basis,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the Licence for the specific language governing
17   * permissions and limitations under the Licence.
18   */
19  
20  package eu.ehri.extension;
21  
22  import com.google.common.collect.Lists;
23  import eu.ehri.extension.base.AbstractAccessibleResource;
24  import eu.ehri.extension.base.AbstractResource;
25  import eu.ehri.extension.base.DeleteResource;
26  import eu.ehri.extension.base.GetResource;
27  import eu.ehri.extension.base.ListResource;
28  import eu.ehri.extension.base.ParentResource;
29  import eu.ehri.extension.base.UpdateResource;
30  import eu.ehri.project.core.Tx;
31  import eu.ehri.project.definitions.Entities;
32  import eu.ehri.project.exceptions.DeserializationError;
33  import eu.ehri.project.exceptions.ItemNotFound;
34  import eu.ehri.project.exceptions.PermissionDenied;
35  import eu.ehri.project.exceptions.ValidationError;
36  import eu.ehri.project.exporters.ead.Ead2002Exporter;
37  import eu.ehri.project.exporters.ead.EadExporter;
38  import eu.ehri.project.exporters.eag.Eag2012Exporter;
39  import eu.ehri.project.importers.ImportCallback;
40  import eu.ehri.project.importers.ImportLog;
41  import eu.ehri.project.importers.json.BatchOperations;
42  import eu.ehri.project.models.DocumentaryUnit;
43  import eu.ehri.project.models.Repository;
44  import eu.ehri.project.models.base.Accessible;
45  import eu.ehri.project.models.base.Actioner;
46  import eu.ehri.project.persistence.Bundle;
47  import org.neo4j.graphdb.GraphDatabaseService;
48  
49  import javax.ws.rs.Consumes;
50  import javax.ws.rs.DELETE;
51  import javax.ws.rs.DefaultValue;
52  import javax.ws.rs.GET;
53  import javax.ws.rs.POST;
54  import javax.ws.rs.PUT;
55  import javax.ws.rs.Path;
56  import javax.ws.rs.PathParam;
57  import javax.ws.rs.Produces;
58  import javax.ws.rs.QueryParam;
59  import javax.ws.rs.WebApplicationException;
60  import javax.ws.rs.core.Context;
61  import javax.ws.rs.core.MediaType;
62  import javax.ws.rs.core.Response;
63  import javax.ws.rs.core.StreamingOutput;
64  import javax.xml.transform.TransformerException;
65  import java.io.IOException;
66  import java.io.InputStream;
67  import java.util.List;
68  
69  /**
70   * Provides a web service interface for the Repository.
71   */
72  @Path(AbstractResource.RESOURCE_ENDPOINT_PREFIX + "/" + Entities.REPOSITORY)
73  public class RepositoryResource extends AbstractAccessibleResource<Repository>
74          implements ParentResource, GetResource, ListResource, UpdateResource, DeleteResource {
75  
76      public RepositoryResource(@Context GraphDatabaseService database) {
77          super(database, Repository.class);
78      }
79  
80      @GET
81      @Produces(MediaType.APPLICATION_JSON)
82      @Path("{id:[^/]+}")
83      @Override
84      public Response get(@PathParam("id") String id) throws ItemNotFound {
85          return getItem(id);
86      }
87  
88      @GET
89      @Produces(MediaType.APPLICATION_JSON)
90      @Override
91      public Response list() {
92          return listItems();
93      }
94  
95      @GET
96      @Produces(MediaType.APPLICATION_JSON)
97      @Path("{id:[^/]+}/list")
98      @Override
99      public Response listChildren(
100             @PathParam("id") String id,
101             @QueryParam(ALL_PARAM) @DefaultValue("false") boolean all) throws ItemNotFound {
102         try (final Tx tx = beginTx()) {
103             Repository repository = api().detail(id, cls);
104             Response response = streamingPage(() -> {
105                 Iterable<DocumentaryUnit> units = all
106                         ? repository.getAllDocumentaryUnits()
107                         : repository.getTopLevelDocumentaryUnits();
108                 return getQuery().page(units, DocumentaryUnit.class);
109             });
110             tx.success();
111             return response;
112         }
113     }
114 
115     @PUT
116     @Consumes(MediaType.APPLICATION_JSON)
117     @Produces(MediaType.APPLICATION_JSON)
118     @Path("{id:[^/]+}")
119     @Override
120     public Response update(@PathParam("id") String id, Bundle bundle)
121             throws PermissionDenied, ValidationError,
122             DeserializationError, ItemNotFound {
123         try (final Tx tx = beginTx()) {
124             Response response = updateItem(id, bundle);
125             tx.success();
126             return response;
127         }
128     }
129 
130     @DELETE
131     @Path("{id:[^/]+}")
132     @Override
133     public void delete(@PathParam("id") String id)
134             throws PermissionDenied, ItemNotFound, ValidationError {
135         try (final Tx tx = beginTx()) {
136             deleteItem(id);
137             tx.success();
138         }
139     }
140 
141     /**
142      * Create a documentary unit for this repository.
143      *
144      * @param id     The repository ID
145      * @param bundle The new unit data
146      * @return The new unit
147      */
148     @POST
149     @Consumes(MediaType.APPLICATION_JSON)
150     @Produces(MediaType.APPLICATION_JSON)
151     @Path("{id:[^/]+}")
152     @Override
153     public Response createChild(@PathParam("id") String id,
154             Bundle bundle, @QueryParam(ACCESSOR_PARAM) List<String> accessors)
155             throws PermissionDenied, ValidationError,
156             DeserializationError, ItemNotFound {
157         try (final Tx tx = beginTx()) {
158             final Repository repository = api().detail(id, cls);
159             Response response = createItem(bundle, accessors,
160                     repository::addTopLevelDocumentaryUnit,
161                     api().withScope(repository), DocumentaryUnit.class);
162             tx.success();
163             return response;
164         }
165     }
166 
167     /**
168      * Add items to a repository via serialised data.
169      *
170      * @param id       the repository ID
171      * @param data     a list of serialised items
172      * @return an import log
173      */
174     @POST
175     @Consumes(MediaType.APPLICATION_JSON)
176     @Produces(MediaType.APPLICATION_JSON)
177     @Path("{id:[^/]+}/list")
178     public ImportLog addChildren(
179             @PathParam("id") String id,
180             InputStream data) throws ItemNotFound, DeserializationError, ValidationError {
181         try (final Tx tx = beginTx()) {
182             Actioner user = getCurrentActioner();
183             Repository repository = api().detail(id, cls);
184             ImportCallback cb = mutation -> {
185                 Accessible accessible = mutation.getNode();
186                 if (!Entities.DOCUMENTARY_UNIT.equals(accessible.getType())) {
187                     throw new RuntimeException("Bundle is not a documentary unit: " + accessible.getId());
188                 }
189                 accessible.setPermissionScope(repository);
190                 repository.addTopLevelDocumentaryUnit(accessible.as(DocumentaryUnit.class));
191             };
192             ImportLog log = new BatchOperations(graph, repository, true,
193                     false, Lists.newArrayList(cb)).batchImport(data, user, getLogMessage());
194             logger.debug("Committing batch ingest transaction...");
195             tx.success();
196             return log;
197         }
198     }
199 
200     /**
201      * Export the given repository as an EAG file.
202      *
203      * @param id   the unit id
204      * @param lang a three-letter ISO639-2 code
205      * @return an EAG XML Document
206      */
207     @GET
208     @Path("{id:[^/]+}/eag")
209     @Produces(MediaType.TEXT_XML)
210     public Response exportEag(@PathParam("id") String id,
211             final @QueryParam("lang") @DefaultValue("eng") String lang)
212             throws IOException, ItemNotFound {
213         try (final Tx tx = beginTx()) {
214             Repository repository = api().detail(id, cls);
215             tx.success();
216             return Response.ok((StreamingOutput) outputStream -> {
217                 try (final Tx tx2 = beginTx()) {
218                     new Eag2012Exporter(api()).export(repository, outputStream, lang);
219                     tx2.success();
220                 } catch (TransformerException e) {
221                     throw new WebApplicationException(e);
222                 }
223             }).type(MediaType.TEXT_XML + "; charset=utf-8").build();
224         }
225     }
226 
227     /**
228      * Export the given repository's top-level units as EAD streamed
229      * in a ZIP file.
230      *
231      * @param id   the unit id
232      * @param lang a three-letter ISO639-2 code
233      * @return an EAD XML Document
234      */
235     @GET
236     @Path("{id:[^/]+}/ead")
237     @Produces("application/zip")
238     public Response exportEad(@PathParam("id") String id,
239             final @QueryParam("lang") @DefaultValue("eng") String lang)
240             throws IOException, ItemNotFound {
241         try (final Tx tx = beginTx()) {
242             final Repository repo = api().detail(id, cls);
243             final EadExporter eadExporter = new Ead2002Exporter(api());
244             Response response = exportItemsAsZip(eadExporter, repo.getTopLevelDocumentaryUnits(), lang);
245             tx.success();
246             return response;
247         }
248     }
249 }