Code Monkey home page Code Monkey logo

wazicloud-api's Introduction

WAZIUP API Server

waziup-api is a component of the Waziup platform

Install

Installation follows the standard approach to installing Stack-based projects.

  1. Install the Haskell stack tool.
  2. Run stack install --fast to install this package.

Run

Once installed, you can run the program as follows:

stack run

Command line help is available at waziup-servant --help. API documentation can be viewed at http://localhost:3000/docs

Develop

Any IDE can be used to develop. I like ghcid with this .ghcid:

--command="stack ghci --no-package-hiding --load waziup:waziup-servant --ghci-options=-fno-break-on-exception --ghci-options=-fno-break-on-error --ghci-options=-v1 --ghci-options=-ferror-spans --ghci-options=-j --ghci-options=-fno-warn-orphans" --warnings --test=:main

ghcid will reload the code for each change, and launch the server if everything is OK.

Work with subtrees:

git subtree pull --prefix=keycloak-hs keycloak-hs master --squash
git subtree push --prefix=keycloak-hs keycloak-hs master --squash

wazicloud-api's People

Contributors

cdupont avatar

Watchers

 avatar James Cloos avatar  avatar  avatar

wazicloud-api's Issues

MQTT Fail messages

MQTT Publish seems to fail randomly:

[2019-07-11 12:47:04 UTC : MQTT : DEBUG] Received: Fail "\NUL\"devices/0ff8373bo/sensors/HU/value{\"value\": \"2.21\"}" [] "string"
[2019-07-11 12:47:11 UTC : MQTT : DEBUG] Received: Fail "\NUL\"devices/0ff8373bo/sensors/TC/value{\"value\": \"6.00\"}" [] "string"
[2019-07-11 12:47:11 UTC : MQTT : DEBUG] Received: Fail "\NUL\"devices/0ff8373bo/sensors/HU/value{\"value\": \"2.21\"}" [] "string"
[2019-07-11 12:48:52 UTC : MQTT : DEBUG] Received: Fail "devices/0ff8373bo/sensors/TC/value" [] "string"
[2019-07-11 12:48:52 UTC : MQTT : DEBUG] Received: Fail "{\"value\": \"6.00\"}" [] "string"
[2019-07-11 12:48:52 UTC : MQTT : DEBUG] Received: Fail "\NUL\"" [] "string"
[2019-07-11 12:48:52 UTC : MQTT : DEBUG] Received: Fail "devices/0ff8373bo/sensors/HU/value" [] "string"
[2019-07-11 12:48:52 UTC : MQTT : DEBUG] Received: Fail "{\"value\": \"2.21\"}" [] "string"
[2019-07-11 12:49:04 UTC : MQTT : DEBUG] Received: Fail "\NUL\"devices/0ff8373bo/sensors/TC/value{\"value\": \"6.00\"}" [] "string"
[2019-07-11 12:49:04 UTC : MQTT : DEBUG] Received: Fail "\NUL\"devices/0ff8373bo/sensors/HU/value{\"value\": \"2.21\"}" [] "string"
[2019-07-11 12:49:12 UTC : MQTT : DEBUG] Received: Fail "\NUL\"" [] "string"
[2019-07-11 12:49:12 UTC : MQTT : DEBUG] Received: Fail "devices/0ff8373bo/sensors/TC/value{\"value\": \"6.00\"}" [] "string"
[2019-07-11 12:49:12 UTC : MQTT : DEBUG] Received: Fail "\NUL\"" [] "string"
[2019-07-11 12:49:12 UTC : MQTT : DEBUG] Received: Fail "devices/0ff8373bo/sensors/HU/value{\"value\": \"2.21\"}" [] "string"

The payload does not seem to be framed correctly: sometime it begins too early or too late.

Link between devices and gateways

Currently, the API represent the link between a device and a gateway, e.g. "a device radio data has been received by a gateway". This link is represented as a field in Device data structure:

device: 
{
  id: XX
  gateway_id: YYY
}

It's not the best because:

  • finding the devices connected to a gateway takes time, because you need to scan all the devices (thousands) and also verify the authorization.
  • a device could be received by several gateways, this is not representable: gateway_id field has only one gateway.

umlaut names not supported

The following API call fails, most likely because of the umlaut letter "ä":

await fetch("https://api.waziup.io/api/v2/devices/MyGuestSensor/sensors/TC/value", {
	method: "POST",
	headers: {
		"Content-Type": "application/json; charset=UTF-8"
    },
    body: JSON.stringify({
		value: "Hällo",
		timestamp: new Date()
    })
});

Returns: 500 Something went wrong

Without the umlaut there are no problems.

Space leak

Waziup-API might have a space leak: memory grows a lot.

Screenshot from 2019-10-01 14-28-32

array values endpoint, sync endpoint

For synchronization mechanisms with the Gateway, an endpoint is required that allows the upload of multiple separated data points. This can be achieved with multiple calls to the POST devices/xxx/sensors/yyy/value endpoint, what is a very inefficient solution.

Based on the /value endpoint (see the swagger spec.), the /values should be like:

Headers:

  • Content-Type: application/json

Body: array of data points, can have length 0, ordered by timestamp (ascending, latest value last)
Data Point: a {Value,Timestamp} Object
Value: required, any type, not null
Timestamp: required, timestamp (JavaScript Date)

Example:

[{
  "value": 25,
  "timestamp": "2016-06-08T18:00:00.000Z"
},{
  "value": 27,
  "timestamp": "2016-06-08T19:00:00.000Z"
},{
  "value": 24,
  "timestamp": "2016-06-08T20:00:00.000Z"
}]

Note that the API implementation might require to only submit values that are newer than the existing values at the API database. This might prevent the upload of values from the past when there are newer data points present.

Wrong data return by sensors_data endpoint

Retrieving data in descending order doesn't return the last datapoint.
Today is 2019-06-04 but it returns data from yesterday:

cdupont@cdupont-XPS:~$ curl -X GET "https://api.staging.waziup.io/api/v2/sensors_data?device_id=UGB-PILOTS_Sensor200&sensor_id=BV&limit=1&sort=dsc" -H  "accept: text/csv;charset=utf-8"
device_id,sensor_id,value,timestamp,date_received
UGB-PILOTS_Sensor200,BV,4.11,2019-06-03T16:15:30Z,2019-06-03T16:15:39Z

Adding just a "date_from" filter makes it return the last datapoint:

$ curl -X GET "https://api.staging.waziup.io/api/v2/sensors_data?device_id=UGB-PILOTS_Sensor200&sensor_id=BV&limit=1&sort=dsc&date_from=2019-06-04T06:43:21Z" -H  "accept: text/csv;charset=utf-8"
device_id,sensor_id,value,timestamp,date_received
UGB-PILOTS_Sensor200,BV,4.11,2019-06-04T16:07:06Z,2019-06-04T16:07:16Z

Support list of sensors and devices in /sensors_data

Currently, GET /sensors_data need two parameters: device_id and sensor_id.
Idea is to replace them with plural devices and sensors.
Both parameters can be supplied with a list of values, or a start * for all values.

Example:

curl -X GET "https://api.waziup.io/api/v2/sensors_data?devices=Dev1,Dev2&sensors=*" -H  "accept: application/json;charset=utf-8"

Missing parts

  • limit/offset in notifications and socials
  • ontologies checking in sensors and actuators
  • check existence of gateway in devices
  • check existence of devices and gateways in projects
  • permissions for projects, gateways, notifications
  • replace q request
  • domain in projects

Improvements:

  • change exception package
  • add messages on throw
  • remove type checks in Orion
  • remove warnings

Notifications and security

Notifications triggering will call socials/batch.
This in turn requires Keycloak authorizations:

  • "view users" to retrieve the user twitter/phone number
  • "manage users" to write new SMS amount.

However, notifications triggers cannot carry a token, because it will be expired. How to recognize the author of the subscription, and authorize him?

Permissions cache

A cache containing the permissions for each users can be built:

permsCache :: Map Username PermCache

PermCache :: {
  perms :: [Perm],
  retrievedTime :: UTCTime
}

Perm :: {
  resourceId :: String,
  scopes :: [Scope]
}

The cache is a map between the users and their permissions. The permissions is a list including all resources (devices, gateways, projects) and all scopes (create, delete...). There are currently 700 resources with 3-5 scopes per resources. It takes 700ms to extract all permissions from Keycloak.

The cache starts empty. At each request, the cache is tested: if the user doesn't have a cache, or if the cache is outdated, all permissions are requested from Keycloak for that user. The cache is then updated. If the user does have a valid cache, it is used instead.

Any change of resource in Keycloak (create, update visibility, delete) should update invalidate the cache. Invalidating the cache simply mean to delete the full cache and restart empty.
The cache entries have a validity of ~5min. This means that changes in the policies will be reflected after max 5 min.

MongoDB performance

When querying datapoints, the performance is bad:

$ time curl -X GET "http://localhost:800/api/v2/sensors_data?device_id=MyDevice&sensor_id=TC1&calibrated=false" -H  "accept: application/json;charset=utf-8"
[{"sensor_id":"TC1","date_received":"2019-11-06T16:31:49Z","device_id":"MyDevice","value":25,"timestamp":"2016-06-08T18:20:27.873Z"}]
real	0m2,594s
user	0m0,006s
sys	0m0,000s

There are currently 3 million datapoints in DB.

HTTP/1.1 415 Unsupported Media Type

The API PUTs need header "Content-Type: application/json;charset=utf-8".
Would be better to simplify to "Content-Type: application/json".

Clashing when creating a device and a gateway with the same ID

When creating a gateway, and then a device with the same ID, the gateway resource created in Keycloak gets overwritten. This leads to authorization problems afterwards.

Screenshot from 2019-10-23 11-52-22

For example, in this image the ID of the resource is gateway-00155d00641b (in the URL), which identifies it as a gateway. But the data in the resource corresponds to a device.

Running without MQTT gives errors

curl -X POST "http://localhost:3000/api/v2/devices/MyDevice/sensors/TC1/value" -H  "accept: application/json;charset=utf-8" -H  "Content-Type: application/json;charset=utf-8" -d "{  \"value\": \"\",  \"timestamp\": \"2016-06-08T18:20:27.873Z\"}"
Something went wrong

It should be possible to run the platform without MQTT (HTTP only)

Permissions not updated after MQTT Connect

In the current implementation, the permissions are retrieved from Keycloak and stored in the API server during the MQTT CONNECT. All further actions (i.e. Publishes) will use the same permissions, until the disconnect.
This is a problem when permissions change between the Connect and Disconnect. E.g. if the user connects, creates a device, and try to publish on that device, it will not work: the new device will not appear in the permissions stored.

Exceptions mismatch

The code uses Control.Monad.Catch.try, but it seems that this function cannot catch exceptions thrown by throwError. Instead, the function catchError should be used.

Use device ID as keycloak ID

Currently, a device contains the keycloak ID as a field.
It would be better to use the device ID directly as the resource ID in Keycloak (as is already the case for projects and gateways).
This requires a database migration:

  • Each Keycloak resource need to be destroyed and re-created with the right ID
  • Each Orion device need to drop the keycloak_id field.

MQTT connect goes off

The field "connected" of gateways goes to false after some time.
Currently:
MQTT CONNECT -> connected = true
TCP thread dies -> connected = false

403 "Forbidden" when sending SMS

$ curl -X POST "https://api.waziup.io/api/v2/socials" -H  "accept: text/plain;charset=utf-8" -H  "Content-Type: application/json;charset=utf-8" -d "{  \"channel\": \"sms\",  \"username\": \"cdupont\",  \"message\": \"Test message\"}" -v
HTTP/1.1 403 "Forbidden"

Slow API responses

The API is currently slow. for GET /devices it can take from 1600ms to 7s when the server is loaded (rush hour).

Twitter ID @

Currently, the Twitter ID should not contain the "@". If you include it, it will not work.
Let's remove it automatically when it's present.

Calibration problem

Currently, when a calibration is present in the "sensor" datastructure, this calibration will be used to calculate the values output by the API (e.g. in devices/XX/sensors/YY/values).
This will cause problems because the "value/values" fields of a sensor changes meaning.
For instance the synchronisation module in the gateways will think this is the raw value, while this might be the calibrated value.

error 400 instead of 404

curl -X PUT "http://localhost:800/api/v2/devices/MyDevice/actuators/Act1/value" -H  "accept: application/json;charset=utf-8" -H  "Content-Type: application/json;charset=utf-8" -d "444"

gives:

400
Error from Keycloak: {"error":"invalid_resource","error_description":"Resource with id [device-MyDevice] does not exist."}

It should be 404.

MQTT subscription: return device_id and sensor_id

Currently, MQTT subscriptions return just the value:

{"value": "3"}

However, you can subscribe to a variety or devices/sensors using wildcards:

mosquitto_sub -L "mqtt://api.waziup.io/devices/MyDevice/sensors/+/value"

So, it would be better to return also device_id and sensor_id.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.