From f747216259dbaa7fa926074a39efe1b974a9ad85 Mon Sep 17 00:00:00 2001 From: Michael Hoser Date: Sun, 14 Jul 2024 22:24:57 +0200 Subject: [PATCH] GET API to update a single prop --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++++++------ requests.http | 4 +++ www/api.php | 22 +++++++++++- 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index be23122..80a16bf 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ **dcache** is a PHP REST API implementing a JSON data cache. -* The data cache consists of _data sets_ containing multiple _properties_ (as key-value-pairs). -* A _data set_ is identified by an unique _data token_. +* The data cache consists of _data sets_ containing multiple _properties_ as key-value-pairs. +* A _data set_ is identified by an unique _token_. * _Data Provider_ can create, update, and remove _data sets_. -* _Consumer_ can query a _data set_ based on a provided _data token_. +* _Consumer_ can query a _data set_ identified by the provided _token_. A running demo is available at: . @@ -13,12 +13,12 @@ A running demo is available at: . Usage examples: -* Selected [Home Assistant](https:c//www.home-assistant.io/) entities can be pushed to _dcache_ and visualized on a [TRMNL](https://usetrmnl.com/) e-ink display via a [custom TRMNL-Plugin](https://help.usetrmnl.com/en/articles/9510536-custom-plugins). -* The result status of a DevOps build pipeline can be sent _dcache_ and visualized on a WLED led strip. -* The battery status of multiple IoT devices is available in a single _dcache_ data set. +* Selected [Home Assistant](https:c//www.home-assistant.io/) entities can be pushed to **dcache** and visualized on a [TRMNL](https://usetrmnl.com/) e-ink display via a [custom TRMNL-Plugin](https://help.usetrmnl.com/en/articles/9510536-custom-plugins). +* The result status of a DevOps build pipeline can be sent to **dcache** and visualized on a WLED led strip. +* The battery status of multiple IoT devices is available in a single **dcache** data set. * ... -A typical flow between Home Assistant, dcache, and TRMNL could look like this: +A typical data flow between Home Assistant, **dcache**, and TRMNL could look like this: * HA provides `temperature=25` * HA provides `humidity=65` @@ -47,12 +47,12 @@ No system wide settings are modified during the installation. The database content can be reset to the initial test data with `reset-testdata.cmd`. Already existing data in dcache tables will be removed! -All tools can be removed safely by deleting the `bin` and `node_modules` sub-folders. +All tools and test data can be removed completely by deleting the `bin` and `node_modules` sub-folders. ## Run dcache locally * Execute `npm start` or choose `Terminal > Run Build Task...` in VSCode. -* Open the local dev webserver in your browser. +* Open the local dev webserver address in your browser. * Stop the servers by pressing `Ctrl-C` in the Terminal window. ## Settings @@ -77,10 +77,87 @@ $settings["prefix"] = "dc-"; The REST API is exposed at `/api.php`. For a list of supported requests and related responses, please refer to the [`requests.http`](./requests.http) sample file. - All requests can be executed against the provided PHP development webserver in order to test and debug the API. This requires VSCode with the [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension installed. +| Method | URL | Description | Success Response | +|--------|---------------------------------------|-------------------------------------------|--------------------------------| +| GET | `/api.php?token=T` | Query complete dataset `T` | Dataset `T` as object `{...}` | +| GET | `/api.php?token=T&property=P` | Query property `P` from dataset `T` | Value of `P` as string `"val"` | +| GET² | `/api.php?token=T&property=P&value=V` | Update property `P` to `V` in dataset `T` | Updated dataset `T` as object | +| POST | `/api.php?token=T` | Update provided properties in dataset `T` | Updated dataset `T` as object | +| DELETE | `/api.php?token=T` | Remove complete dataset `T` | Boolean `true` | +| DELETE | `/api.php?token=T&property=P` | Remove property `P` from dataset `T` | Boolean `true` | + +² - This non standard REST operation behaves like a `POST` of the single property `P`, but can be executed from a browser. + +### Restrictions + +| Type | Length | Characters | +|-------------------|----------|-------------------------------| +| Dataset token `T` | min. `8` | `a-z`, `A-Z`, `0-9`, `-`, `.` | +| Property name `P` | min. `1` | `a-z`, `0-9`, `-`, `.` | + +### Request Body (POST) + +A `POST` request can be used to update one or more properties of an existing dataset, which is identified by the given token `T`. If the token `T` was not found, a new dataset related to this token is created. The `POST` payload needs to contain the a JSON object in `Content-Type: application/json`. + +Example: + +If the given dataset, which is related to token `testdata`: + +```json +{ + "firstname": "Max", + "lastname": "Mustermann" +} +``` + +is updated via `POST /api.php?token=testdata` containing the `Content-Type: application/json` payload: + +```json +{ + "firstname": "Hans", + "town": "New York" +} +``` + +it will return the updated dataset as JSON object as: + +```json +{ + "firstname": "Hans", + "lastname": "Mustermann", + "town": "New York" +} +``` + +### Error Response Messages + +In case of errors, a HTTP response code different from `200` (OK) is returned together with an JSON object describing the error. + +```json +{ + "kind": "error", + "code": 400, + "message": "Token contains invalid characters. Only letters, numbers, dots and dashes are allowed." +} +``` + +Possible errors: + +| Code | Message | +|----------------------|----------------------------------------------------------------------------------------| +| `400` Bad Request | Content is not valid JSON. | +| `400` Bad Request | Invalid content type. Expected application/json. | +| `400` Bad Request | No token given. | +| `400` Bad Request | Not implemented. | +| `400` Bad Request | Token contains invalid characters. Only letters, numbers, dots and dashes are allowed. | +| `400` Bad Request | Token is too short. Must be at least 8 characters long. | +| `404` Not found | Dataset not found. | +| `404` Not found | Property not found in dataset. | +| `500` Internal Error | Not configured. | + ## License See [LICENSE](LICENSE). diff --git a/requests.http b/requests.http index eb0a6d1..d5b68d7 100644 --- a/requests.http +++ b/requests.http @@ -51,6 +51,10 @@ GET {{server}}/api.php?token=testdata&property=doesnotexist HTTP/1.1 ### -> 200 (ok) string response GET {{server}}/api.php?token=testdata&property=firstname HTTP/1.1 +### GET with correct token, property, and value +### -> 200 (ok) updated JSON object response +GET {{server}}/api.php?token=testdata&property=firstname&value=Egon HTTP/1.1 + ### ============================================================================ ### SET/UPDATE DATA ### ============================================================================ diff --git a/www/api.php b/www/api.php index d6c0158..be609b9 100644 --- a/www/api.php +++ b/www/api.php @@ -17,7 +17,11 @@ switch ($_SERVER['REQUEST_METHOD']) { case "GET": - handleGet($db, $_REQUEST['token'], $_REQUEST['property']); + if (isset($_REQUEST['value'])) { + handleGetUpdate($db, $_REQUEST['token'], $_REQUEST['property'], $_REQUEST['value']); + } else { + handleGet($db, $_REQUEST['token'], $_REQUEST['property']); + } break; case "POST": @@ -79,6 +83,22 @@ function handleGet($db, $token, $property) } } +function handleGetUpdate($db, $token, $property, $value) +{ + if (!validateToken($token)) return false; + + if (!isset($property) || is_null($property) || empty($property)) { + Rest::respondError(Rest::CODE_400_BAD_REQUEST, "No property given."); + return false; + } + + $db->deleteProperty($token, $property); + $db->insertProperty($token, $property, $value); + + $data = $db->getData($token); + Rest::respond(Rest::CODE_200_OK, $data); +} + function handlePost($db, $token, $contentType) { if (!validateToken($token)) return;