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 }