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.Sets;
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.UpdateResource;
29  import eu.ehri.project.api.Api;
30  import eu.ehri.project.api.EventsApi;
31  import eu.ehri.project.core.Tx;
32  import eu.ehri.project.definitions.Entities;
33  import eu.ehri.project.exceptions.DeserializationError;
34  import eu.ehri.project.exceptions.ItemNotFound;
35  import eu.ehri.project.exceptions.PermissionDenied;
36  import eu.ehri.project.exceptions.ValidationError;
37  import eu.ehri.project.models.EntityClass;
38  import eu.ehri.project.models.Group;
39  import eu.ehri.project.models.base.Accessible;
40  import eu.ehri.project.models.base.Accessor;
41  import eu.ehri.project.models.base.Actioner;
42  import eu.ehri.project.persistence.Bundle;
43  import org.neo4j.graphdb.GraphDatabaseService;
44  
45  import javax.ws.rs.Consumes;
46  import javax.ws.rs.DELETE;
47  import javax.ws.rs.DefaultValue;
48  import javax.ws.rs.GET;
49  import javax.ws.rs.POST;
50  import javax.ws.rs.PUT;
51  import javax.ws.rs.Path;
52  import javax.ws.rs.PathParam;
53  import javax.ws.rs.Produces;
54  import javax.ws.rs.QueryParam;
55  import javax.ws.rs.core.Context;
56  import javax.ws.rs.core.MediaType;
57  import javax.ws.rs.core.Response;
58  import java.util.List;
59  import java.util.Set;
60  
61  /**
62   * Provides a web service interface for the Group model.
63   */
64  @Path(AbstractResource.RESOURCE_ENDPOINT_PREFIX + "/" + Entities.GROUP)
65  public class GroupResource
66          extends AbstractAccessibleResource<Group>
67          implements GetResource, ListResource, UpdateResource, DeleteResource {
68  
69      public static final String MEMBER_PARAM = "member";
70  
71      public GroupResource(@Context GraphDatabaseService database) {
72          super(database, Group.class);
73      }
74  
75      @GET
76      @Produces(MediaType.APPLICATION_JSON)
77      @Path("{id:[^/]+}")
78      @Override
79      public Response get(@PathParam("id") String id) throws ItemNotFound {
80          return getItem(id);
81      }
82  
83      @GET
84      @Produces(MediaType.APPLICATION_JSON)
85      @Override
86      public Response list() {
87          return listItems();
88      }
89  
90      @POST
91      @Consumes(MediaType.APPLICATION_JSON)
92      @Produces(MediaType.APPLICATION_JSON)
93      public Response createGroup(Bundle bundle,
94              @QueryParam(ACCESSOR_PARAM) List<String> accessors,
95              @QueryParam(MEMBER_PARAM) List<String> members)
96              throws PermissionDenied, ValidationError, DeserializationError, ItemNotFound {
97          try (final Tx tx = beginTx()) {
98              final Api.Acl acl = api().acl();
99              final Set<Accessor> groupMembers = Sets.newHashSet();
100             for (String member : members) {
101                 groupMembers.add(manager.getEntity(member, Accessor.class));
102             }
103             Response item = createItem(bundle, accessors, group -> {
104                 for (Accessor member : groupMembers) {
105                     acl.addAccessorToGroup(group, member);
106                 }
107             });
108             tx.success();
109             return item;
110         } catch (ItemNotFound e) {
111             throw new DeserializationError("User or group not found: " + e.getValue());
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, DeserializationError, ItemNotFound {
122         try (final Tx tx = beginTx()) {
123             Response item = updateItem(id, bundle);
124             tx.success();
125             return item;
126         }
127     }
128 
129     /**
130      * Add an accessor to a group.
131      */
132     @POST
133     @Path("{id:[^/]+}/{aid:[^/]+}")
134     public void addMember(@PathParam("id") String id, @PathParam("aid") String aid)
135             throws PermissionDenied, ItemNotFound {
136         try (final Tx tx = beginTx()) {
137             Group group = manager.getEntity(id, EntityClass.GROUP, Group.class);
138             Accessor accessor = manager.getEntity(aid, Accessor.class);
139             api().acl().addAccessorToGroup(group, accessor);
140             tx.success();
141         }
142     }
143 
144     /**
145      * Remove an accessor from a group.
146      */
147     @DELETE
148     @Path("{id:[^/]+}/{aid:[^/]+}")
149     public void removeMember(@PathParam("id") String id, @PathParam("aid") String aid)
150             throws PermissionDenied, ItemNotFound {
151         try (final Tx tx = beginTx()) {
152             Group group = manager.getEntity(id, EntityClass.GROUP, Group.class);
153             Accessor accessor = manager.getEntity(aid, Accessor.class);
154             api().acl().removeAccessorFromGroup(group, accessor);
155             tx.success();
156         }
157     }
158 
159     /**
160      * list members of the specified group;
161      * UserProfiles and sub-Groups (direct descendants)
162      */
163     @GET
164     @Produces(MediaType.APPLICATION_JSON)
165     @Path("{id:[^/]+}/list")
166     public Response listChildren(
167             @PathParam("id") String id,
168             @QueryParam(ALL_PARAM) @DefaultValue("false") boolean all) throws ItemNotFound {
169         try (final Tx tx = beginTx()) {
170             Group group = manager.getEntity(id, EntityClass.GROUP, Group.class);
171             Response response = streamingPage(() -> {
172                 Iterable<Accessible> members = all
173                         ? group.getAllUserProfileMembers()
174                         : group.getMembersAsEntities();
175                 return getQuery().page(members, Accessible.class);
176             });
177             tx.success();
178             return response;
179         }
180     }
181 
182     @DELETE
183     @Path("{id:[^/]+}")
184     @Override
185     public void delete(@PathParam("id") String id)
186             throws PermissionDenied, ItemNotFound, ValidationError {
187         try (final Tx tx = beginTx()) {
188             deleteItem(id);
189             tx.success();
190         }
191     }
192 
193     /**
194      * Fetch an aggregate list of a user's actions.
195      *
196      * @param userId      the user's ID
197      * @param aggregation the manner in which to aggregate the results, accepting
198      *                    "user", "strict" or "off" (no aggregation). Default is
199      *                    <b>strict</b>.
200      * @return a list of event ranges
201      */
202     @GET
203     @Produces(MediaType.APPLICATION_JSON)
204     @Path("{id:[^/]+}/actions")
205     public Response aggregateUserActions(
206             @PathParam("id") String userId,
207             @QueryParam(AGGREGATION_PARAM) @DefaultValue("strict") EventsApi.Aggregation aggregation)
208             throws ItemNotFound {
209         try (final Tx tx = beginTx()) {
210             Actioner group = manager.getEntity(userId, Actioner.class);
211             EventsApi eventsApi = getEventsApi()
212                     .withAggregation(aggregation);
213             Response response = streamingListOfLists(() -> eventsApi.aggregateActions(group));
214             tx.success();
215             return response;
216         }
217     }
218 }