From 95902e731226dc95013b4ebca6d10e2e2f11650a Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Fri, 13 Jan 2023 00:36:47 +0100 Subject: [PATCH] [DEV] upgrade client and back to add basic api for logs --- back/pom.xml | 4 +- back/src/org/kar/karanage/WebLauncher.java | 12 +- .../src/org/kar/karanage/api/LogResource.java | 167 ++++++++++++++++++ .../karanage/api/StateHistoryResource.java | 14 +- .../org/kar/karanage/api/StateResource.java | 35 ++-- back/src/org/kar/karanage/model/DataLog.java | 43 +++++ .../{DataHistory.java => StateHistory.java} | 8 +- .../{DataInstant.java => StateInstant.java} | 4 +- .../python/karanage-tools/bin/karanage-system | 41 ++--- .../bin/karanage-tools-state-get | 46 ++--- .../bin/karanage-tools-state-history-get | 42 ++--- client/python/karanage-tools/version.txt | 2 +- client/python/karanage/karanage/__init__.py | 5 +- client/python/karanage/karanage/connection.py | 75 ++++++++ client/python/karanage/karanage/exception.py | 21 +++ client/python/karanage/karanage/interface.py | 151 ---------------- client/python/karanage/karanage/log.py | 52 ++++++ client/python/karanage/karanage/state.py | 96 ++++++++++ client/python/karanage/setup.py | 3 +- client/python/karanage/version.txt | 2 +- readme.md | 5 +- 21 files changed, 554 insertions(+), 274 deletions(-) create mode 100644 back/src/org/kar/karanage/api/LogResource.java create mode 100644 back/src/org/kar/karanage/model/DataLog.java rename back/src/org/kar/karanage/model/{DataHistory.java => StateHistory.java} (93%) rename back/src/org/kar/karanage/model/{DataInstant.java => StateInstant.java} (97%) create mode 100644 client/python/karanage/karanage/connection.py create mode 100644 client/python/karanage/karanage/exception.py delete mode 100644 client/python/karanage/karanage/interface.py create mode 100644 client/python/karanage/karanage/log.py create mode 100644 client/python/karanage/karanage/state.py diff --git a/back/pom.xml b/back/pom.xml index cc44aa8..8fd6380 100644 --- a/back/pom.xml +++ b/back/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.kar karanage - 0.2.1 + 0.2.2 3.1 @@ -22,7 +22,7 @@ kangaroo-and-rabbit archidata - 0.2.1 + 0.2.4 diff --git a/back/src/org/kar/karanage/WebLauncher.java b/back/src/org/kar/karanage/WebLauncher.java index d037560..19f7c3c 100755 --- a/back/src/org/kar/karanage/WebLauncher.java +++ b/back/src/org/kar/karanage/WebLauncher.java @@ -11,6 +11,7 @@ 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.LogResource; import org.kar.karanage.api.StateHistoryResource; import org.kar.karanage.api.StateResource; import org.kar.karanage.api.UserResource; @@ -25,8 +26,9 @@ import org.kar.archidata.filter.AuthenticationFilter; import org.kar.archidata.filter.CORSFilter; import org.kar.archidata.filter.OptionFilter; import org.kar.archidata.util.ConfigBaseVariable; -import org.kar.karanage.model.DataHistory; -import org.kar.karanage.model.DataInstant; +import org.kar.karanage.model.StateHistory; +import org.kar.karanage.model.StateInstant; +import org.kar.karanage.model.DataLog; import org.kar.karanage.model.Group; public class WebLauncher { @@ -43,9 +45,10 @@ public class WebLauncher { // generate the BDD: try { String out = ""; - out += SqlWrapper.createTable(DataInstant.class); - out += SqlWrapper.createTable(DataHistory.class); + out += SqlWrapper.createTable(StateInstant.class); + out += SqlWrapper.createTable(StateHistory.class); out += SqlWrapper.createTable(Group.class); + out += SqlWrapper.createTable(DataLog.class); System.out.println(out); } catch (Exception e) { // TODO Auto-generated catch block @@ -75,6 +78,7 @@ public class WebLauncher { rc.registerClasses(UserResource.class); rc.registerClasses(StateResource.class); rc.registerClasses(StateHistoryResource.class); + rc.registerClasses(LogResource.class); rc.registerClasses(HealthCheck.class); rc.registerClasses(Front.class); diff --git a/back/src/org/kar/karanage/api/LogResource.java b/back/src/org/kar/karanage/api/LogResource.java new file mode 100644 index 0000000..50e6f13 --- /dev/null +++ b/back/src/org/kar/karanage/api/LogResource.java @@ -0,0 +1,167 @@ +package org.kar.karanage.api; + +import org.kar.archidata.SqlWrapper; +import org.kar.archidata.WhereCondition; +import org.kar.karanage.model.StateInstant; +import org.kar.karanage.model.DataLog; +import org.kar.karanage.model.StateHistory; +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("/log") +@Produces({MediaType.APPLICATION_JSON}) +public class LogResource { + + + @GET + @Path("{group}/{system}") + //@RolesAllowed("USER") + @PermitAll + public static String get( + @PathParam("group") String groupName, + @PathParam("system") String system, + @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 (system == null || system.length() == 0) { + throw new InputException("system", "Missing URL parameter /state/xxx/{system}/..."); + } + 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(DataLog.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("system", "=", system), + new WhereCondition("create_date", ">", time) + ), + "create_date", + true, + limit); + if (datas == null) { + throw new FailException(Response.Status.NOT_FOUND, "system has no new data or does not exist: '" + system + "'"); + } + } else if (sinceId != null) { + datas = SqlWrapper.getsWhere(DataLog.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("system", "=", system), + new WhereCondition("id", ">", sinceId) + ), + "id", + true, + limit); + if (datas == null) { + throw new FailException(Response.Status.NOT_FOUND, "system has no new data or does not exist: '" + system + "'"); + } + } else { + datas = SqlWrapper.getsWhere(DataLog.class, + List.of( + new WhereCondition("group", "=", group.id), + new WhereCondition("system", "=", system) + ), + "id", + true, + limit); + if (datas == null) { + throw new FailException(Response.Status.NOT_FOUND, "system does not exist: '" + system + "'"); + } + } + + StringBuilder out = new StringBuilder("["); + boolean first = true; + for (DataLog data : datas) { + if (first) { + first = false; + } else { + out.append(","); + } + out.append("{ \"id\": " + data.id + ", \"time\": \"" + data.create_date.toInstant().toString() + "\", \"data\":" + data.data + "}"); + } + out.append("]"); + return out.toString(); + } + + @POST + @Path("{group}/{system}") + @PermitAll + @Consumes(MediaType.APPLICATION_JSON) + public Response post( + @PathParam("group") String groupName, + @PathParam("system") String system, + @QueryParam("time") String time, + @QueryParam("id") Integer client_id, + String dataToInsert) throws Exception { + Group group = SqlWrapper.getWhere(Group.class, + List.of( + new WhereCondition("name", "=", groupName) + ), + false); + if (group == null) { + throw new InputException("group", "url: /log/{group}/... ==> Unknown group name"); + } + DataLog data = new DataLog(); + data.group = group.id; + data.system = system; + data.data = dataToInsert; + if (client_id != null) { + data.client_id = client_id; + } + if (time != null) { + try { + data.create_date = Timestamp.from(Instant.parse(time)); + } catch (Exception ex) { + throw new InputException("time", "url: ?time=... ==> is not an iso 8601 time format"); + } + } + SqlWrapper.insert(data); + return Response.status(201).build(); + } + +} + diff --git a/back/src/org/kar/karanage/api/StateHistoryResource.java b/back/src/org/kar/karanage/api/StateHistoryResource.java index 106662c..753cfeb 100644 --- a/back/src/org/kar/karanage/api/StateHistoryResource.java +++ b/back/src/org/kar/karanage/api/StateHistoryResource.java @@ -2,8 +2,8 @@ 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.StateInstant; +import org.kar.karanage.model.StateHistory; import org.kar.karanage.model.Group; import javax.ws.rs.core.Response; @@ -64,7 +64,7 @@ public class StateHistoryResource { throw new InputException("since,sinceId", "The 2 parameters can not be set at the same time..."); } - List datas = null; + List datas = null; if (since != null) { Timestamp time = null; try { @@ -72,7 +72,7 @@ public class StateHistoryResource { } catch (Exception ex) { throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); } - datas = SqlWrapper.getsWhere(DataHistory.class, + datas = SqlWrapper.getsWhere(StateHistory.class, List.of( new WhereCondition("group", "=", group.id), new WhereCondition("topic", "=", topic), @@ -85,7 +85,7 @@ public class StateHistoryResource { 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, + datas = SqlWrapper.getsWhere(StateHistory.class, List.of( new WhereCondition("group", "=", group.id), new WhereCondition("topic", "=", topic), @@ -98,7 +98,7 @@ public class StateHistoryResource { throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'"); } } else { - datas = SqlWrapper.getsWhere(DataHistory.class, + datas = SqlWrapper.getsWhere(StateHistory.class, List.of( new WhereCondition("group", "=", group.id), new WhereCondition("topic", "=", topic) @@ -113,7 +113,7 @@ public class StateHistoryResource { StringBuilder out = new StringBuilder("["); boolean first = true; - for (DataInstant data : datas) { + for (StateInstant data : datas) { if (first) { first = false; } else { diff --git a/back/src/org/kar/karanage/api/StateResource.java b/back/src/org/kar/karanage/api/StateResource.java index 0b7c23e..b7bf45c 100644 --- a/back/src/org/kar/karanage/api/StateResource.java +++ b/back/src/org/kar/karanage/api/StateResource.java @@ -2,8 +2,8 @@ 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.StateInstant; +import org.kar.karanage.model.StateHistory; import org.kar.karanage.model.Group; import javax.ws.rs.core.Response; @@ -45,7 +45,7 @@ public class StateResource { if (group == null) { throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); } - DataInstant data = null; + StateInstant data = null; if (since != null) { Timestamp time = null; try { @@ -53,7 +53,7 @@ public class StateResource { } catch (Exception ex) { throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); } - data = SqlWrapper.getWhere(DataInstant.class, + data = SqlWrapper.getWhere(StateInstant.class, List.of( new WhereCondition("group", "=", group.id), new WhereCondition("topic", "=", topic), @@ -64,7 +64,7 @@ public class StateResource { throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'"); } } else { - data = SqlWrapper.getWhere(DataInstant.class, + data = SqlWrapper.getWhere(StateInstant.class, List.of( new WhereCondition("group", "=", group.id), new WhereCondition("topic", "=", topic) @@ -96,7 +96,7 @@ public class StateResource { throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); } - List datas = null; + List datas = null; if (since != null) { Timestamp time = null; try { @@ -104,14 +104,14 @@ public class StateResource { } catch (Exception ex) { throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); } - datas = SqlWrapper.getsWhere(DataInstant.class, + datas = SqlWrapper.getsWhere(StateInstant.class, List.of( new WhereCondition("group", "=", group.id), new WhereCondition("modify_date", ">", time) ), true); } else { - datas = SqlWrapper.getsWhere(DataInstant.class, + datas = SqlWrapper.getsWhere(StateInstant.class, List.of( new WhereCondition("group", "=", group.id) ), @@ -119,7 +119,7 @@ public class StateResource { } StringBuilder out = new StringBuilder("["); boolean first = true; - for (DataInstant data : datas) { + for (StateInstant data : datas) { if (first) { first = false; } else { @@ -147,9 +147,10 @@ public class StateResource { @Consumes(MediaType.APPLICATION_JSON) public Response post( @PathParam("group") String groupName, - @PathParam("topic") String topic, + @PathParam("topic") String topic, + @QueryParam("state") String state, String dataToInsert) throws Exception { - DataInstant previous = null; + StateInstant previous = null; Group group = SqlWrapper.getWhere(Group.class, List.of( @@ -160,7 +161,7 @@ public class StateResource { throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); } if (group != null) { - previous = SqlWrapper.getWhere(DataInstant.class, + previous = SqlWrapper.getWhere(StateInstant.class, List.of( new WhereCondition("group", "=", group.id), new WhereCondition("topic", "=", topic) @@ -169,12 +170,13 @@ public class StateResource { } if (previous == null) { // insert new data - DataInstant data = new DataInstant(); + StateInstant data = new StateInstant(); data.group = group.id; data.topic = topic; data.data = dataToInsert; + data.state = state; previous = SqlWrapper.insert(data); - DataHistory dataHistory = new DataHistory(previous); + StateHistory dataHistory = new StateHistory(previous); dataHistory.year = true; dataHistory.month = true; dataHistory.week = true; @@ -185,9 +187,10 @@ public class StateResource { // update Data try { previous.data = dataToInsert; - SqlWrapper.update(previous, previous.id, List.of("data")); + previous.state = state; + SqlWrapper.update(previous, previous.id, List.of("data", "state")); // add it in history: - DataHistory dataHistory = new DataHistory(previous); + StateHistory dataHistory = new StateHistory(previous); OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ); OffsetDateTime sqlTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(previous.modify_date.getTime()), ZoneOffset.UTC); diff --git a/back/src/org/kar/karanage/model/DataLog.java b/back/src/org/kar/karanage/model/DataLog.java new file mode 100644 index 0000000..b5e2241 --- /dev/null +++ b/back/src/org/kar/karanage/model/DataLog.java @@ -0,0 +1,43 @@ +package org.kar.karanage.model; + +import java.sql.Timestamp; + +import org.kar.archidata.annotation.SQLAutoIncrement; +import org.kar.archidata.annotation.SQLComment; +import org.kar.archidata.annotation.SQLDefault; +import org.kar.archidata.annotation.SQLForeignKey; +import org.kar.archidata.annotation.SQLIfNotExists; +import org.kar.archidata.annotation.SQLLimitSize; +import org.kar.archidata.annotation.SQLNotNull; +import org.kar.archidata.annotation.SQLNotRead; +import org.kar.archidata.annotation.SQLPrimaryKey; +import org.kar.archidata.annotation.SQLTableName; +import org.kar.archidata.annotation.SQLUpdateTime; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@SQLTableName ("log") +@SQLIfNotExists +@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +public class DataLog { + @SQLAutoIncrement // Add AUTO_INCREMENT modifier + @SQLPrimaryKey // Create a PRIMARY KEY based on this field + @SQLNotNull + @SQLComment("Primary key of the base") + public Long id = null; + @SQLUpdateTime + @SQLNotNull + @SQLDefault("now(3)") + @SQLComment("When update the object") + public Timestamp create_date = null; + @SQLComment("Group of the element") + @SQLForeignKey("group") + public long group; + @SQLLimitSize(256) + @SQLComment("System of the message") + public String system = null; + @SQLComment("Client unique internal ID (for synchronisation system)") + public Integer client_id = null; + @SQLComment("Message to store") + public String data = null; +} \ No newline at end of file diff --git a/back/src/org/kar/karanage/model/DataHistory.java b/back/src/org/kar/karanage/model/StateHistory.java similarity index 93% rename from back/src/org/kar/karanage/model/DataHistory.java rename to back/src/org/kar/karanage/model/StateHistory.java index 64a364c..f3f5d0f 100644 --- a/back/src/org/kar/karanage/model/DataHistory.java +++ b/back/src/org/kar/karanage/model/StateHistory.java @@ -36,15 +36,15 @@ enum TypeData { NUMBER, BOOLEAN, JSON, STRING } -@SQLTableName ("dataHistory") +@SQLTableName ("stateHistory") @SQLIfNotExists @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) -public class DataHistory extends DataInstant { +public class StateHistory extends StateInstant { // default constructor - public DataHistory() { + public StateHistory() { } - public DataHistory(DataInstant other) { + public StateHistory(StateInstant other) { this.group = other.group; this.topic = other.topic; this.state = other.state; diff --git a/back/src/org/kar/karanage/model/DataInstant.java b/back/src/org/kar/karanage/model/StateInstant.java similarity index 97% rename from back/src/org/kar/karanage/model/DataInstant.java rename to back/src/org/kar/karanage/model/StateInstant.java index 98cbb2b..cd2dc35 100644 --- a/back/src/org/kar/karanage/model/DataInstant.java +++ b/back/src/org/kar/karanage/model/StateInstant.java @@ -27,10 +27,10 @@ import org.kar.archidata.annotation.SQLUpdateTime; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -@SQLTableName ("data") +@SQLTableName ("state") @SQLIfNotExists @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) -public class DataInstant { +public class StateInstant { @SQLAutoIncrement // Add AUTO_INCREMENT modifier @SQLPrimaryKey // Create a PRIMARY KEY based on this field @SQLNotNull diff --git a/client/python/karanage-tools/bin/karanage-system b/client/python/karanage-tools/bin/karanage-system index 8777de1..fc25251 100755 --- a/client/python/karanage-tools/bin/karanage-system +++ b/client/python/karanage-tools/bin/karanage-system @@ -7,7 +7,7 @@ import subprocess import json from pathlib import Path from typing import Dict, List -import karanage +from karanage import KaranageState, KaranageConnection, KaranageException, StateSystem cpu_core_count = psutil.cpu_count(logical=False) cpu_thread_count = psutil.cpu_count() @@ -122,12 +122,12 @@ if __name__ == '__main__': # Load arguments: parser = argparse.ArgumentParser() parser.add_argument("-c", "--config", type=str, default="/etc/karanage/system.json", help="json configuration file") - parser.add_argument("-C", "--connection", type=str, default="/etc/karanage/connection.json", help="json configuration file") + parser.add_argument("-C", "--connection", type=str, default=None, help="json configuration file") # This element are read from the connection file: - parser.add_argument("-u", "--url", type=str, default="http://localhost:20080/karanage/api", help="Base URL of the web service") - parser.add_argument("-g", "--group", type=str, default="home", help="Group the the message") - parser.add_argument("-T", "--token", type=str, default="", help="Token to access to the server") + parser.add_argument("-u", "--url", type=str, default=None, help="Base URL of the web service") + parser.add_argument("-g", "--group", type=str, default=None, help="Group the the message") + parser.add_argument("-T", "--token", type=str, default=None, help="Token to access to the server") # This element are read from the configuration file: parser.add_argument("-t", "--topic", type=str, default="PC/system", help="Topic of the message") @@ -158,26 +158,15 @@ if __name__ == '__main__': if "topic" not in configuration["config"]: configuration["config"]["topic"] = args.topic + connection = KaranageConnection( + url = args.url, + group = args.group, + token = args.token, + config_file = args.connection + ) - if Path(args.connection).exists(): - f = open(args.connection, "r") - connection = json.loads(f.read()) - f.close() - else: - connection = {} - # manage the connection model - if "url" not in connection: - connection["url"] = args.url - if "group" not in connection: - connection["group"] = args.group - if "token" not in connection: - connection["token"] = args.token - # create the rest interface of karanage - restInterface = karanage.KaranageREST( - connection["url"], - connection["group"], - connection["token"]) + stateInterface = KaranageState(connection) while True: out = {} @@ -205,7 +194,7 @@ if __name__ == '__main__': print(json.dumps(out, indent=4)) # send message to the server: try: - restInterface.send_state_to_server(configuration["config"]["topic"], out) - except karanage.KarangeException as ex: + stateInterface.send(configuration["config"]["topic"], out, state = StateSystem.OK) + except KaranageException as ex: print(f"Can not send to the server: {ex}") - time.sleep(configuration["config"]["sleep"]) \ No newline at end of file + time.sleep(configuration["config"]["sleep"]) diff --git a/client/python/karanage-tools/bin/karanage-tools-state-get b/client/python/karanage-tools/bin/karanage-tools-state-get index 37dc424..850df34 100755 --- a/client/python/karanage-tools/bin/karanage-tools-state-get +++ b/client/python/karanage-tools/bin/karanage-tools-state-get @@ -7,45 +7,33 @@ import subprocess import json from pathlib import Path from typing import Dict, List -import karanage +from karanage import KaranageException, KaranageConnection, KaranageState + if __name__ == '__main__': # Load arguments: parser = argparse.ArgumentParser() - parser.add_argument("-C", "--connection", type=str, default="/etc/karanage/connection.json", help="json configuration file") - parser.add_argument("-t", "--topic", type=str, default="", help="Topic of the message") + parser.add_argument("-C", "--connection", type=str, default=None, help="json configuration file") + parser.add_argument("-t", "--topic", type=str, default=None, help="Topic of the message") parser.add_argument("-s", "--since", type=str, default=None, help="Iso date since the value time must be") # This element are read from the connection file: - parser.add_argument("-u", "--url", type=str, default="http://localhost:20080/karanage/api", help="Base URL of the web service") - parser.add_argument("-g", "--group", type=str, default="home", help="Group the the message") - parser.add_argument("-T", "--token", type=str, default="", help="Token to access to the server") + parser.add_argument("-u", "--url", type=str, default=None, help="Base URL of the web service") + parser.add_argument("-g", "--group", type=str, default=None, help="Group the the message") + parser.add_argument("-T", "--token", type=str, default=None, help="Token to access to the server") args = parser.parse_args() - if Path(args.connection).exists(): - f = open(args.connection, "r") - connection = json.loads(f.read()) - f.close() - else: - connection = {} - # manage the connection model - if "url" not in connection: - connection["url"] = args.url - if "group" not in connection: - connection["group"] = args.group - if "token" not in connection: - connection["token"] = args.token + connection = KaranageConnection( + url = args.url, + group = args.group, + token = args.token, + config_file = args.connection + ) # create the rest interface of karanage - restInterface = karanage.KaranageREST( - connection["url"], - connection["group"], - connection["token"]) + stateInterface = KaranageState(connection) - if args.topic == "": - data = restInterface.get_state_all(since=args.since) - print(f"Ret = {json.dumps(data, indent=4)}") - else: - data = restInterface.get_state_topic(args.topic, since=args.since) - print(f"Ret = {json.dumps(data, indent=4)}") \ No newline at end of file + data = stateInterface.gets(topic=args.topic, since=args.since) + print(f"Ret = {json.dumps(data, indent=4)}") + diff --git a/client/python/karanage-tools/bin/karanage-tools-state-history-get b/client/python/karanage-tools/bin/karanage-tools-state-history-get index c7411ab..adebff9 100755 --- a/client/python/karanage-tools/bin/karanage-tools-state-history-get +++ b/client/python/karanage-tools/bin/karanage-tools-state-history-get @@ -7,46 +7,36 @@ import subprocess import json from pathlib import Path from typing import Dict, List -import karanage +from karanage import KaranageConnection, KaranageState, KaranageState if __name__ == '__main__': # Load arguments: parser = argparse.ArgumentParser() - parser.add_argument("-C", "--connection", type=str, default="/etc/karanage/connection.json", help="json configuration file") - parser.add_argument("-t", "--topic", type=str, default="", help="Topic of the message") + parser.add_argument("-C", "--connection", type=str, default=None, help="json configuration file") + parser.add_argument("-t", "--topic", type=str, default=None, help="Topic of the message") parser.add_argument("-s", "--since", type=str, default=None, help="Iso date since the value time must be") parser.add_argument("-S", "--since-id", type=str, default=None, help="Remote BDD id to start request") parser.add_argument("-l", "--limit", type=int, default=100, help="Limit the number of request") # This element are read from the connection file: - parser.add_argument("-u", "--url", type=str, default="http://localhost:20080/karanage/api", help="Base URL of the web service") - parser.add_argument("-g", "--group", type=str, default="home", help="Group the the message") - parser.add_argument("-T", "--token", type=str, default="", help="Token to access to the server") + parser.add_argument("-u", "--url", type=str, default=None, help="Base URL of the web service") + parser.add_argument("-g", "--group", type=str, default=None, help="Group the the message") + parser.add_argument("-T", "--token", type=str, default=None, help="Token to access to the server") args = parser.parse_args() - if Path(args.connection).exists(): - f = open(args.connection, "r") - connection = json.loads(f.read()) - f.close() - else: - connection = {} - # manage the connection model - if "url" not in connection: - connection["url"] = args.url - if "group" not in connection: - connection["group"] = args.group - if "token" not in connection: - connection["token"] = args.token - + connection = KaranageConnection( + url = args.url, + group = args.group, + token = args.token, + config_file = args.connection + ) + # create the rest interface of karanage - restInterface = karanage.KaranageREST( - connection["url"], - connection["group"], - connection["token"]) + stateInterface = KaranageState(connection) - if args.topic == "": + if args.topic is None: print("Missing TOPIC ...") else: - data = restInterface.get_state_history_topic(args.topic, since=args.since, since_id=args.since_id, limit=args.limit) + data = stateInterface.get_history(topic=args.topic, since=args.since, since_id=args.since_id, limit=args.limit) print(f"Ret = {json.dumps(data, indent=4)}") \ No newline at end of file diff --git a/client/python/karanage-tools/version.txt b/client/python/karanage-tools/version.txt index b4f09dd..0ba2319 100644 --- a/client/python/karanage-tools/version.txt +++ b/client/python/karanage-tools/version.txt @@ -1 +1 @@ -0.2.0-dev \ No newline at end of file +0.3.0-dev \ No newline at end of file diff --git a/client/python/karanage/karanage/__init__.py b/client/python/karanage/karanage/__init__.py index 35cc386..4bac672 100755 --- a/client/python/karanage/karanage/__init__.py +++ b/client/python/karanage/karanage/__init__.py @@ -7,4 +7,7 @@ ## ## @license MPL v2.0 (see license file) ## -from .interface import StateSystem, KaranageREST, KarangeSendError, KarangeException +from .exception import KaranageException +from .connection import KaranageConnection +from .state import StateSystem, KaranageState +from .log import KaranageLog diff --git a/client/python/karanage/karanage/connection.py b/client/python/karanage/karanage/connection.py new file mode 100644 index 0000000..49131fb --- /dev/null +++ b/client/python/karanage/karanage/connection.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2023, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## +import enum +import requests +import json +from typing import Dict, Optional +from pathlib import Path + + +class KaranageConnection: + def __init__(self, + url: Optional[str] = None, + group: Optional[str] = None, + token: Optional[str] = None, + config_file: Optional[str] = None, + default_values: Optional[str] = None) -> None: + """ + @brief Initialize the communication class. + @param[in] url URL of the karanage API server. + @param[in] group Group of the message (token need to have the autorisation to pubhied on it). + @param[in] token Token to validate the access on the application. + @param[in] config_file path to the configuration file if overload. + @param[in] default_values Default configurations. + """ + self.url = "http://localhost:20080/karanage/api" + self.group = "test" + self.token = None + # load user default value: + if default_values is not None: + if "url" in default_values: + self.url = default_values["url"] + if "group" in default_values: + self.group = default_values["group"] + if "token" in default_values: + self.token = default_values["token"] + # keep correct config file: + if config_file is None: + config_file = "/etc/karanage/connection.json" + # check if the config exist: + if Path(config_file).exists(): + f = open(config_file, "r") + configuaration = json.loads(f.read()) + f.close() + else: + configuaration = {} + # Update data with config file: + if "url" in configuaration: + self.url = configuaration["url"] + if "group" in configuaration: + self.group = configuaration["group"] + if "token" in configuaration: + self.token = configuaration["token"] + # set user command - line configuration: + if url is not None: + self.url = url + if group is not None: + self.group = group + if token is not None: + self.token = token + + def get_url(self, service: str): + return f"{self.url}/{service}/{self.group}" + + def get_header(self): + header = {} + if self.token is not None and len(self.token) >15: + header['Authorization'] = f"zota {self.token}" + return header \ No newline at end of file diff --git a/client/python/karanage/karanage/exception.py b/client/python/karanage/karanage/exception.py new file mode 100644 index 0000000..e0693f2 --- /dev/null +++ b/client/python/karanage/karanage/exception.py @@ -0,0 +1,21 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2023, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## + +class KaranageException(Exception): + def __init__(self, message, error_id, error_message): + # Call the base class constructor with the parameters it needs + super().__init__(message) + + # Now for your custom code... + self.error_id = error_id + self.error_message = error_message + + def __str__(self): + return f"{Exception.__str__(self)} Status={self.error_id} message='{self.error_message}'" diff --git a/client/python/karanage/karanage/interface.py b/client/python/karanage/karanage/interface.py deleted file mode 100644 index 928a42b..0000000 --- a/client/python/karanage/karanage/interface.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -## -## @author Edouard DUPIN -## -## @copyright 2023, Edouard DUPIN, all right reserved -## -## @license MPL v2.0 (see license file) -## -import enum -import requests -import json -from typing import Dict, Optional - -class KarangeSendError(Exception): - def __init__(self, message, error_id, error_message): - # Call the base class constructor with the parameters it needs - super().__init__(message) - - # Now for your custom code... - self.error_id = error_id - self.error_message = error_message - - def __str__(self): - return f"{Exception.__str__(self)} Status={self.error_id} message='{self.error_message}'" - -class KarangeException(KarangeSendError): - def __init__(self, message, error_id, error_message): - # Call the base class constructor with the parameters it needs - super().__init__(message, error_id, error_message ) - - - -class StateSystem(enum.Enum): - OK = "OK" - FAIL = "FAIL" - DOWN = "DOWN" - - - -## Generic karanage sending system. -class KaranageREST: - def __init__(self, url: str, group: str, token: str) -> None: - """ - @brief Initialize the communication class. - @param[in] url URL of the karanage API server. - @param[in] group Group of the message (token need to have the autorisation to pubhied on it). - @param[in] token Token to validate the access on the application. - """ - self.url = url - self.group = group - self.token = token - - def get_url(self, service: str, topic: Optional[str] = None): - if topic is None: - return f"{self.url}/{service}/{self.group}" - return f"{self.url}/{service}/{self.group}/{topic}" - - def send_state_to_server(self, topic: str, data: Optional[Dict], state: StateSystem = StateSystem.OK) -> None: - """ - @brief Send a message to the server. - @param[in] topic Topic where to publish the data. - @param[in] data: Data to send to the server - @param[in] state: State of the current system - """ - if data is None: - data = {} - param = { - "state": state, - } - header = {} - if self.token is not None and len(self.token) >15: - header['Authorization'] = f"zota {self.token}" - try: - ret = requests.post(self.get_url("state", topic), json=data, headers=header, params=param) - except requests.exceptions.ConnectionError as ex: - raise KarangeException(f"Fail connect server: {self.get_url('state', topic)}", 0, str(ex)) - if 200 <= ret.status_code <= 299: - pass - else: - raise KarangeException(f"Fail send message: {self.get_url('state', topic)}", ret.status_code, ret.content.decode("utf-8")) - - def get_state_all(self, since: Optional[str] = None) -> Dict: - """ - @brief Get all the topic fom the server. - @param since ISO1866 time value. - @return A dictionnary with the requested data. - """ - param = { } - header = { } - if self.token is not None and len(self.token) >15: - header['Authorization'] = f"zota {self.token}" - if since is not None: - param["since"] = since - ret = requests.get(self.get_url("state"), headers=header, params=param) - if 200 == ret.status_code: - return json.loads(ret.content.decode('utf-8')) - raise KarangeException(f"Fail get data: {self.get_url('state')}", ret.status_code, ret.content.decode("utf-8")) - - def get_state_topic(self, topic: str, since: Optional[str] = None) -> Dict: - """ - @brief Get all the topic fom the server. - @param since ISO1866 time value. - @return A dictionnary with the requested data. - """ - param = { } - header = { } - if self.token is not None and len(self.token) >15: - header['Authorization'] = f"zota {self.token}" - if since is not None: - param["since"] = since - ret = requests.get(self.get_url("state", topic), headers=header, params=param) - #print(ret.content.decode('utf-8')) - if 200 == ret.status_code: - return json.loads(ret.content.decode('utf-8')) - raise KarangeException(f"Fail get data: {self.get_url('state', topic)}", ret.status_code, ret.content.decode("utf-8")) - - def get_state_history_topic(self, topic: str, since: Optional[str] = None, since_id: Optional[int] = None, limit: Optional[int] = None) -> Dict: - """ - @brief Get all the topic fom the server. - @param since ISO1866 time value. - @param since_id remote BDD index of tje fielf. - @param limit Number of value we want to get - @return A dictionnary with the requested data. - """ - param = { } - header = { } - if self.token is not None and len(self.token) >15: - header['Authorization'] = f"zota {self.token}" - if since is not None: - param["since"] = since - if since_id is not None: - param["sinceId"] = since_id - if limit is not None: - param["limit"] = limit - ret = requests.get(self.get_url("state_history", topic), headers=header, params=param) - #print(ret.content.decode('utf-8')) - if 200 == ret.status_code: - return json.loads(ret.content.decode('utf-8')) - raise KarangeException(f"Fail get data: {self.get_url('state_history', topic)}", ret.status_code, ret.content.decode("utf-8")) - - - def get_all(self) -> Dict: - """Deprecated""" - return self.get_state_all() - def get_topic(self, topic: str) -> Dict: - """Deprecated""" - return self.get_state_topic(topic) - def send_to_server(self, topic: str, data: Optional[Dict], state: StateSystem = StateSystem.OK) -> None: - """Deprecated""" - return self.send_state_to_server(topic, data, state) \ No newline at end of file diff --git a/client/python/karanage/karanage/log.py b/client/python/karanage/karanage/log.py new file mode 100644 index 0000000..0bdac83 --- /dev/null +++ b/client/python/karanage/karanage/log.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2023, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## +import enum +import requests +import json +from typing import Dict, Optional +from .connection import KaranageConnection +from .exception import KaranageException + +## Generic karanage sending system. +class KaranageLog: + def __init__(self, connection: KaranageConnection, system: Optional[str] = None) -> None: + """ + @brief Initialize the communication class. + @param[in] connection Connection interface. + """ + self.connection = connection + self.system = system + self.service = "log" + + def get_url(self): + if self.system is None: + return self.connection.get_url(self.service) + return f"{self.connection.get_url(self.service)}/{self.system}" + + def send(self, system: str, data: Dict, id: int = None) -> None: + """ + @brief Send a message to the server. + @param[in] system system where to publish the data. + @param[in] data: Data to send to the server + @param[in] id: Local internal ID + """ + param = {} + if id is not None: + param["id"] = id + header = self.connection.get_header() + try: + ret = requests.post(self.get_url(), json=data, headers=header, params=param) + except requests.exceptions.ConnectionError as ex: + raise KaranageException(f"Fail connect server: {self.get_url('state', system)}", 0, str(ex)) + if 200 <= ret.status_code <= 299: + pass + else: + raise KaranageException(f"Fail send message: {self.get_url('state', system)}", ret.status_code, ret.content.decode("utf-8")) + \ No newline at end of file diff --git a/client/python/karanage/karanage/state.py b/client/python/karanage/karanage/state.py new file mode 100644 index 0000000..aa5b767 --- /dev/null +++ b/client/python/karanage/karanage/state.py @@ -0,0 +1,96 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2023, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## +import enum +import requests +import json +from typing import Dict, Optional +from .connection import KaranageConnection +from .exception import KaranageException + +class StateSystem(enum.Enum): + OK = "OK" + FAIL = "FAIL" + DOWN = "DOWN" + +## Generic karanage sending system. +class KaranageState: + def __init__(self, connection: KaranageConnection) -> None: + """ + @brief Initialize the communication class. + @param[in] connection Connection interface. + """ + self.connection = connection + + def get_url(self, service: str, topic: Optional[str] = None): + if topic is None: + return self.connection.get_url(service) + return f"{self.connection.get_url(service)}/{topic}" + + def send(self, topic: str, data: Optional[Dict], state: StateSystem = StateSystem.OK) -> None: + """ + @brief Send a message to the server. + @param[in] topic Topic where to publish the data. + @param[in] data: Data to send to the server + @param[in] state: State of the current system + """ + if data is None: + data = {} + param = {} + if state is not None: + param["state"] = state + header = self.connection.get_header() + try: + ret = requests.post(self.get_url("state", topic), json=data, headers=header, params=param) + except requests.exceptions.ConnectionError as ex: + raise KaranageException(f"Fail connect server: {self.get_url('state', topic)}", 0, str(ex)) + if 200 <= ret.status_code <= 299: + pass + else: + raise KaranageException(f"Fail send message: {self.get_url('state', topic)}", ret.status_code, ret.content.decode("utf-8")) + + + def gets(self, topic: Optional[str] = None, since: Optional[str] = None) -> Dict: + """ + @brief Get all the topic fom the server. + @param since ISO1866 time value. + @return A dictionnary with the requested data. + """ + param = { } + header = self.connection.get_header() + if since is not None: + param["since"] = since + ret = requests.get(self.get_url("state", topic), headers=header, params=param) + #print(ret.content.decode('utf-8')) + if 200 == ret.status_code: + return json.loads(ret.content.decode('utf-8')) + raise KaranageException(f"Fail get data: {self.get_url('state', topic)}", ret.status_code, ret.content.decode("utf-8")) + + def get_history(self, topic: Optional[str] = None, since: Optional[str] = None, since_id: Optional[int] = None, limit: Optional[int] = None) -> Dict: + """ + @brief Get all the topic fom the server. + @param since ISO1866 time value. + @param since_id remote BDD index of tje fielf. + @param limit Number of value we want to get + @return A dictionnary with the requested data. + """ + param = { } + header = self.connection.get_header() + if since is not None: + param["since"] = since + if since_id is not None: + param["sinceId"] = since_id + if limit is not None: + param["limit"] = limit + ret = requests.get(self.get_url("state_history", topic), headers=header, params=param) + #print(ret.content.decode('utf-8')) + if 200 == ret.status_code: + return json.loads(ret.content.decode('utf-8')) + raise KaranageException(f"Fail get data: {self.get_url('state_history', topic)}", ret.status_code, ret.content.decode("utf-8")) + diff --git a/client/python/karanage/setup.py b/client/python/karanage/setup.py index 9b31ae6..be27b29 100755 --- a/client/python/karanage/setup.py +++ b/client/python/karanage/setup.py @@ -56,4 +56,5 @@ setup(name='karanage', # https://packaging.python.org/en/latest/tutorials/packaging-projects/ # python3 -m build -# python3 -m twine upload dist/* \ No newline at end of file +# python3 -m twine upload dist/* + diff --git a/client/python/karanage/version.txt b/client/python/karanage/version.txt index b4f09dd..0ba2319 100644 --- a/client/python/karanage/version.txt +++ b/client/python/karanage/version.txt @@ -1 +1 @@ -0.2.0-dev \ No newline at end of file +0.3.0-dev \ No newline at end of file diff --git a/readme.md b/readme.md index 405599c..921f2f0 100644 --- a/readme.md +++ b/readme.md @@ -48,11 +48,10 @@ docker-compose up -d build the local image: +docker login gitea.atria-soft.org + docker pull archlinux:base-devel docker pull bellsoft/liberica-openjdk-alpine:latest docker build -t gitea.atria-soft.org/kangaroo-and-rabbit/karanage:latest . - -docker login gitea.atria-soft.org - docker push gitea.atria-soft.org/kangaroo-and-rabbit/karanage:latest