diff --git a/back/src/org/kar/karanage/api/LogResource.java b/back/src/org/kar/karanage/api/LogResource.java index 50e6f13..7980a1f 100644 --- a/back/src/org/kar/karanage/api/LogResource.java +++ b/back/src/org/kar/karanage/api/LogResource.java @@ -2,10 +2,9 @@ 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 org.kar.karanage.model.MultipleLogElement; import javax.ws.rs.core.Response; @@ -18,8 +17,6 @@ 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; @@ -135,7 +132,8 @@ public class LogResource { @PathParam("group") String groupName, @PathParam("system") String system, @QueryParam("time") String time, - @QueryParam("id") Integer client_id, + @QueryParam("id") Long clientId, + @QueryParam("uuid") Long clientUuid, String dataToInsert) throws Exception { Group group = SqlWrapper.getWhere(Group.class, List.of( @@ -149,8 +147,11 @@ public class LogResource { data.group = group.id; data.system = system; data.data = dataToInsert; - if (client_id != null) { - data.client_id = client_id; + if (clientId != null) { + data.clientId = clientId; + } + if (clientUuid != null) { + data.clientUuid = clientUuid; } if (time != null) { try { @@ -162,6 +163,46 @@ public class LogResource { SqlWrapper.insert(data); return Response.status(201).build(); } + @POST + @Path("{group}/{system}/push_multiple") + @PermitAll + @Consumes(MediaType.APPLICATION_JSON) + public Response post( + @PathParam("group") String groupName, + @PathParam("system") String system, + @QueryParam("uuid") Long clientUuid, + List listDataToInsert) 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"); + } + for (MultipleLogElement dataToInsert : listDataToInsert) { + DataLog data = new DataLog(); + data.group = group.id; + data.system = system; + data.data = dataToInsert.data; + if (dataToInsert.id != null) { + data.clientId = dataToInsert.id; + } + if (clientUuid != null) { + data.clientUuid = clientUuid; + } + if (dataToInsert.time != null) { + try { + data.create_date = Timestamp.from(Instant.parse(dataToInsert.time)); + } catch (Exception ex) { + throw new InputException("time", "url: ?time=... ==> is not an iso 8601 time format"); + } + } + // TODO: Do a single insertion for multiple DATAS... + 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 753cfeb..653c93c 100644 --- a/back/src/org/kar/karanage/api/StateHistoryResource.java +++ b/back/src/org/kar/karanage/api/StateHistoryResource.java @@ -17,8 +17,6 @@ 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; diff --git a/back/src/org/kar/karanage/model/ApplicationToken.java b/back/src/org/kar/karanage/model/ApplicationToken.java index 1cbf82d..0eb0b56 100644 --- a/back/src/org/kar/karanage/model/ApplicationToken.java +++ b/back/src/org/kar/karanage/model/ApplicationToken.java @@ -1,16 +1,4 @@ package org.kar.karanage.model; -/* -CREATE TABLE `node` ( - `id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY, - `deleted` BOOLEAN NOT NULL DEFAULT false, - `create_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been created', - `modify_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been update', - `type` enum("TYPE", "UNIVERS", "SERIE", "SAISON", "MEDIA") NOT NULL DEFAULT 'TYPE', - `name` TEXT COLLATE 'utf8_general_ci' NOT NULL, - `description` TEXT COLLATE 'utf8_general_ci', - `parent_id` bigint -) AUTO_INCREMENT=10; -*/ import org.kar.archidata.annotation.SQLComment; import org.kar.archidata.annotation.SQLForeignKey; @@ -19,11 +7,11 @@ import org.kar.archidata.annotation.SQLLimitSize; import org.kar.archidata.annotation.SQLTableName; import org.kar.archidata.model.GenericTable; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonInclude; @SQLTableName ("applicationToken") @SQLIfNotExists -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class ApplicationToken extends GenericTable { @SQLComment("Group of the element") @SQLForeignKey("group") @@ -34,4 +22,4 @@ public class ApplicationToken extends GenericTable { @SQLComment("Description of the Group") @SQLLimitSize(1024) public String description; -} \ No newline at end of file +} diff --git a/back/src/org/kar/karanage/model/DataLog.java b/back/src/org/kar/karanage/model/DataLog.java index b5e2241..3f3e124 100644 --- a/back/src/org/kar/karanage/model/DataLog.java +++ b/back/src/org/kar/karanage/model/DataLog.java @@ -9,16 +9,16 @@ 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; +import com.fasterxml.jackson.annotation.JsonInclude; + @SQLTableName ("log") @SQLIfNotExists -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class DataLog { @SQLAutoIncrement // Add AUTO_INCREMENT modifier @SQLPrimaryKey // Create a PRIMARY KEY based on this field @@ -33,11 +33,15 @@ public class DataLog { @SQLComment("Group of the element") @SQLForeignKey("group") public long group; + // TODO: use external system ID table ==> simplify the table complexity @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("Client internal UUID") + public Long clientUuid; + @SQLComment("Client unique internal ID (for synchronization system)") + public Long clientId = null; @SQLComment("Message to store") + @SQLNotNull public String data = null; } \ No newline at end of file diff --git a/back/src/org/kar/karanage/model/Group.java b/back/src/org/kar/karanage/model/Group.java index 43a8eb0..712a196 100644 --- a/back/src/org/kar/karanage/model/Group.java +++ b/back/src/org/kar/karanage/model/Group.java @@ -7,12 +7,11 @@ import org.kar.archidata.annotation.SQLNotNull; import org.kar.archidata.annotation.SQLTableName; import org.kar.archidata.model.GenericTable; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - +import com.fasterxml.jackson.annotation.JsonInclude; @SQLTableName ("group") @SQLIfNotExists -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class Group extends GenericTable { @SQLNotNull @SQLLimitSize(256) diff --git a/back/src/org/kar/karanage/model/MultipleLogElement.java b/back/src/org/kar/karanage/model/MultipleLogElement.java new file mode 100644 index 0000000..d01bee7 --- /dev/null +++ b/back/src/org/kar/karanage/model/MultipleLogElement.java @@ -0,0 +1,7 @@ +package org.kar.karanage.model; + +public class MultipleLogElement { + public Long id; + public String time; + public String data; +} \ No newline at end of file diff --git a/back/src/org/kar/karanage/model/StateHistory.java b/back/src/org/kar/karanage/model/StateHistory.java index f3f5d0f..151e6ee 100644 --- a/back/src/org/kar/karanage/model/StateHistory.java +++ b/back/src/org/kar/karanage/model/StateHistory.java @@ -1,36 +1,17 @@ package org.kar.karanage.model; -/* -CREATE TABLE `node` ( - `id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY, - `deleted` BOOLEAN NOT NULL DEFAULT false, - `create_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been created', - `modify_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been update', - `type` enum("TYPE", "UNIVERS", "SERIE", "SAISON", "MEDIA") NOT NULL DEFAULT 'TYPE', - `name` TEXT COLLATE 'utf8_general_ci' NOT NULL, - `description` TEXT COLLATE 'utf8_general_ci', - `parent_id` bigint -) AUTO_INCREMENT=10; -*/ -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; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; enum State { - OK, FAIL, UNSTABLE, RETREIVING, HAPPY, FULL, EMPTY + OK, FAIL, UNSTABLE, RETRIEVING, HAPPY, FULL, EMPTY } enum TypeData { NUMBER, BOOLEAN, JSON, STRING @@ -38,7 +19,7 @@ enum TypeData { @SQLTableName ("stateHistory") @SQLIfNotExists -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(Include.NON_NULL) public class StateHistory extends StateInstant { // default constructor public StateHistory() { @@ -58,7 +39,7 @@ public class StateHistory extends StateInstant { @SQLComment("First message of the month (in GMT time)") @SQLNotNull @SQLDefault("0") - public boolean month = false; + public boolean month = false; @SQLComment("First message of the week: monday (in GMT time)") @SQLNotNull @SQLDefault("0") diff --git a/back/src/org/kar/karanage/model/StateInstant.java b/back/src/org/kar/karanage/model/StateInstant.java index cd2dc35..4891816 100644 --- a/back/src/org/kar/karanage/model/StateInstant.java +++ b/back/src/org/kar/karanage/model/StateInstant.java @@ -1,16 +1,4 @@ package org.kar.karanage.model; -/* -CREATE TABLE `node` ( - `id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY, - `deleted` BOOLEAN NOT NULL DEFAULT false, - `create_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been created', - `modify_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been update', - `type` enum("TYPE", "UNIVERS", "SERIE", "SAISON", "MEDIA") NOT NULL DEFAULT 'TYPE', - `name` TEXT COLLATE 'utf8_general_ci' NOT NULL, - `description` TEXT COLLATE 'utf8_general_ci', - `parent_id` bigint -) AUTO_INCREMENT=10; -*/ import java.sql.Timestamp; @@ -20,16 +8,15 @@ 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; +import com.fasterxml.jackson.annotation.JsonInclude; @SQLTableName ("state") @SQLIfNotExists -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class StateInstant { @SQLAutoIncrement // Add AUTO_INCREMENT modifier @SQLPrimaryKey // Create a PRIMARY KEY based on this field diff --git a/back/src/org/kar/karanage/model/UserKaranage.java b/back/src/org/kar/karanage/model/UserKaranage.java index 9db6992..e90abe5 100644 --- a/back/src/org/kar/karanage/model/UserKaranage.java +++ b/back/src/org/kar/karanage/model/UserKaranage.java @@ -4,11 +4,11 @@ import org.kar.archidata.annotation.SQLIfNotExists; import org.kar.archidata.annotation.SQLTableName; import org.kar.archidata.model.User; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonInclude; @SQLTableName ("user") @SQLIfNotExists -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_NULL) public class UserKaranage extends User { } diff --git a/client/python/karanage/README.md b/client/python/karanage/README.md index 27359f6..6108425 100644 --- a/client/python/karanage/README.md +++ b/client/python/karanage/README.md @@ -22,7 +22,7 @@ Just run: pip3 install karanage ``` -developpement for KARANAGE: +development for KARANAGE: ``` git clone https://gitea.atria-soft.org/kangaroo-and-rabbit/karanage cd karanage/client/python/karanage diff --git a/client/python/karanage/karanage/connection.py b/client/python/karanage/karanage/connection.py index 49131fb..d1d500d 100644 --- a/client/python/karanage/karanage/connection.py +++ b/client/python/karanage/karanage/connection.py @@ -21,13 +21,12 @@ class KaranageConnection: 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. + """ Initialize the communication class. + :param url: URL of the karanage API server. + :param group: Group of the message (token need to have the autorotation to published on it). + :param token: Token to validate the access on the application. + :param config_file: path to the configuration file if overload. + :param default_values: Default configurations. """ self.url = "http://localhost:20080/karanage/api" self.group = "test" @@ -46,17 +45,17 @@ class KaranageConnection: # check if the config exist: if Path(config_file).exists(): f = open(config_file, "r") - configuaration = json.loads(f.read()) + configuration = json.loads(f.read()) f.close() else: - configuaration = {} + configuration = {} # 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"] + if "url" in configuration: + self.url = configuration["url"] + if "group" in configuration: + self.group = configuration["group"] + if "token" in configuration: + self.token = configuration["token"] # set user command - line configuration: if url is not None: self.url = url diff --git a/client/python/karanage/karanage/log.py b/client/python/karanage/karanage/log.py index 0bdac83..f14730b 100644 --- a/client/python/karanage/karanage/log.py +++ b/client/python/karanage/karanage/log.py @@ -14,39 +14,62 @@ from typing import Dict, Optional from .connection import KaranageConnection from .exception import KaranageException +class GroupLogElement: + def __init__(self, id: int, time: Datetime:None, data: str): + self.id = id + self.time = time + self.data = data + ## 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. + """Initialize the communication class. + :param 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 + + def send(self, data: Dict, id: int = None, uuid_group: int= None, time: Datetime = None) -> None: + """Send a message to the server. + :param data: Data to send to the server + :param id: Local internal ID + :param uuid_group: local internal group UUID + :param time_log: Receive time of the log """ param = {} if id is not None: param["id"] = id + if uuid_group is not None: + param["uuid"] = uuid_group + if time is not None: + param["time"] = 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 + raise KaranageException(f"Fail connect server: {self.get_url()}", 0, str(ex)) + if not 200 <= ret.status_code <= 299: + raise KaranageException(f"Fail send message: {self.get_url()}", ret.status_code, ret.content.decode("utf-8")) + + def send_multiple(self, data: List[GroupLogElement], uuid_group: int= None) -> None: + """Send multiple log message to the server. + :param data: Data to send to the server + :param uuid_group: local internal group UUID + """ + param = {} + if uuid_group is not None: + param["uuid"] = uuid_group + header = self.connection.get_header() + try: + ret = requests.post(f"{self.get_url()}/push_multiple", json=data, headers=header, params=param) + except requests.exceptions.ConnectionError as ex: + raise KaranageException(f"Fail connect server: {self.get_url()}", 0, str(ex)) + if not 200 <= ret.status_code <= 299: + raise KaranageException(f"Fail send message: {self.get_url()}", ret.status_code, ret.content.decode("utf-8")) + diff --git a/client/python/karanage/karanage/state.py b/client/python/karanage/karanage/state.py index aa5b767..91e37aa 100644 --- a/client/python/karanage/karanage/state.py +++ b/client/python/karanage/karanage/state.py @@ -22,9 +22,8 @@ class StateSystem(enum.Enum): ## Generic karanage sending system. class KaranageState: def __init__(self, connection: KaranageConnection) -> None: - """ - @brief Initialize the communication class. - @param[in] connection Connection interface. + """Initialize the communication class. + :param connection: Connection interface. """ self.connection = connection @@ -34,33 +33,29 @@ class KaranageState: 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 + """Send a message to the server. + :param topic: Topic where to publish the data. + :param data: Data to send to the server + :param state: State of the current system """ if data is None: data = {} param = {} if state is not None: - param["state"] = state + param["state"] = state.value 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: + if not 200 <= ret.status_code <= 299: 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. + """Get all the topic fom the server. + :param since: ISO1866 time value. + :return: A dictionary with the requested data. """ param = { } header = self.connection.get_header() @@ -68,17 +63,16 @@ class KaranageState: 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: + if 200 <= ret.status_code <= 299: 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. + """Get all the topic fom the server. + :param since: ISO1866 time value. + :param since_id: remote BDD index of the field. + :param limit: the number of value we want to get + :return: A dictionary with the requested data. """ param = { } header = self.connection.get_header() @@ -90,7 +84,7 @@ class KaranageState: 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: + if 200 <= ret.status_code <= 299: 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"))