From 0efa7320e329d00522aabd8082054da37e00c04a Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Thu, 5 Jan 2023 22:24:47 +0100 Subject: [PATCH] Fixed up the API and added API doc --- hal-core/resource/resource/web/api/docs.html | 25 +++ .../resource/resource/web/api/openapi.json | 189 ++++++++++++++++++ .../resource/resource/web/sensor_detail.tmpl | 6 +- hal-core/resource/resource/web/startup.tmpl | 4 +- hal-core/src/se/hal/HalServer.java | 2 +- .../src/se/hal/intf/HalAbstractDevice.java | 6 +- .../{HalJsonPage.java => HalApiEndpoint.java} | 4 +- ...entJsonPage.java => EventApiEndpoint.java} | 42 ++-- .../{MapJsonPage.java => MapApiEndpoint.java} | 6 +- ...orJsonPage.java => SensorApiEndpoint.java} | 73 ++++--- hal-core/src/se/hal/plugin.json | 6 +- .../resource/resource/web/pc_overview.tmpl | 8 +- 12 files changed, 313 insertions(+), 58 deletions(-) create mode 100644 hal-core/resource/resource/web/api/docs.html create mode 100644 hal-core/resource/resource/web/api/openapi.json rename hal-core/src/se/hal/intf/{HalJsonPage.java => HalApiEndpoint.java} (94%) rename hal-core/src/se/hal/page/api/{EventJsonPage.java => EventApiEndpoint.java} (53%) rename hal-core/src/se/hal/page/api/{MapJsonPage.java => MapApiEndpoint.java} (95%) rename hal-core/src/se/hal/page/api/{SensorJsonPage.java => SensorApiEndpoint.java} (64%) diff --git a/hal-core/resource/resource/web/api/docs.html b/hal-core/resource/resource/web/api/docs.html new file mode 100644 index 00000000..a187ec73 --- /dev/null +++ b/hal-core/resource/resource/web/api/docs.html @@ -0,0 +1,25 @@ + + + +Hal OpenAPI Documentation + + + + + + + +
+ + \ No newline at end of file diff --git a/hal-core/resource/resource/web/api/openapi.json b/hal-core/resource/resource/web/api/openapi.json new file mode 100644 index 00000000..1fd17ddc --- /dev/null +++ b/hal-core/resource/resource/web/api/openapi.json @@ -0,0 +1,189 @@ +{ + "components": { + "schemas": { + "sensorClass": { + "type": "object", + "properties": { + "data": { + "type": "object", + "$ref": "#/components/schemas/dataClass" + }, + "name": {"type": "string"}, + "id": {"type": "integer"}, + "map_x": {"type": "number"}, + "map_y": {"type": "number"}, + "user": {"type": "string"}, + "config": { + "type": "object", + "$ref": "#/components/schemas/configClass" + }, + "aggregate": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "number" + } + }, + "timestamps": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "eventClass": { + "type": "object", + "properties": { + "data": { + "type": "object", + "$ref": "#/components/schemas/dataClass" + }, + "name": {"type": "string"}, + "id": {"type": "integer"}, + "map_x": {"type": "number"}, + "map_y": {"type": "number"}, + "user": {"type": "string"}, + "config": { + "type": "object", + "$ref": "#/components/schemas/configClass" + } + } + }, + + "configClass": { + "type": "object", + "properties": { + "typeConfig": {"type": "string"}, + "typeData": {"type": "string"} + } + }, + "dataClass": { + "type": "object", + "properties": { + "valueStr": {"type": "string"}, + "value": {"type": "number"}, + "timestamp": {"type": "integer"} + } + } + } + }, + "servers": [ + { + "description": "Hal Server", + "url": "/api" + } + ], + "openapi": "3.0.1", + "paths": { + "/event": { + "get": { + "responses": { + "200": { + "description": "A successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "$ref": "#/components/schemas/eventClass" + } + } + } + } + }, + "parameters": [ + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "id", + "required": false + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "typeConfig", + "required": false + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "typeData", + "required": false + } + ] + } + }, + "/sensor": { + "get": { + "responses": { + "200": { + "description": "A successful response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "$ref": "#/components/schemas/sensorClass" + } + } + } + } + }, + "parameters": [ + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "id", + "required": false + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "typeConfig", + "required": false + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "typeData", + "required": false + }, + { + "schema": { + "type": "string", + "enum": [ + "min", + "hour", + "day", + "week", + ] + }, + "in": "query", + "name": "aggregation", + "required": false + } + ] + } + } + }, + "info": { + "description": "This API allows developers and external tools to interface to Hal data and trigger different actions.", + "title": "Hal REST API", + "version": "" + } +} \ No newline at end of file diff --git a/hal-core/resource/resource/web/sensor_detail.tmpl b/hal-core/resource/resource/web/sensor_detail.tmpl index aaa2fb72..f6979784 100644 --- a/hal-core/resource/resource/web/sensor_detail.tmpl +++ b/hal-core/resource/resource/web/sensor_detail.tmpl @@ -93,8 +93,8 @@ \ No newline at end of file diff --git a/hal-core/resource/resource/web/startup.tmpl b/hal-core/resource/resource/web/startup.tmpl index 457d3223..9a186300 100644 --- a/hal-core/resource/resource/web/startup.tmpl +++ b/hal-core/resource/resource/web/startup.tmpl @@ -5,8 +5,8 @@ HAL is initializing... - - + + diff --git a/hal-core/src/se/hal/HalServer.java b/hal-core/src/se/hal/HalServer.java index a303b03e..a51adeba 100644 --- a/hal-core/src/se/hal/HalServer.java +++ b/hal-core/src/se/hal/HalServer.java @@ -140,7 +140,7 @@ public class HalServer { http.setPage("/", new HttpRedirectPage("/map")); http.setPage(HalAlertManager.getInstance().getUrl(), HalAlertManager.getInstance()); - for (Iterator it = pluginManager.getSingletonIterator(HalJsonPage.class); it.hasNext(); ) + for (Iterator it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); ) registerPage(it.next()); for (Iterator it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); ) registerPage(it.next()); diff --git a/hal-core/src/se/hal/intf/HalAbstractDevice.java b/hal-core/src/se/hal/intf/HalAbstractDevice.java index 6b22de5a..6392c990 100644 --- a/hal-core/src/se/hal/intf/HalAbstractDevice.java +++ b/hal-core/src/se/hal/intf/HalAbstractDevice.java @@ -224,12 +224,12 @@ public abstract class HalAbstractDevice */ -public class EventJsonPage extends HalJsonPage { +public class EventApiEndpoint extends HalApiEndpoint { private static final Logger logger = LogUtil.getLogger(); - public EventJsonPage() { + public EventApiEndpoint() { super("api/event"); } @@ -39,31 +39,49 @@ public class EventJsonPage extends HalJsonPage { DBConnection db = HalContext.getDB(); DataNode root = new DataNode(DataNode.DataType.List); - // Get Events + // -------------------------------------- + // Get Action + // -------------------------------------- String[] req_ids = new String[0]; if (request.get("id") != null) req_ids = request.get("id").split(","); - String req_type = request.get("type"); + + String req_typeConfig = request.get("typeConfig"); + String req_typeData = request.get("typeData"); + + // Filter devices List events = new ArrayList<>(); for (Event event : Event.getLocalEvents(db)) { - if (ArrayUtil.contains(req_ids, "" + event.getId())) { // id filtering - events.add(event); + boolean filter_match = true; + + // id filtering + if (!ObjectUtil.isEmpty(req_ids) && !ArrayUtil.contains(req_ids, "" + event.getId())) { + filter_match = false; } - if (!ObjectUtil.isEmpty(req_type) && - event.getDeviceConfig().getDeviceDataClass().getSimpleName().contains(req_type)) { // device type filtering - events.add(event); + // device type filtering + if (!ObjectUtil.isEmpty(req_typeConfig) && + !event.getDeviceConfig().getClass().getSimpleName().equals(req_typeConfig)) { + filter_match = false; } - // no options defined, then add all events - if (ObjectUtil.isEmpty(req_ids, req_type)) { + // data type filtering + if (!ObjectUtil.isEmpty(req_typeData) && + !event.getDeviceConfig().getDeviceDataClass().getSimpleName().equals(req_typeData)) { + filter_match = false; + } + + // Check the filter + if (filter_match) { events.add(event); } } + // -------------------------------------- // Generate DataNode + // -------------------------------------- for (Event event : events) { DataNode deviceNode = event.getDataNode(); diff --git a/hal-core/src/se/hal/page/api/MapJsonPage.java b/hal-core/src/se/hal/page/api/MapApiEndpoint.java similarity index 95% rename from hal-core/src/se/hal/page/api/MapJsonPage.java rename to hal-core/src/se/hal/page/api/MapApiEndpoint.java index f256c3cb..5e25c68c 100644 --- a/hal-core/src/se/hal/page/api/MapJsonPage.java +++ b/hal-core/src/se/hal/page/api/MapApiEndpoint.java @@ -2,7 +2,7 @@ package se.hal.page.api; import se.hal.HalContext; import se.hal.intf.HalAbstractDevice; -import se.hal.intf.HalJsonPage; +import se.hal.intf.HalApiEndpoint; import se.hal.struct.Event; import se.hal.struct.Sensor; import zutil.db.DBConnection; @@ -16,10 +16,10 @@ import java.util.logging.Logger; /** * TODO: This json endpoint might not be needed as we have SensorJsonPage? */ -public class MapJsonPage extends HalJsonPage { +public class MapApiEndpoint extends HalApiEndpoint { private static final Logger logger = LogUtil.getLogger(); - public MapJsonPage() { + public MapApiEndpoint() { super("api/map"); } diff --git a/hal-core/src/se/hal/page/api/SensorJsonPage.java b/hal-core/src/se/hal/page/api/SensorApiEndpoint.java similarity index 64% rename from hal-core/src/se/hal/page/api/SensorJsonPage.java rename to hal-core/src/se/hal/page/api/SensorApiEndpoint.java index 65903d23..7999e58b 100644 --- a/hal-core/src/se/hal/page/api/SensorJsonPage.java +++ b/hal-core/src/se/hal/page/api/SensorApiEndpoint.java @@ -2,7 +2,7 @@ package se.hal.page.api; import se.hal.HalContext; import se.hal.daemon.SensorDataAggregatorDaemon; -import se.hal.intf.HalJsonPage; +import se.hal.intf.HalApiEndpoint; import se.hal.struct.Sensor; import se.hal.util.AggregateDataListSqlResult; import se.hal.util.UTCTimeUtility; @@ -28,11 +28,11 @@ import java.util.logging.Logger; * aggr: Aggregation periods, needs to be provided to retrieve data. Possible values: minute,hour,day,week * */ -public class SensorJsonPage extends HalJsonPage { +public class SensorApiEndpoint extends HalApiEndpoint { private static final Logger logger = LogUtil.getLogger(); - public SensorJsonPage() { + public SensorApiEndpoint() { super("api/sensor"); } @@ -45,60 +45,83 @@ public class SensorJsonPage extends HalJsonPage { DBConnection db = HalContext.getDB(); DataNode root = new DataNode(DataNode.DataType.List); - // Get sensors - String[] req_ids = new String[0]; + // -------------------------------------- + // Get Action + // -------------------------------------- + + String[] reqIds = new String[0]; if (request.get("id") != null) - req_ids = request.get("id").split(","); - String req_type = request.get("type"); + reqIds = request.get("id").split(","); + + String reqTypeConfig = request.get("typeConfig"); + String reqTypeData = request.get("typeData"); + List sensors = new ArrayList<>(); for (Sensor sensor : Sensor.getSensors(db)) { - if (ArrayUtil.contains(req_ids, "" + sensor.getId())) { // id filtering - sensors.add(sensor); + boolean filter_match = true; + + // id filtering + if (!ObjectUtil.isEmpty(reqIds) && !ArrayUtil.contains(reqIds, "" + sensor.getId())) { + filter_match = false; } - if (!ObjectUtil.isEmpty(req_type) && - sensor.getDeviceConfig().getDeviceDataClass().getSimpleName().contains(req_type)) { // device type filtering - sensors.add(sensor); + // device type filtering + if (!ObjectUtil.isEmpty(reqTypeConfig) && + !sensor.getDeviceConfig().getClass().getSimpleName().equals(reqTypeConfig)) { + filter_match = false; } - // no options defined, then add all sensors - if (ObjectUtil.isEmpty(req_ids, req_type)) { + // data type filtering + if (!ObjectUtil.isEmpty(reqTypeData) && + !sensor.getDeviceConfig().getDeviceDataClass().getSimpleName().equals(reqTypeData)) { + filter_match = false; + } + + // Check the filter + if (filter_match) { sensors.add(sensor); } } - // Figure out aggregation period + // -------------------------------------- + // Was aggregated data requested + // -------------------------------------- + SensorDataAggregatorDaemon.AggregationPeriodLength aggrType = null; - long aggrLength = -1; - if (request.get("aggr") != null) { - switch (request.get("aggr")) { + long aggregationLength = -1; + + if (request.get("aggregation") != null) { + switch (request.get("aggregation")) { case "minute": aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.FIVE_MINUTES; - aggrLength = UTCTimeUtility.DAY_IN_MS; + aggregationLength = UTCTimeUtility.DAY_IN_MS; break; case "hour": aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.HOUR; - aggrLength = UTCTimeUtility.WEEK_IN_MS; + aggregationLength = UTCTimeUtility.WEEK_IN_MS; break; case "day": aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.DAY; - aggrLength = UTCTimeUtility.INFINITY; + aggregationLength = UTCTimeUtility.INFINITY; break; case "week": aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.WEEK; - aggrLength = UTCTimeUtility.INFINITY; + aggregationLength = UTCTimeUtility.INFINITY; break; } } + // -------------------------------------- // Generate DataNode + // -------------------------------------- + for (Sensor sensor : sensors) { DataNode deviceNode = sensor.getDataNode(); - if (aggrLength > 0) { - DataNode aggregateNode = getAggregateDataNode(aggrLength, - AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggrLength)); + if (aggregationLength > 0) { + DataNode aggregateNode = getAggregateDataNode(aggregationLength, + AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggregationLength)); deviceNode.set("aggregate", aggregateNode); } diff --git a/hal-core/src/se/hal/plugin.json b/hal-core/src/se/hal/plugin.json index 35711a8d..26962511 100644 --- a/hal-core/src/se/hal/plugin.json +++ b/hal-core/src/se/hal/plugin.json @@ -12,9 +12,9 @@ {"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataAggregatorDaemon"}, {"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataCleanupDaemon"}, - {"se.hal.intf.HalJsonPage": "se.hal.page.api.MapJsonPage"}, - {"se.hal.intf.HalJsonPage": "se.hal.page.api.EventJsonPage"}, - {"se.hal.intf.HalJsonPage": "se.hal.page.api.SensorJsonPage"}, + {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.EventApiEndpoint"}, + {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.MapApiEndpoint"}, + {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.SensorApiEndpoint"}, {"se.hal.intf.HalWebPage": "se.hal.page.MapWebPage"}, {"se.hal.intf.HalWebPage": "se.hal.page.SensorOverviewWebPage"}, diff --git a/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl b/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl index 4c9965e7..fdc2bfe3 100644 --- a/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl +++ b/plugins/hal-powerchallenge/resource/resource/web/pc_overview.tmpl @@ -20,10 +20,10 @@