1 package de.dlr.shepard.endpoints;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.ArrayList;
6
7 import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
8 import org.glassfish.jersey.media.multipart.FormDataParam;
9
10 import de.dlr.shepard.exceptions.InvalidRequestException;
11 import de.dlr.shepard.filters.Subscribable;
12 import de.dlr.shepard.influxDB.FillOption;
13 import de.dlr.shepard.influxDB.SingleValuedUnaryFunction;
14 import de.dlr.shepard.influxDB.Timeseries;
15 import de.dlr.shepard.influxDB.TimeseriesPayload;
16 import de.dlr.shepard.neo4Core.io.PermissionsIO;
17 import de.dlr.shepard.neo4Core.io.TimeseriesContainerIO;
18 import de.dlr.shepard.neo4Core.orderBy.ContainerAttributes;
19 import de.dlr.shepard.neo4Core.services.PermissionsService;
20 import de.dlr.shepard.neo4Core.services.TimeseriesContainerService;
21 import de.dlr.shepard.security.PermissionsUtil;
22 import de.dlr.shepard.util.Constants;
23 import de.dlr.shepard.util.QueryParamHelper;
24 import io.swagger.v3.oas.annotations.Parameter;
25 import jakarta.validation.Valid;
26 import jakarta.ws.rs.Consumes;
27 import jakarta.ws.rs.DELETE;
28 import jakarta.ws.rs.GET;
29 import jakarta.ws.rs.POST;
30 import jakarta.ws.rs.PUT;
31 import jakarta.ws.rs.Path;
32 import jakarta.ws.rs.PathParam;
33 import jakarta.ws.rs.Produces;
34 import jakarta.ws.rs.QueryParam;
35 import jakarta.ws.rs.core.Context;
36 import jakarta.ws.rs.core.MediaType;
37 import jakarta.ws.rs.core.Response;
38 import jakarta.ws.rs.core.Response.Status;
39 import jakarta.ws.rs.core.SecurityContext;
40
41 @Consumes(MediaType.APPLICATION_JSON)
42 @Produces(MediaType.APPLICATION_JSON)
43 @Path(Constants.TIMESERIES)
44 public class TimeseriesRestImpl implements TimeseriesRest {
45 private TimeseriesContainerService timeseriesContainerService = new TimeseriesContainerService();
46 private PermissionsService permissionsService = new PermissionsService();
47
48 @Context
49 private SecurityContext securityContext;
50
51 @GET
52 @Override
53 public Response getAllTimeseriesContainers(@QueryParam(Constants.QP_NAME) String name,
54 @QueryParam(Constants.QP_PAGE) Integer page, @QueryParam(Constants.QP_SIZE) Integer size,
55 @QueryParam(Constants.QP_ORDER_BY_ATTRIBUTE) ContainerAttributes orderBy,
56 @QueryParam(Constants.QP_ORDER_DESC) Boolean orderDesc) {
57 var params = new QueryParamHelper();
58 if (name != null)
59 params = params.withName(name);
60 if (page != null && size != null)
61 params = params.withPageAndSize(page, size);
62 if (orderBy != null)
63 params = params.withOrderByAttribute(orderBy, orderDesc);
64 var containers = timeseriesContainerService.getAllContainers(params,
65 securityContext.getUserPrincipal().getName());
66 var result = new ArrayList<TimeseriesContainerIO>(containers.size());
67 for (var container : containers) {
68 result.add(new TimeseriesContainerIO(container));
69 }
70 return Response.ok(result).build();
71 }
72
73 @GET
74 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}")
75 @Override
76 public Response getTimeseriesContainer(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesId) {
77 var result = timeseriesContainerService.getContainer(timeseriesId);
78 return Response.ok(new TimeseriesContainerIO(result)).build();
79 }
80
81 @POST
82 @Override
83 public Response createTimeseriesContainer(TimeseriesContainerIO timeseriesContainer) {
84 var result = timeseriesContainerService.createContainer(timeseriesContainer,
85 securityContext.getUserPrincipal().getName());
86
87 return Response.ok(new TimeseriesContainerIO(result)).status(Status.CREATED).build();
88 }
89
90 @DELETE
91 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}")
92 @Subscribable
93 @Override
94 public Response deleteTimeseriesContainer(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesId) {
95 var result = timeseriesContainerService.deleteContainer(timeseriesId,
96 securityContext.getUserPrincipal().getName());
97
98 return result ? Response.status(Status.NO_CONTENT).build()
99 : Response.status(Status.INTERNAL_SERVER_ERROR).build();
100 }
101
102 @POST
103 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.PAYLOAD)
104 @Subscribable
105 @Override
106 public Response createTimeseries(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesId,
107 TimeseriesPayload payload) {
108 var result = timeseriesContainerService.createTimeseries(timeseriesId, payload);
109 return result != null ? Response.status(Status.CREATED).entity(result).build()
110 : Response.status(Status.INTERNAL_SERVER_ERROR).build();
111 }
112
113 @GET
114 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.AVAILABLE)
115 @Override
116 public Response getTimeseriesAvailable(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesContainerId) {
117 return Response.ok(timeseriesContainerService.getTimeseriesAvailable(timeseriesContainerId)).build();
118 }
119
120 @GET
121 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.PAYLOAD)
122 @Override
123 public Response getTimeseries(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesContainerId,
124 @QueryParam(Constants.MEASUREMENT) @Parameter(required = true) String measurement,
125 @QueryParam(Constants.LOCATION) @Parameter(required = true) String location,
126 @QueryParam(Constants.DEVICE) @Parameter(required = true) String device,
127 @QueryParam(Constants.SYMBOLICNAME) @Parameter(required = true) String symbolicName,
128 @QueryParam(Constants.FIELD) @Parameter(required = true) String field,
129 @QueryParam(Constants.START) @Parameter(required = true) long start,
130 @QueryParam(Constants.END) @Parameter(required = true) long end,
131 @QueryParam(Constants.FUNCTION) SingleValuedUnaryFunction function,
132 @QueryParam(Constants.GROUP_BY) Long groupBy, @QueryParam(Constants.FILLOPTION) FillOption fillOption) {
133 if (measurement == null || location == null || device == null || symbolicName == null || field == null) {
134 throw new InvalidRequestException("Some query params are missing");
135 }
136
137 var timeseries = new Timeseries(measurement, device, location, symbolicName, field);
138 var result = timeseriesContainerService.getTimeseriesPayload(timeseriesContainerId, timeseries, start, end,
139 function, groupBy, fillOption);
140
141 return result != null ? Response.ok(result).build() : Response.status(Status.NOT_FOUND).build();
142 }
143
144 @GET
145 @Produces({ MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON })
146 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.EXPORT)
147 @Override
148 public Response exportTimeseries(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesContainerId,
149 @QueryParam(Constants.MEASUREMENT) @Parameter(required = true) String measurement,
150 @QueryParam(Constants.LOCATION) @Parameter(required = true) String location,
151 @QueryParam(Constants.DEVICE) @Parameter(required = true) String device,
152 @QueryParam(Constants.SYMBOLICNAME) @Parameter(required = true) String symbolicName,
153 @QueryParam(Constants.FIELD) @Parameter(required = true) String field,
154 @QueryParam(Constants.START) @Parameter(required = true) long start,
155 @QueryParam(Constants.END) @Parameter(required = true) long end,
156 @QueryParam(Constants.FUNCTION) SingleValuedUnaryFunction function,
157 @QueryParam(Constants.GROUP_BY) Long groupBy, @QueryParam(Constants.FILLOPTION) FillOption fillOption)
158 throws IOException {
159
160 if (measurement == null || location == null || device == null || symbolicName == null || field == null) {
161 throw new InvalidRequestException("Some query params are missing");
162 }
163
164 var timeseries = new Timeseries(measurement, device, location, symbolicName, field);
165 var result = timeseriesContainerService.exportTimeseriesPayload(timeseriesContainerId, timeseries, start, end,
166 function, groupBy, fillOption);
167 return result != null
168 ? Response.ok(result, MediaType.APPLICATION_OCTET_STREAM)
169 .header("Content-Disposition", "attachment; filename=\"timeseries-export.csv\"").build()
170 : Response.status(Status.NOT_FOUND).build();
171 }
172
173 @POST
174 @Consumes(MediaType.MULTIPART_FORM_DATA)
175 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.IMPORT)
176 @Subscribable
177 @Override
178 public Response importTimeseries(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesId,
179 @FormDataParam(Constants.FILE) InputStream fileInputStream,
180 @FormDataParam(Constants.FILE) FormDataContentDisposition fileMetaData) throws IOException {
181 var result = timeseriesContainerService.importTimeseries(timeseriesId, fileInputStream);
182
183 return result ? Response.ok().build() : Response.status(Status.INTERNAL_SERVER_ERROR).build();
184 }
185
186 @GET
187 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.PERMISSIONS)
188 @Override
189 public Response getTimeseriesPermissions(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesContainerId) {
190 var perms = permissionsService.getPermissionsByEntity(timeseriesContainerId);
191 return perms != null ? Response.ok(new PermissionsIO(perms)).build()
192 : Response.status(Status.NOT_FOUND).build();
193 }
194
195 @PUT
196 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.PERMISSIONS)
197 @Override
198 public Response editTimeseriesPermissions(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesContainerId,
199 @Valid PermissionsIO permissions) {
200 var perms = permissionsService.updatePermissions(permissions, timeseriesContainerId);
201 return perms != null ? Response.ok(new PermissionsIO(perms)).build()
202 : Response.status(Status.NOT_FOUND).build();
203 }
204
205 @GET
206 @Path("/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.ROLES)
207 @Override
208 public Response getTimeseriesRoles(@PathParam(Constants.TIMESERIES_CONTAINER_ID) long timeseriesContainerId) {
209 var roles = new PermissionsUtil().getRoles(timeseriesContainerId, securityContext.getUserPrincipal().getName());
210 return roles != null ? Response.ok(roles).build() : Response.status(Status.NOT_FOUND).build();
211 }
212 }