From bd96b836d78246e7dc055cebe585cce51f8a7595 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Tue, 3 Jan 2023 23:53:52 +0100 Subject: [PATCH] [DEV] better interface with search and filter --- back/pom.xml | 4 +- back/src/org/kar/karanage/WebLauncher.java | 12 ++ .../karanage/api/StateHistoryResource.java | 129 ++++++++++++++++++ .../org/kar/karanage/api/StateResource.java | 126 +++++++++++++---- .../org/kar/karanage/api/UserResource.java | 4 +- 5 files changed, 247 insertions(+), 28 deletions(-) create mode 100644 back/src/org/kar/karanage/api/StateHistoryResource.java diff --git a/back/pom.xml b/back/pom.xml index 75123c1..204d311 100644 --- a/back/pom.xml +++ b/back/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.kar karanage - 0.1.0 + 0.2.0 3.1 @@ -22,7 +22,7 @@ kangaroo-and-rabbit archidata - 0.1.5 + 0.2.0 diff --git a/back/src/org/kar/karanage/WebLauncher.java b/back/src/org/kar/karanage/WebLauncher.java index c73e4f3..d037560 100755 --- a/back/src/org/kar/karanage/WebLauncher.java +++ b/back/src/org/kar/karanage/WebLauncher.java @@ -11,11 +11,16 @@ import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; import org.kar.karanage.api.Front; import org.kar.karanage.api.HealthCheck; +import org.kar.karanage.api.StateHistoryResource; import org.kar.karanage.api.StateResource; import org.kar.karanage.api.UserResource; import org.kar.archidata.GlobalConfiguration; import org.kar.archidata.SqlWrapper; import org.kar.archidata.UpdateJwtPublicKey; +import org.kar.archidata.catcher.ExceptionCatcher; +import org.kar.archidata.catcher.FailExceptionCatcher; +import org.kar.archidata.catcher.InputExceptionCatcher; +import org.kar.archidata.catcher.SystemExceptionCatcher; import org.kar.archidata.filter.AuthenticationFilter; import org.kar.archidata.filter.CORSFilter; import org.kar.archidata.filter.OptionFilter; @@ -60,9 +65,16 @@ public class WebLauncher { rc.register(new CORSFilter()); // global authentication system rc.registerClasses(AuthenticationFilter.class); + // register exception catcher + rc.register(InputExceptionCatcher.class); + rc.register(SystemExceptionCatcher.class); + rc.register(FailExceptionCatcher.class); + rc.register(ExceptionCatcher.class); + // add default resource: rc.registerClasses(UserResource.class); rc.registerClasses(StateResource.class); + rc.registerClasses(StateHistoryResource.class); rc.registerClasses(HealthCheck.class); rc.registerClasses(Front.class); diff --git a/back/src/org/kar/karanage/api/StateHistoryResource.java b/back/src/org/kar/karanage/api/StateHistoryResource.java new file mode 100644 index 0000000..106662c --- /dev/null +++ b/back/src/org/kar/karanage/api/StateHistoryResource.java @@ -0,0 +1,129 @@ +package org.kar.karanage.api; + +import org.kar.archidata.SqlWrapper; +import org.kar.archidata.WhereCondition; +import org.kar.karanage.model.DataInstant; +import org.kar.karanage.model.DataHistory; +import org.kar.karanage.model.Group; +import javax.ws.rs.core.Response; + + +import org.kar.archidata.annotation.security.PermitAll; +import org.kar.archidata.exception.FailException; +import org.kar.archidata.exception.InputException; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + +import java.sql.Timestamp; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; + + +@Path("/state_history") +@Produces({MediaType.APPLICATION_JSON}) +public class StateHistoryResource { + + @GET + @Path("{group}/{topic:.*}") + //@RolesAllowed("USER") + @PermitAll + public static String get( + @PathParam("group") String groupName, + @PathParam("topic") String topic, + @QueryParam("since") String since, + @QueryParam("sinceId") Integer sinceId, + @QueryParam("limit") Integer limit) throws Exception { + // Keep Group ID: + if (groupName == null || groupName.length() == 0) { + throw new InputException("group", "Missing URL parameter /state/{group}/..."); + } + Group group = SqlWrapper.getWhere(Group.class, + List.of( + new WhereCondition("name", "=", groupName) + ), + false); + if (group == null) { + throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); + } + if (topic == null || topic.length() == 0) { + throw new InputException("topic", "Missing URL parameter /state/xxx/{topic}/..."); + } + if (limit == null || limit == 0) { + limit = 100; + } + if (limit > 100) { + limit = 100; + } + if (limit < 0) { + throw new InputException("limit", "Limit must be inside [1..100]"); + } + if (since != null && sinceId != null) { + throw new InputException("since,sinceId", "The 2 parameters can not be set at the same time..."); + } + + List datas = null; + if (since != null) { + Timestamp time = null; + try { + time = Timestamp.from(Instant.parse(since)); + } catch (Exception ex) { + throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); + } + datas = SqlWrapper.getsWhere(DataHistory.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("topic", "=", topic), + new WhereCondition("modify_date", ">", time) + ), + "modify_date", + true, + limit); + if (datas == null) { + throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'"); + } + } else if (sinceId != null) { + datas = SqlWrapper.getsWhere(DataHistory.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("topic", "=", topic), + new WhereCondition("id", ">", sinceId) + ), + "id", + true, + limit); + if (datas == null) { + throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'"); + } + } else { + datas = SqlWrapper.getsWhere(DataHistory.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("topic", "=", topic) + ), + "id", + true, + limit); + if (datas == null) { + throw new FailException(Response.Status.NOT_FOUND, "Topic does not exist: '" + topic + "'"); + } + } + + StringBuilder out = new StringBuilder("["); + boolean first = true; + for (DataInstant data : datas) { + if (first) { + first = false; + } else { + out.append(","); + } + out.append("{ \"id\": " + data.id + ", \"time\": \"" + data.modify_date.toInstant().toString() + "\", \"data\":" + data.data + "}"); + } + out.append("]"); + return out.toString(); + } + +} + diff --git a/back/src/org/kar/karanage/api/StateResource.java b/back/src/org/kar/karanage/api/StateResource.java index a8afc2c..0b7c23e 100644 --- a/back/src/org/kar/karanage/api/StateResource.java +++ b/back/src/org/kar/karanage/api/StateResource.java @@ -1,6 +1,7 @@ package org.kar.karanage.api; import org.kar.archidata.SqlWrapper; +import org.kar.archidata.WhereCondition; import org.kar.karanage.model.DataInstant; import org.kar.karanage.model.DataHistory; import org.kar.karanage.model.Group; @@ -8,6 +9,9 @@ import javax.ws.rs.core.Response; import org.kar.archidata.annotation.security.PermitAll; +import org.kar.archidata.exception.FailException; +import org.kar.archidata.exception.InputException; + import javax.ws.rs.*; import javax.ws.rs.core.MediaType; @@ -26,35 +30,94 @@ public class StateResource { @Path("{group}/{topic:.*}") //@RolesAllowed("USER") @PermitAll - public static String getWithTopic(@PathParam("group") String groupName, @PathParam("topic") String topic) throws Exception { - Group group = SqlWrapper.getWhere(Group.class, "name", "=", groupName); - if (group == null) { - throw new Exception("Missing Group !!!"); + public static String getWithTopic( + @PathParam("group") String groupName, + @PathParam("topic") String topic, + @QueryParam("since") String since) throws Exception { + if (groupName == null || groupName.length() == 0) { + throw new InputException("group", "Missing URL parameter /state/{group}/..."); } - DataInstant data = SqlWrapper.getWhere(DataInstant.class, "group", "=", group.id, "topic", "=", topic, true); - - return "{ \"time\": \"" + data.modify_date.toLocalDateTime().toString() + "\", \"data\":" + data.data + "}"; + Group group = SqlWrapper.getWhere(Group.class, + List.of( + new WhereCondition("name", "=", groupName) + ), + false); + if (group == null) { + throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); + } + DataInstant data = null; + if (since != null) { + Timestamp time = null; + try { + time = Timestamp.from(Instant.parse(since)); + } catch (Exception ex) { + throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); + } + data = SqlWrapper.getWhere(DataInstant.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("topic", "=", topic), + new WhereCondition("modify_date", ">", time) + ), + true); + if (data == null) { + throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'"); + } + } else { + data = SqlWrapper.getWhere(DataInstant.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("topic", "=", topic) + ), + true); + if (data == null) { + throw new FailException(Response.Status.NOT_FOUND, "Topic does not exist: '" + topic + "'"); + } + } + return "{ \"time\": \"" + data.modify_date.toInstant().toString() + "\", \"data\":" + data.data + "}"; } - /* - public static DataInstant getWithTopic(@PathParam("group") String groupName, @PathParam("topic") String topic) throws Exception { - Group group = SqlWrapper.getWhere(Group.class, "name", "=", groupName); - if (group == null) { - throw new Exception("Missing Group !!!"); - } - return SqlWrapper.getWhere(DataInstant.class, "group", "=", group.id, "topic", "=", topic); - }*/ @GET @Path("{group}") //@RolesAllowed("USER") @PermitAll - public String get(@PathParam("group") String groupName) throws Exception { - Group group = SqlWrapper.getWhere(Group.class, "name", "=", groupName); + public String get( + @PathParam("group") String groupName, + @QueryParam("since") String since) throws Exception { + if (groupName == null || groupName.length() == 0) { + throw new InputException("group", "Missing URL parameter /state/{group}/..."); + } + Group group = SqlWrapper.getWhere(Group.class, + List.of( + new WhereCondition("name", "=", groupName) + ), + false); if (group == null) { - throw new Exception("Missing Group !!!"); + throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); + } + + List datas = null; + if (since != null) { + Timestamp time = null; + try { + time = Timestamp.from(Instant.parse(since)); + } catch (Exception ex) { + throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); + } + datas = SqlWrapper.getsWhere(DataInstant.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("modify_date", ">", time) + ), + true); + } else { + datas = SqlWrapper.getsWhere(DataInstant.class, + List.of( + new WhereCondition("group", "=", group.id) + ), + true); } StringBuilder out = new StringBuilder("["); - List datas = SqlWrapper.getsWhere(DataInstant.class, "group", "=", group.id, true); boolean first = true; for (DataInstant data : datas) { if (first) { @@ -62,7 +125,7 @@ public class StateResource { } else { out.append(","); } - out.append("{ \"time\": \"" + data.modify_date.toLocalDateTime().toString() + "\", \"topic\": \"" + data.topic + "\", \"data\":" + data.data + "}"); + out.append("{ \"time\": \"" + data.modify_date.toInstant().toString() + "\", \"topic\": \"" + data.topic + "\", \"data\":" + data.data + "}"); } out.append("]"); return out.toString(); @@ -82,11 +145,27 @@ public class StateResource { //@RolesAllowed("ADMIN") @PermitAll @Consumes(MediaType.APPLICATION_JSON) - public Response post(@PathParam("group") String groupName, @PathParam("topic") String topic, String dataToInsert) throws Exception { + public Response post( + @PathParam("group") String groupName, + @PathParam("topic") String topic, + String dataToInsert) throws Exception { DataInstant previous = null; - Group group = SqlWrapper.getWhere(Group.class, "name", "=", groupName); + + Group group = SqlWrapper.getWhere(Group.class, + List.of( + new WhereCondition("name", "=", groupName) + ), + false); + if (group == null) { + throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); + } if (group != null) { - previous = SqlWrapper.getWhere(DataInstant.class, "group", "=", group.id, "topic", "=", topic, true); + previous = SqlWrapper.getWhere(DataInstant.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("topic", "=", topic) + ), + true); } if (previous == null) { // insert new data @@ -111,7 +190,6 @@ public class StateResource { DataHistory dataHistory = new DataHistory(previous); OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ); OffsetDateTime sqlTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(previous.modify_date.getTime()), ZoneOffset.UTC); - //tstamp = Timestamp.valueOf(ofsdatetime.atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime()) now.getDayOfMonth(); if (sqlTime.getYear() != now.getYear()) { diff --git a/back/src/org/kar/karanage/api/UserResource.java b/back/src/org/kar/karanage/api/UserResource.java index 94f7cff..ecad9ad 100755 --- a/back/src/org/kar/karanage/api/UserResource.java +++ b/back/src/org/kar/karanage/api/UserResource.java @@ -1,11 +1,11 @@ package org.kar.karanage.api; -import org.kar.archidata.SqlWrapper; import org.kar.archidata.filter.GenericContext; import org.kar.archidata.model.User; import org.kar.karanage.model.UserKaranage; - +import org.kar.archidata.SqlWrapper; import org.kar.archidata.annotation.security.RolesAllowed; + import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType;