View Javadoc
1   package de.dlr.shepard.context.collection.endpoints;
2   
3   import de.dlr.shepard.auth.permission.io.PermissionsIO;
4   import de.dlr.shepard.auth.permission.model.Permissions;
5   import de.dlr.shepard.auth.permission.model.Roles;
6   import de.dlr.shepard.auth.security.AuthenticationContext;
7   import de.dlr.shepard.common.filters.Subscribable;
8   import de.dlr.shepard.common.util.Constants;
9   import de.dlr.shepard.common.util.QueryParamHelper;
10  import de.dlr.shepard.context.collection.entities.Collection;
11  import de.dlr.shepard.context.collection.io.CollectionIO;
12  import de.dlr.shepard.context.collection.services.CollectionService;
13  import de.dlr.shepard.context.export.ExportService;
14  import jakarta.enterprise.context.RequestScoped;
15  import jakarta.inject.Inject;
16  import jakarta.validation.Valid;
17  import jakarta.validation.constraints.NotNull;
18  import jakarta.validation.constraints.PositiveOrZero;
19  import jakarta.ws.rs.Consumes;
20  import jakarta.ws.rs.DELETE;
21  import jakarta.ws.rs.GET;
22  import jakarta.ws.rs.POST;
23  import jakarta.ws.rs.PUT;
24  import jakarta.ws.rs.Path;
25  import jakarta.ws.rs.PathParam;
26  import jakarta.ws.rs.Produces;
27  import jakarta.ws.rs.QueryParam;
28  import jakarta.ws.rs.core.MediaType;
29  import jakarta.ws.rs.core.Response;
30  import jakarta.ws.rs.core.Response.Status;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.util.ArrayList;
34  import java.util.UUID;
35  import org.eclipse.microprofile.openapi.annotations.Operation;
36  import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
37  import org.eclipse.microprofile.openapi.annotations.media.Content;
38  import org.eclipse.microprofile.openapi.annotations.media.Schema;
39  import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
40  import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
41  import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
42  import org.eclipse.microprofile.openapi.annotations.tags.Tag;
43  
44  @Path(Constants.COLLECTIONS)
45  @Produces(MediaType.APPLICATION_JSON)
46  @Consumes(MediaType.APPLICATION_JSON)
47  @RequestScoped
48  public class CollectionRest {
49  
50    @Inject
51    CollectionService collectionService;
52  
53    @Inject
54    ExportService exportService;
55  
56    @Inject
57    AuthenticationContext authenticationContext;
58  
59    @GET
60    @Tag(name = Constants.COLLECTION)
61    @Operation(description = "Get all collections")
62    @APIResponse(
63      description = "ok",
64      responseCode = "200",
65      content = @Content(schema = @Schema(type = SchemaType.ARRAY, implementation = CollectionIO.class))
66    )
67    @APIResponse(description = "not found", responseCode = "404")
68    @APIResponse(responseCode = "401", description = "not authorized")
69    @APIResponse(responseCode = "400", description = "bad request")
70    @Parameter(name = Constants.QP_NAME)
71    @Parameter(name = Constants.QP_PAGE)
72    @Parameter(name = Constants.QP_SIZE)
73    @Parameter(name = Constants.QP_ORDER_BY_ATTRIBUTE)
74    @Parameter(name = Constants.QP_ORDER_DESC)
75    public Response getAllCollections(
76      @QueryParam(Constants.QP_NAME) String name,
77      @QueryParam(Constants.QP_PAGE) @PositiveOrZero Integer page,
78      @QueryParam(Constants.QP_SIZE) @PositiveOrZero Integer size,
79      @QueryParam(Constants.QP_ORDER_BY_ATTRIBUTE) DataObjectAttributes orderBy,
80      @QueryParam(Constants.QP_ORDER_DESC) Boolean orderDesc
81    ) {
82      var params = new QueryParamHelper();
83      if (name != null) params = params.withName(name);
84      if (page != null && size != null) params = params.withPageAndSize(page, size);
85      if (orderBy != null) params = params.withOrderByAttribute(orderBy, orderDesc);
86      var collections = collectionService.getAllCollections(params);
87  
88      var result = new ArrayList<CollectionIO>(collections.size());
89      for (var collection : collections) {
90        result.add(new CollectionIO(collection));
91      }
92      return Response.ok(result).build();
93    }
94  
95    @GET
96    @Path("/{" + Constants.COLLECTION_ID + "}")
97    @Tag(name = Constants.COLLECTION)
98    @Operation(description = "Get collection")
99    @APIResponse(
100     description = "ok",
101     responseCode = "200",
102     content = @Content(schema = @Schema(implementation = CollectionIO.class))
103   )
104   @APIResponse(responseCode = "400", description = "bad request")
105   @APIResponse(responseCode = "401", description = "not authorized")
106   @APIResponse(responseCode = "403", description = "forbidden")
107   @APIResponse(responseCode = "404", description = "not found")
108   @Parameter(name = Constants.COLLECTION_ID)
109   @Parameter(name = Constants.VERSION_UID)
110   public Response getCollection(
111     @PathParam(Constants.COLLECTION_ID) @NotNull @PositiveOrZero Long collectionId,
112     @QueryParam(Constants.VERSION_UID) @org.hibernate.validator.constraints.UUID String versionUID
113   ) {
114     // Do not use UUID type as query param type, since it results in a wrong HTTP error message when type is wrong
115     // instead use validation annotation and string and then create a UUID object from it
116     UUID versionUUID = null;
117     if (versionUID != null) {
118       versionUUID = UUID.fromString(versionUID);
119     }
120 
121     Collection collection = collectionService.getCollectionWithDataObjectsAndIncomingReferences(
122       collectionId,
123       versionUUID
124     );
125     return Response.ok(new CollectionIO(collection)).build();
126   }
127 
128   @PUT
129   @Path("/{" + Constants.COLLECTION_ID + "}")
130   @Subscribable
131   @Tag(name = Constants.COLLECTION)
132   @Operation(description = "Update collection")
133   @APIResponse(
134     description = "ok",
135     responseCode = "200",
136     content = @Content(schema = @Schema(implementation = CollectionIO.class))
137   )
138   @Parameter(name = Constants.COLLECTION_ID)
139   @APIResponse(responseCode = "400", description = "bad request")
140   @APIResponse(responseCode = "401", description = "not authorized")
141   @APIResponse(responseCode = "403", description = "forbidden")
142   @APIResponse(responseCode = "404", description = "not found")
143   public Response updateCollection(
144     @PathParam(Constants.COLLECTION_ID) @NotNull @PositiveOrZero Long collectionId,
145     @RequestBody(
146       required = true,
147       content = @Content(schema = @Schema(implementation = CollectionIO.class))
148     ) @Valid CollectionIO collection
149   ) {
150     Collection updatedCollection = collectionService.updateCollectionByShepardId(collectionId, collection);
151     return Response.ok(new CollectionIO(updatedCollection)).build();
152   }
153 
154   @DELETE
155   @Path("/{" + Constants.COLLECTION_ID + "}")
156   @Subscribable
157   @Tag(name = Constants.COLLECTION)
158   @Operation(description = "Delete collection")
159   @APIResponse(responseCode = "400", description = "bad request")
160   @APIResponse(responseCode = "401", description = "not authorized")
161   @APIResponse(responseCode = "403", description = "forbidden")
162   @APIResponse(responseCode = "404", description = "not found")
163   @Parameter(name = Constants.COLLECTION_ID)
164   public Response deleteCollection(@PathParam(Constants.COLLECTION_ID) @NotNull @PositiveOrZero Long collectionId) {
165     collectionService.deleteCollection(collectionId);
166     return Response.status(Status.NO_CONTENT).build();
167   }
168 
169   @GET
170   @Path("/{" + Constants.COLLECTION_ID + "}/" + Constants.PERMISSIONS)
171   @Tag(name = Constants.COLLECTION)
172   @Operation(description = "Get permissions")
173   @APIResponse(
174     description = "ok",
175     responseCode = "200",
176     content = @Content(schema = @Schema(implementation = PermissionsIO.class))
177   )
178   @APIResponse(responseCode = "400", description = "bad request")
179   @APIResponse(responseCode = "401", description = "not authorized")
180   @APIResponse(responseCode = "403", description = "forbidden")
181   @APIResponse(responseCode = "404", description = "not found")
182   @Parameter(name = Constants.COLLECTION_ID)
183   public Response getCollectionPermissions(
184     @PathParam(Constants.COLLECTION_ID) @NotNull @PositiveOrZero Long collectionId
185   ) {
186     Permissions permissions = collectionService.getCollectionPermissions(collectionId);
187     return Response.ok(new PermissionsIO(permissions)).build();
188   }
189 
190   @PUT
191   @Path("/{" + Constants.COLLECTION_ID + "}/" + Constants.PERMISSIONS)
192   @Tag(name = Constants.COLLECTION)
193   @Operation(description = "Edit permissions")
194   @APIResponse(
195     description = "ok",
196     responseCode = "200",
197     content = @Content(schema = @Schema(implementation = PermissionsIO.class))
198   )
199   @APIResponse(responseCode = "400", description = "bad request")
200   @APIResponse(responseCode = "401", description = "not authorized")
201   @APIResponse(responseCode = "403", description = "forbidden")
202   @APIResponse(responseCode = "404", description = "not found")
203   @Parameter(name = Constants.COLLECTION_ID)
204   public Response editCollectionPermissions(
205     @PathParam(Constants.COLLECTION_ID) @NotNull @PositiveOrZero Long collectionId,
206     @RequestBody(
207       required = true,
208       content = @Content(schema = @Schema(implementation = PermissionsIO.class))
209     ) @Valid PermissionsIO newPermissions
210   ) {
211     Permissions updatedPermissions = collectionService.updateCollectionPermissions(newPermissions, collectionId);
212 
213     return Response.ok(new PermissionsIO(updatedPermissions)).build();
214   }
215 
216   @GET
217   @Path("/{" + Constants.COLLECTION_ID + "}/" + Constants.ROLES)
218   @Tag(name = Constants.COLLECTION)
219   @Operation(description = "Get roles")
220   @APIResponse(
221     description = "ok",
222     responseCode = "200",
223     content = @Content(schema = @Schema(implementation = Roles.class))
224   )
225   @APIResponse(responseCode = "400", description = "bad request")
226   @APIResponse(responseCode = "401", description = "not authorized")
227   @APIResponse(responseCode = "403", description = "forbidden")
228   @APIResponse(responseCode = "404", description = "not found")
229   @Parameter(name = Constants.COLLECTION_ID)
230   public Response getCollectionRoles(@PathParam(Constants.COLLECTION_ID) @NotNull @PositiveOrZero Long collectionId) {
231     Roles roles = collectionService.getCollectionRoles(collectionId);
232     return Response.ok(roles).build();
233   }
234 
235   @POST
236   @Tag(name = Constants.COLLECTION)
237   @Operation(description = "Create a new collection")
238   @APIResponse(
239     description = "created",
240     responseCode = "201",
241     content = @Content(schema = @Schema(implementation = CollectionIO.class))
242   )
243   @APIResponse(responseCode = "400", description = "bad request")
244   @APIResponse(responseCode = "403", description = "forbidden")
245   @APIResponse(responseCode = "404", description = "not found")
246   public Response createCollection(
247     @RequestBody(
248       required = true,
249       content = @Content(schema = @Schema(implementation = CollectionIO.class))
250     ) @Valid CollectionIO collection
251   ) {
252     Collection newCollection = collectionService.createCollection(collection);
253     return Response.ok(new CollectionIO(newCollection)).status(Status.CREATED).build();
254   }
255 
256   @GET
257   @Path("/{" + Constants.COLLECTION_ID + "}/" + Constants.EXPORT)
258   @Produces(MediaType.APPLICATION_OCTET_STREAM)
259   @Tag(name = Constants.COLLECTION)
260   @Operation(description = "Export Collection as RoCrate")
261   @APIResponse(
262     description = "ok",
263     responseCode = "200",
264     content = @Content(
265       mediaType = MediaType.APPLICATION_OCTET_STREAM,
266       schema = @Schema(type = SchemaType.STRING, format = "binary")
267     )
268   )
269   @APIResponse(responseCode = "400", description = "bad request")
270   @APIResponse(responseCode = "401", description = "not authorized")
271   @APIResponse(responseCode = "403", description = "forbidden")
272   @APIResponse(responseCode = "404", description = "not found")
273   @Parameter(name = Constants.COLLECTION_ID)
274   public Response exportCollection(@PathParam(Constants.COLLECTION_ID) @NotNull @PositiveOrZero Long collectionId)
275     throws IOException {
276     InputStream is = exportService.exportCollectionByShepardId(collectionId);
277     return Response.ok(is, MediaType.APPLICATION_OCTET_STREAM)
278       .header("Content-Disposition", "attachment; filename=\"export.zip\"")
279       .build();
280   }
281 }