[DEV] upgrade client and back to add basic api for logs

This commit is contained in:
Edouard DUPIN 2023-01-13 00:36:47 +01:00
parent 31af52dc9e
commit 95902e7312
21 changed files with 554 additions and 274 deletions

View File

@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.kar</groupId> <groupId>org.kar</groupId>
<artifactId>karanage</artifactId> <artifactId>karanage</artifactId>
<version>0.2.1</version> <version>0.2.2</version>
<properties> <properties>
<maven.compiler.version>3.1</maven.compiler.version> <maven.compiler.version>3.1</maven.compiler.version>
@ -22,7 +22,7 @@
<dependency> <dependency>
<groupId>kangaroo-and-rabbit</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.2.1</version> <version>0.2.4</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -11,6 +11,7 @@ import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import org.kar.karanage.api.Front; import org.kar.karanage.api.Front;
import org.kar.karanage.api.HealthCheck; import org.kar.karanage.api.HealthCheck;
import org.kar.karanage.api.LogResource;
import org.kar.karanage.api.StateHistoryResource; import org.kar.karanage.api.StateHistoryResource;
import org.kar.karanage.api.StateResource; import org.kar.karanage.api.StateResource;
import org.kar.karanage.api.UserResource; 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.CORSFilter;
import org.kar.archidata.filter.OptionFilter; import org.kar.archidata.filter.OptionFilter;
import org.kar.archidata.util.ConfigBaseVariable; import org.kar.archidata.util.ConfigBaseVariable;
import org.kar.karanage.model.DataHistory; import org.kar.karanage.model.StateHistory;
import org.kar.karanage.model.DataInstant; import org.kar.karanage.model.StateInstant;
import org.kar.karanage.model.DataLog;
import org.kar.karanage.model.Group; import org.kar.karanage.model.Group;
public class WebLauncher { public class WebLauncher {
@ -43,9 +45,10 @@ public class WebLauncher {
// generate the BDD: // generate the BDD:
try { try {
String out = ""; String out = "";
out += SqlWrapper.createTable(DataInstant.class); out += SqlWrapper.createTable(StateInstant.class);
out += SqlWrapper.createTable(DataHistory.class); out += SqlWrapper.createTable(StateHistory.class);
out += SqlWrapper.createTable(Group.class); out += SqlWrapper.createTable(Group.class);
out += SqlWrapper.createTable(DataLog.class);
System.out.println(out); System.out.println(out);
} catch (Exception e) { } catch (Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -75,6 +78,7 @@ public class WebLauncher {
rc.registerClasses(UserResource.class); rc.registerClasses(UserResource.class);
rc.registerClasses(StateResource.class); rc.registerClasses(StateResource.class);
rc.registerClasses(StateHistoryResource.class); rc.registerClasses(StateHistoryResource.class);
rc.registerClasses(LogResource.class);
rc.registerClasses(HealthCheck.class); rc.registerClasses(HealthCheck.class);
rc.registerClasses(Front.class); rc.registerClasses(Front.class);

View File

@ -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<DataLog> 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();
}
}

View File

@ -2,8 +2,8 @@ package org.kar.karanage.api;
import org.kar.archidata.SqlWrapper; import org.kar.archidata.SqlWrapper;
import org.kar.archidata.WhereCondition; import org.kar.archidata.WhereCondition;
import org.kar.karanage.model.DataInstant; import org.kar.karanage.model.StateInstant;
import org.kar.karanage.model.DataHistory; import org.kar.karanage.model.StateHistory;
import org.kar.karanage.model.Group; import org.kar.karanage.model.Group;
import javax.ws.rs.core.Response; 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..."); throw new InputException("since,sinceId", "The 2 parameters can not be set at the same time...");
} }
List<DataHistory> datas = null; List<StateHistory> datas = null;
if (since != null) { if (since != null) {
Timestamp time = null; Timestamp time = null;
try { try {
@ -72,7 +72,7 @@ public class StateHistoryResource {
} catch (Exception ex) { } catch (Exception ex) {
throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); 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( List.of(
new WhereCondition("group", "=", group.id), new WhereCondition("group", "=", group.id),
new WhereCondition("topic", "=", topic), 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 + "'"); throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'");
} }
} else if (sinceId != null) { } else if (sinceId != null) {
datas = SqlWrapper.getsWhere(DataHistory.class, datas = SqlWrapper.getsWhere(StateHistory.class,
List.of( List.of(
new WhereCondition("group", "=", group.id), new WhereCondition("group", "=", group.id),
new WhereCondition("topic", "=", topic), 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 + "'"); throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'");
} }
} else { } else {
datas = SqlWrapper.getsWhere(DataHistory.class, datas = SqlWrapper.getsWhere(StateHistory.class,
List.of( List.of(
new WhereCondition("group", "=", group.id), new WhereCondition("group", "=", group.id),
new WhereCondition("topic", "=", topic) new WhereCondition("topic", "=", topic)
@ -113,7 +113,7 @@ public class StateHistoryResource {
StringBuilder out = new StringBuilder("["); StringBuilder out = new StringBuilder("[");
boolean first = true; boolean first = true;
for (DataInstant data : datas) { for (StateInstant data : datas) {
if (first) { if (first) {
first = false; first = false;
} else { } else {

View File

@ -2,8 +2,8 @@ package org.kar.karanage.api;
import org.kar.archidata.SqlWrapper; import org.kar.archidata.SqlWrapper;
import org.kar.archidata.WhereCondition; import org.kar.archidata.WhereCondition;
import org.kar.karanage.model.DataInstant; import org.kar.karanage.model.StateInstant;
import org.kar.karanage.model.DataHistory; import org.kar.karanage.model.StateHistory;
import org.kar.karanage.model.Group; import org.kar.karanage.model.Group;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -45,7 +45,7 @@ public class StateResource {
if (group == null) { if (group == null) {
throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); throw new InputException("group", "url: /state/{group}/... ==> Unknown group name");
} }
DataInstant data = null; StateInstant data = null;
if (since != null) { if (since != null) {
Timestamp time = null; Timestamp time = null;
try { try {
@ -53,7 +53,7 @@ public class StateResource {
} catch (Exception ex) { } catch (Exception ex) {
throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); 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( List.of(
new WhereCondition("group", "=", group.id), new WhereCondition("group", "=", group.id),
new WhereCondition("topic", "=", topic), 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 + "'"); throw new FailException(Response.Status.NOT_FOUND, "Topic has no new data or does not exist: '" + topic + "'");
} }
} else { } else {
data = SqlWrapper.getWhere(DataInstant.class, data = SqlWrapper.getWhere(StateInstant.class,
List.of( List.of(
new WhereCondition("group", "=", group.id), new WhereCondition("group", "=", group.id),
new WhereCondition("topic", "=", topic) new WhereCondition("topic", "=", topic)
@ -96,7 +96,7 @@ public class StateResource {
throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); throw new InputException("group", "url: /state/{group}/... ==> Unknown group name");
} }
List<DataInstant> datas = null; List<StateInstant> datas = null;
if (since != null) { if (since != null) {
Timestamp time = null; Timestamp time = null;
try { try {
@ -104,14 +104,14 @@ public class StateResource {
} catch (Exception ex) { } catch (Exception ex) {
throw new InputException("since", "url: ?since=... ==> is not an iso 8601 time format"); 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( List.of(
new WhereCondition("group", "=", group.id), new WhereCondition("group", "=", group.id),
new WhereCondition("modify_date", ">", time) new WhereCondition("modify_date", ">", time)
), ),
true); true);
} else { } else {
datas = SqlWrapper.getsWhere(DataInstant.class, datas = SqlWrapper.getsWhere(StateInstant.class,
List.of( List.of(
new WhereCondition("group", "=", group.id) new WhereCondition("group", "=", group.id)
), ),
@ -119,7 +119,7 @@ public class StateResource {
} }
StringBuilder out = new StringBuilder("["); StringBuilder out = new StringBuilder("[");
boolean first = true; boolean first = true;
for (DataInstant data : datas) { for (StateInstant data : datas) {
if (first) { if (first) {
first = false; first = false;
} else { } else {
@ -148,8 +148,9 @@ public class StateResource {
public Response post( public Response post(
@PathParam("group") String groupName, @PathParam("group") String groupName,
@PathParam("topic") String topic, @PathParam("topic") String topic,
@QueryParam("state") String state,
String dataToInsert) throws Exception { String dataToInsert) throws Exception {
DataInstant previous = null; StateInstant previous = null;
Group group = SqlWrapper.getWhere(Group.class, Group group = SqlWrapper.getWhere(Group.class,
List.of( List.of(
@ -160,7 +161,7 @@ public class StateResource {
throw new InputException("group", "url: /state/{group}/... ==> Unknown group name"); throw new InputException("group", "url: /state/{group}/... ==> Unknown group name");
} }
if (group != null) { if (group != null) {
previous = SqlWrapper.getWhere(DataInstant.class, previous = SqlWrapper.getWhere(StateInstant.class,
List.of( List.of(
new WhereCondition("group", "=", group.id), new WhereCondition("group", "=", group.id),
new WhereCondition("topic", "=", topic) new WhereCondition("topic", "=", topic)
@ -169,12 +170,13 @@ public class StateResource {
} }
if (previous == null) { if (previous == null) {
// insert new data // insert new data
DataInstant data = new DataInstant(); StateInstant data = new StateInstant();
data.group = group.id; data.group = group.id;
data.topic = topic; data.topic = topic;
data.data = dataToInsert; data.data = dataToInsert;
data.state = state;
previous = SqlWrapper.insert(data); previous = SqlWrapper.insert(data);
DataHistory dataHistory = new DataHistory(previous); StateHistory dataHistory = new StateHistory(previous);
dataHistory.year = true; dataHistory.year = true;
dataHistory.month = true; dataHistory.month = true;
dataHistory.week = true; dataHistory.week = true;
@ -185,9 +187,10 @@ public class StateResource {
// update Data // update Data
try { try {
previous.data = dataToInsert; 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: // add it in history:
DataHistory dataHistory = new DataHistory(previous); StateHistory dataHistory = new StateHistory(previous);
OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ); OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC );
OffsetDateTime sqlTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(previous.modify_date.getTime()), ZoneOffset.UTC); OffsetDateTime sqlTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(previous.modify_date.getTime()), ZoneOffset.UTC);

View File

@ -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;
}

View File

@ -36,15 +36,15 @@ enum TypeData {
NUMBER, BOOLEAN, JSON, STRING NUMBER, BOOLEAN, JSON, STRING
} }
@SQLTableName ("dataHistory") @SQLTableName ("stateHistory")
@SQLIfNotExists @SQLIfNotExists
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class DataHistory extends DataInstant { public class StateHistory extends StateInstant {
// default constructor // default constructor
public DataHistory() { public StateHistory() {
} }
public DataHistory(DataInstant other) { public StateHistory(StateInstant other) {
this.group = other.group; this.group = other.group;
this.topic = other.topic; this.topic = other.topic;
this.state = other.state; this.state = other.state;

View File

@ -27,10 +27,10 @@ import org.kar.archidata.annotation.SQLUpdateTime;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@SQLTableName ("data") @SQLTableName ("state")
@SQLIfNotExists @SQLIfNotExists
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class DataInstant { public class StateInstant {
@SQLAutoIncrement // Add AUTO_INCREMENT modifier @SQLAutoIncrement // Add AUTO_INCREMENT modifier
@SQLPrimaryKey // Create a PRIMARY KEY based on this field @SQLPrimaryKey // Create a PRIMARY KEY based on this field
@SQLNotNull @SQLNotNull

View File

@ -7,7 +7,7 @@ import subprocess
import json import json
from pathlib import Path from pathlib import Path
from typing import Dict, List from typing import Dict, List
import karanage from karanage import KaranageState, KaranageConnection, KaranageException, StateSystem
cpu_core_count = psutil.cpu_count(logical=False) cpu_core_count = psutil.cpu_count(logical=False)
cpu_thread_count = psutil.cpu_count() cpu_thread_count = psutil.cpu_count()
@ -122,12 +122,12 @@ if __name__ == '__main__':
# Load arguments: # Load arguments:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("-c", "--config", type=str, default="/etc/karanage/system.json", help="json configuration file") 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: # 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("-u", "--url", type=str, default=None, help="Base URL of the web service")
parser.add_argument("-g", "--group", type=str, default="home", help="Group the the message") parser.add_argument("-g", "--group", type=str, default=None, help="Group the the message")
parser.add_argument("-T", "--token", type=str, default="", help="Token to access to the server") parser.add_argument("-T", "--token", type=str, default=None, help="Token to access to the server")
# This element are read from the configuration file: # This element are read from the configuration file:
parser.add_argument("-t", "--topic", type=str, default="PC/system", help="Topic of the message") 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"]: if "topic" not in configuration["config"]:
configuration["config"]["topic"] = args.topic configuration["config"]["topic"] = args.topic
connection = KaranageConnection(
if Path(args.connection).exists(): url = args.url,
f = open(args.connection, "r") group = args.group,
connection = json.loads(f.read()) token = args.token,
f.close() config_file = args.connection
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 # create the rest interface of karanage
restInterface = karanage.KaranageREST( stateInterface = KaranageState(connection)
connection["url"],
connection["group"],
connection["token"])
while True: while True:
out = {} out = {}
@ -205,7 +194,7 @@ if __name__ == '__main__':
print(json.dumps(out, indent=4)) print(json.dumps(out, indent=4))
# send message to the server: # send message to the server:
try: try:
restInterface.send_state_to_server(configuration["config"]["topic"], out) stateInterface.send(configuration["config"]["topic"], out, state = StateSystem.OK)
except karanage.KarangeException as ex: except KaranageException as ex:
print(f"Can not send to the server: {ex}") print(f"Can not send to the server: {ex}")
time.sleep(configuration["config"]["sleep"]) time.sleep(configuration["config"]["sleep"])

View File

@ -7,45 +7,33 @@ import subprocess
import json import json
from pathlib import Path from pathlib import Path
from typing import Dict, List from typing import Dict, List
import karanage from karanage import KaranageException, KaranageConnection, KaranageState
if __name__ == '__main__': if __name__ == '__main__':
# Load arguments: # Load arguments:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
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")
parser.add_argument("-t", "--topic", type=str, default="", help="Topic of the message") 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", type=str, default=None, help="Iso date since the value time must be")
# This element are read from the connection 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("-u", "--url", type=str, default=None, help="Base URL of the web service")
parser.add_argument("-g", "--group", type=str, default="home", help="Group the the message") parser.add_argument("-g", "--group", type=str, default=None, help="Group the the message")
parser.add_argument("-T", "--token", type=str, default="", help="Token to access to the server") parser.add_argument("-T", "--token", type=str, default=None, help="Token to access to the server")
args = parser.parse_args() args = parser.parse_args()
if Path(args.connection).exists(): connection = KaranageConnection(
f = open(args.connection, "r") url = args.url,
connection = json.loads(f.read()) group = args.group,
f.close() token = args.token,
else: config_file = args.connection
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 # create the rest interface of karanage
restInterface = karanage.KaranageREST( stateInterface = KaranageState(connection)
connection["url"],
connection["group"], data = stateInterface.gets(topic=args.topic, since=args.since)
connection["token"]) print(f"Ret = {json.dumps(data, indent=4)}")
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)}")

View File

@ -7,46 +7,36 @@ import subprocess
import json import json
from pathlib import Path from pathlib import Path
from typing import Dict, List from typing import Dict, List
import karanage from karanage import KaranageConnection, KaranageState, KaranageState
if __name__ == '__main__': if __name__ == '__main__':
# Load arguments: # Load arguments:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
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")
parser.add_argument("-t", "--topic", type=str, default="", help="Topic of the message") 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", 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("-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") parser.add_argument("-l", "--limit", type=int, default=100, help="Limit the number of request")
# This element are read from the connection 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("-u", "--url", type=str, default=None, help="Base URL of the web service")
parser.add_argument("-g", "--group", type=str, default="home", help="Group the the message") parser.add_argument("-g", "--group", type=str, default=None, help="Group the the message")
parser.add_argument("-T", "--token", type=str, default="", help="Token to access to the server") parser.add_argument("-T", "--token", type=str, default=None, help="Token to access to the server")
args = parser.parse_args() args = parser.parse_args()
if Path(args.connection).exists(): connection = KaranageConnection(
f = open(args.connection, "r") url = args.url,
connection = json.loads(f.read()) group = args.group,
f.close() token = args.token,
else: config_file = args.connection
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 # create the rest interface of karanage
restInterface = karanage.KaranageREST( stateInterface = KaranageState(connection)
connection["url"],
connection["group"],
connection["token"])
if args.topic == "": if args.topic is None:
print("Missing TOPIC ...") print("Missing TOPIC ...")
else: 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)}") print(f"Ret = {json.dumps(data, indent=4)}")

View File

@ -1 +1 @@
0.2.0-dev 0.3.0-dev

View File

@ -7,4 +7,7 @@
## ##
## @license MPL v2.0 (see license file) ## @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

View File

@ -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

View File

@ -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}'"

View File

@ -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)

View File

@ -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"))

View File

@ -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"))

View File

@ -57,3 +57,4 @@ setup(name='karanage',
# https://packaging.python.org/en/latest/tutorials/packaging-projects/ # https://packaging.python.org/en/latest/tutorials/packaging-projects/
# python3 -m build # python3 -m build
# python3 -m twine upload dist/* # python3 -m twine upload dist/*

View File

@ -1 +1 @@
0.2.0-dev 0.3.0-dev

View File

@ -48,11 +48,10 @@ docker-compose up -d
build the local image: build the local image:
docker login gitea.atria-soft.org
docker pull archlinux:base-devel docker pull archlinux:base-devel
docker pull bellsoft/liberica-openjdk-alpine:latest docker pull bellsoft/liberica-openjdk-alpine:latest
docker build -t gitea.atria-soft.org/kangaroo-and-rabbit/karanage: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 docker push gitea.atria-soft.org/kangaroo-and-rabbit/karanage:latest