From 273df65856b254cf848d99e7d0c57c7e1d7681a6 Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Fri, 21 Jul 2017 16:51:34 +0200 Subject: [PATCH 01/12] update license --- license.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/license.md b/license.md index 08d9ccc6..e6f68b77 100644 --- a/license.md +++ b/license.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Matthias Pfeil +Copyright (c) 2015-2017 Matthias Pfeil, Gerald Pape Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. From 4c8eba6b8b950c26e2cc81671a16a0088a3b3675 Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Mon, 24 Jul 2017 13:34:13 +0200 Subject: [PATCH 02/12] move sketch generation to own package --- .dockerignore | 1 - .gitignore | 1 - .npmignore | 3 - config/index.js | 1 - docker-compose.yml | 1 - .../template_gisgk/template_gisgk.ino | 230 --------------- .../template_home_2014/template_home_2014.ino | 203 ------------- .../template_photonik_ethernet.ino | 178 ------------ .../template_photonik_wifi.ino | 213 -------------- files/template_basic/template_basic.ino | 208 -------------- .../template_custom_setup.ino | 107 ------- files/template_home/template_home.ino | 243 ---------------- .../template_home_feinstaub.ino | 255 ----------------- .../template_home_wifi/template_home_wifi.ino | 249 ---------------- .../template_home_wifi_feinstaub.ino | 269 ------------------ lib/controllers/boxesController.js | 11 +- lib/mails.js | 13 +- lib/models/box.js | 10 +- lib/models/user.js | 2 - lib/sketches/index.js | 62 ---- lib/sketches/processGeneric.js | 46 --- lib/sketches/processWifi.js | 90 ------ package.json | 1 + tests-docker-compose.yml | 1 - yarn.lock | 10 + 25 files changed, 24 insertions(+), 2384 deletions(-) delete mode 100644 files/deprecated/template_gisgk/template_gisgk.ino delete mode 100644 files/deprecated/template_home_2014/template_home_2014.ino delete mode 100644 files/deprecated/template_photonik/template_photonik_ethernet/template_photonik_ethernet.ino delete mode 100644 files/deprecated/template_photonik/template_photonik_wifi/template_photonik_wifi.ino delete mode 100644 files/template_basic/template_basic.ino delete mode 100644 files/template_custom_setup/template_custom_setup.ino delete mode 100644 files/template_home/template_home.ino delete mode 100644 files/template_home_feinstaub/template_home_feinstaub.ino delete mode 100644 files/template_home_wifi/template_home_wifi.ino delete mode 100644 files/template_home_wifi_feinstaub/template_home_wifi_feinstaub.ino delete mode 100644 lib/sketches/index.js delete mode 100644 lib/sketches/processGeneric.js delete mode 100644 lib/sketches/processWifi.js diff --git a/.dockerignore b/.dockerignore index a5d63c88..8b8340c6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,6 @@ node_modules mongo-data userimages -usersketches osem-mongo docker-compose.yml Dockerfile diff --git a/.gitignore b/.gitignore index 82c54aaa..509ad917 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ node_modules config/index.js -usersketches userimages mongo-data .env diff --git a/.npmignore b/.npmignore index ac5b5d9d..1ab7ef72 100644 --- a/.npmignore +++ b/.npmignore @@ -15,10 +15,7 @@ dump npm-debug.log node_modules -files/ -!files/template.ino -usersketches userimages mongo-data .env diff --git a/config/index.js b/config/index.js index 8f6b7dce..3cec9e22 100644 --- a/config/index.js +++ b/config/index.js @@ -8,7 +8,6 @@ const isProdEnv = function isProdEnv () { // environment variables starting with `OSEM_` will override the values here. // Example: `OSEM_targetfolder` will override the setting for `targetFolder` const config = { - targetFolder: './usersketches/', // absolute location of generated arduino sketch ino files imageFolder: './userimages/', // absolute location of user images logFolder: './logs/', // absolute location of error log files diff --git a/docker-compose.yml b/docker-compose.yml index 907b5f9f..8af3c41d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,6 @@ services: - ./app.js:/usr/src/app/app.js - ./lib:/usr/src/app/lib - ./config/index.js:/usr/src/app/config/index.js - - ./usersketches:/usr/src/app/usersketches - ./userimages:/usr/src/app/userimages env_file: .env depends_on: diff --git a/files/deprecated/template_gisgk/template_gisgk.ino b/files/deprecated/template_gisgk/template_gisgk.ino deleted file mode 100644 index 45cb3016..00000000 --- a/files/deprecated/template_gisgk/template_gisgk.ino +++ /dev/null @@ -1,230 +0,0 @@ -/* -senseBox Citizen Sensingplatform -Version: 1.2 -Date: 2015-01-06 -Homepage: http://www.sensebox.de -Author: Jan Wirwahn, Institute for Geoinformatics, University of Muenster -Note: Version with additional sensors for GIS-GK@ifgi -*/ - -#include -#include -#include -#include -#include -#include -#include - -//senseBox ID - -//Sensor IDs - -//Sensor pin settings -#define NOISEPIN A0 -#define DHTPIN A1 -#define UVPIN A2 -#define DHTTYPE DHT11 -//Ethernet shield settings -#define W5200_CS 10 -#define SDCARD_CS 4 - -//Network settings -IPAddress ip(192,168,178,111);//If DHCP is disabled, specify an IP according to your network settings -byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; -char server[] = "opensensemap.org"; -EthernetClient client; - -String currentSensorId = TEMPERATURESENSOR_ID; -float temperature; -float humidity; -float pressure; -unsigned long lux; -int analogNoise; -int uvValue; - -DHT dht(DHTPIN, DHTTYPE); -Barometer barometer; - -//Variables for time sync and upload intervall -const unsigned long postingInterval = 10*1000*60; //1 minute -unsigned long lastConnectionTime = 0; -boolean lastConnected = false; -boolean uploadSuccess = true; -String sensorSample; -int sampleType = 1; //1=temp,2=humi,3=baro,4=light - -void setup(){ - Wire.begin(); - Serial.begin(9600); - //Setup chip select and deselect the SD card - pinMode(SDCARD_CS,OUTPUT); - digitalWrite(SDCARD_CS,HIGH); - delay(500); - // start the Ethernet connection - if (Ethernet.begin(mac) == 0) { - Serial.println("Failed to configure Ethernet using DHCP."); - //If DHCP is disabled, use static IP - Ethernet.begin(mac, ip); - }else Serial.println("DHCP setup successful."); - //Print the IP to serial port - Serial.print("My IP address: "); - for (byte thisByte = 0; thisByte < 4; thisByte++) { - // print the value of each byte of the IP address: - Serial.print(Ethernet.localIP()[thisByte], DEC); - Serial.print("."); - } - Serial.println(); - barometer.init(); - dht.begin(); - TSL2561.init(); -} - -void loop(){ - // if there's incoming data from the net connection. - // send it out the serial port. This is for debugging purposes only - if (client.available()) { - char c = client.read(); - Serial.print(c); - } - // if there's no net connection, but there was one last time - // through the loop, then stop the client: - if (!client.connected() && lastConnected) { - Serial.println(); - Serial.println(); - Serial.println("...stopping client!"); - client.stop(); - } - //If last upload was successful, switch case to select the next sensor - if (uploadSuccess){ - Serial.println(); - delay(1000); - sensorSample = ""; - switch (sampleType) - { - case 1: - //temperature = dht.readTemperature(); - temperature = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - sensorSample = floatToString(temperature,0); - currentSensorId = TEMPERATURESENSOR_ID; - break; - case 2: - humidity = dht.readHumidity(); - sensorSample = floatToString(humidity,0); - currentSensorId = HUMIDITYSENSOR_ID;//bmp085ReadUT MUST be called first - break; - case 3: - pressure = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - pressure = barometer.bmp085GetPressure(barometer.bmp085ReadUP()); - sensorSample = floatToString(pressure,0); - currentSensorId = PRESSURESENSOR_ID; - break; - case 4: - analogNoise = analogRead(NOISEPIN); - sensorSample = (String)analogNoise; - currentSensorId = NOISESENSOR_ID; - break; - case 5: - TSL2561.getLux(); - lux = TSL2561.calculateLux(0,0,1); - sensorSample = (String)lux; - currentSensorId = LIGHTSENSOR_ID; - break; - case 6: - uvValue = analogRead(UVPIN); - sensorSample = (String)calcUVIndex(uvValue); - currentSensorId = UVSENSOR_ID; - break; - } - } - // if you're not already connected, and 60 seconds have passed since - // your last connection, then connect again and send data: - uploadSuccess = false; - if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) { - Serial.println("-----------------------------------"); - Serial.print("Connecting and posting sensor sample (case "); - Serial.print(sampleType); - Serial.print(")..."); - Serial.println(); - postObservation(sensorSample, currentSensorId, SENSEBOX_ID); - } - // store the state of the connection for next time through the loop - lastConnected = client.connected(); -} - -String floatToString(float number, int precision) -{ - String stringNumber = ""; - //int prec; - //only temperature (case 1) has a decimal place - //if (sampleType == 1) prec = 1; else prec = 0; - char tempChar[10]; - dtostrf(number, 3, precision, tempChar); - stringNumber += tempChar; - return stringNumber; -} - -int calcUVIndex(int analogValue){ - int uvi; - if (analogValue<10) uvi = 0; - else if (analogValue<46) uvi = 1; - else if (analogValue<65) uvi = 2; - else if (analogValue<83) uvi = 3; - else if (analogValue<103) uvi = 4; - else if (analogValue<124) uvi = 5; - else if (analogValue<142) uvi = 6; - else if (analogValue<162) uvi = 7; - else if (analogValue<180) uvi = 8; - else if (analogValue<200) uvi = 9; - else if (analogValue<221) uvi = 10; - else if (analogValue<240) uvi = 11; - return uvi; -} - -//Method for posting a measurement to OSM server -void postObservation(String measurement, String sensorId, String boxId) -{ - //if (measurement=="") return; - wdt_enable(WDTO_8S); - //json must look like {"value":"12.5"} - String valueJson = "{\"value\":"; - valueJson += measurement; - valueJson += "}"; - Serial.println(valueJson); - //post observation to: http://opensensemap.org:8000/boxes/boxId/sensorId - // if you get a connection, report back via serial: - wdt_reset(); - if (client.connect(server, 8000)) - { - Serial.print("connected..."); - Serial.println(); - // Make a HTTP Post request: - client.print("POST /boxes/"); - client.print(boxId); - client.print("/"); - client.print(sensorId); - client.println(" HTTP/1.1"); - // Send the required header parameters - client.println("Host:opensensemap.org"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.print("Content-Length: "); - client.println(valueJson.length()); - client.println(); - client.print(valueJson); - client.println(); - Serial.println("done!"); - uploadSuccess = true; - //Change case - if (sampleType == 6) { - sampleType = 1; - // remember the time that the connection was made or attempted - lastConnectionTime = millis(); - }else sampleType++; - }else - { - // stop the client if you couldn't make a connection ! - Serial.println("failed...disconnecting."); - client.stop(); - } - wdt_disable(); -} diff --git a/files/deprecated/template_home_2014/template_home_2014.ino b/files/deprecated/template_home_2014/template_home_2014.ino deleted file mode 100644 index 756585d0..00000000 --- a/files/deprecated/template_home_2014/template_home_2014.ino +++ /dev/null @@ -1,203 +0,0 @@ -/* -senseBox Citizen Sensingplatform -Version: 1.1 -Date: 2015-01-14 -Homepage: http://www.sensebox.de -Author: Jan Wirwahn, Institute for Geoinformatics, University of Muenster -Note: Sketch for SB-Home Edition 2014 -*/ - -#include -#include -#include -#include -#include -#include - -//IDs are being generated by the OSM server -//senseBox ID - -//Sensor IDs - -//Sensor pin settings -#define NOISEPIN A0 -#define LIGHTPIN A1 -#define DHTPIN A2 -#define DHTTYPE DHT11 -//Ethernet shield settings -#define W5200_CS 10 -#define SDCARD_CS 4 - -//Network settings -IPAddress ip(192,168,178,111);//If DHCP is disabled, specify an IP according to your network settings -byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; -char server[] = "opensensemap.org"; -EthernetClient client; - -String currentSensorId = TEMPERATURESENSOR_ID; -float temperature, humidity, pressure; -int analogNoise, analogLight; - -DHT dht(DHTPIN, DHTTYPE); -Barometer barometer; - -//Variables for time sync and upload intervall -const unsigned long postingInterval = 10*1000*60; //1 minute -unsigned long lastConnectionTime = 0; -boolean lastConnected = false; -boolean uploadSuccess = true; -String sensorSample; -int sampleType = 1; -int phenomenonCount = 5; //1=temp,2=humi,3=baro,4=noise,5=light - -void setup(){ - Wire.begin(); - Serial.begin(9600); - //Setup chip select and deselect the SD card - pinMode(SDCARD_CS,OUTPUT); - digitalWrite(SDCARD_CS,HIGH); - delay(500); - // start the Ethernet connection - if (Ethernet.begin(mac) == 0) { - Serial.println("Failed to configure Ethernet using DHCP."); - //If DHCP is disabled, use static IP - Ethernet.begin(mac, ip); - }else Serial.println("DHCP setup successful."); - //Print the IP to serial port - Serial.print("My IP address: "); - for (byte thisByte = 0; thisByte < 4; thisByte++) { - // print the value of each byte of the IP address: - Serial.print(Ethernet.localIP()[thisByte], DEC); - Serial.print("."); - } - Serial.println(); - barometer.init(); - dht.begin(); -} - -void loop(){ - // if there's incoming data from the net connection. - // send it out the serial port. This is for debugging purposes only - if (client.available()) { - char c = client.read(); - Serial.print(c); - } - // if there's no net connection, but there was one last time - // through the loop, then stop the client: - if (!client.connected() && lastConnected) { - Serial.println(); - Serial.println(); - Serial.println("...stopping client!"); - client.stop(); - } - //If last upload was successful, switch case to select the next sensor - if (uploadSuccess){ - Serial.println(); - delay(1000); - sensorSample = ""; - switch (sampleType) - { - case 1: - //temperature = dht.readTemperature(); - temperature = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - sensorSample = floatToString(temperature,1); - currentSensorId = TEMPERATURESENSOR_ID; - break; - case 2: - humidity = dht.readHumidity(); - sensorSample = floatToString(humidity,0); - currentSensorId = HUMIDITYSENSOR_ID;//bmp085ReadUT MUST be called first - break; - case 3: - pressure = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - pressure = barometer.bmp085GetPressure(barometer.bmp085ReadUP()); - sensorSample = floatToString(pressure,0); - currentSensorId = PRESSURESENSOR_ID; - break; - case 4: - analogNoise = analogRead(NOISEPIN); - sensorSample = (String)analogNoise; - currentSensorId = NOISESENSOR_ID; - break; - case 5: - analogLight = analogRead(LIGHTPIN); - sensorSample = (String)analogLight; - currentSensorId = LIGHTSENSOR_ID; - break; - } - } - // if you're not already connected, and 60 seconds have passed since - // your last connection, then connect again and send data: - uploadSuccess = false; - if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) { - Serial.println("-----------------------------------"); - Serial.print("Connecting and posting sensor sample (case "); - Serial.print(sampleType); - Serial.print(")..."); - Serial.println(); - postObservation(sensorSample, currentSensorId, SENSEBOX_ID); - } - // store the state of the connection for next time through the loop - lastConnected = client.connected(); -} - -String floatToString(float number, int precision) -{ - String stringNumber = ""; - //int prec; - //only temperature (case 1) has a decimal place - //if (sampleType == 1) prec = 1; else prec = 0; - char tempChar[10]; - dtostrf(number, 3, precision, tempChar); - stringNumber += tempChar; - return stringNumber; -} - -//Method for posting a measurement to OSM server -void postObservation(String measurement, String sensorId, String boxId) -{ - //if (measurement=="") return; - wdt_enable(WDTO_8S); - //json must look like {"value":"12.5"} - String valueJson = "{\"value\":"; - valueJson += measurement; - valueJson += "}"; - Serial.println(valueJson); - //post observation to: http://opensensemap.org:8000/boxes/boxId/sensorId - // if you get a connection, report back via serial: - wdt_reset(); - if (client.connect(server, 8000)) - { - Serial.print("connected..."); - Serial.println(); - // Make a HTTP Post request: - client.print("POST /boxes/"); - client.print(boxId); - client.print("/"); - client.print(sensorId); - client.println(" HTTP/1.1"); - // Send the required header parameters - client.println("Host:opensensemap.org"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.print("Content-Length: "); - client.println(valueJson.length()); - client.println(); - client.print(valueJson); - client.println(); - Serial.println("done!"); - uploadSuccess = true; - //Change case - if (sampleType == phenomenonCount) { - sampleType = 1; - // remember the time that the connection was made or attempted - lastConnectionTime = millis(); - }else sampleType++; - }else - { - // stop the client if you couldn't make a connection ! - Serial.println("failed...disconnecting."); - client.stop(); - } - wdt_disable(); -} diff --git a/files/deprecated/template_photonik/template_photonik_ethernet/template_photonik_ethernet.ino b/files/deprecated/template_photonik/template_photonik_ethernet/template_photonik_ethernet.ino deleted file mode 100644 index 6b8310b0..00000000 --- a/files/deprecated/template_photonik/template_photonik_ethernet/template_photonik_ethernet.ino +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Jan Wirwahn, Institute for Geoinformatics, Uni Muenster - * July 2015 - * Arduino Web-Client for pushing senseBox:Home measurements - * to the OpenSenseMap server. - */ - -#include -#include -#include -#include -#include -#include - -#define UV_ADDR 0x38 -#define IT_1 0x1 - -//INDIVIDUAL SETUP -IPAddress ip(192, 168, 179, 194); //IP Adress -IPAddress dn(192, 168, 179, 10); //DNS Server -IPAddress gw(192, 168, 179, 254); //Gateway -byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x86, 0x46 }; //MAC Adress - -char server[] = "opensensemap.org"; -EthernetClient client; - -//IDs are being generated by the OSM server -//If you want to update your old sketch, insert the IDs from your old file -//senseBox ID - -//Sensor IDs - - -BMP280 bmp; -HDC100X HDC(0x43); -//Makerblog_TSL45315 luxsensor = Makerblog_TSL45315(TSL45315_TIME_M4); - -//measurement variables -float temperature = 0; -float humidity = 0; -double tempBaro, pressure; -uint32_t lux; -int messTyp; -byte msb=0, lsb=0; -uint16_t uv; -float uvIndex; - -//post sample each minute -int postInterval = 60000; -long timeOld = 0; -long timeNew = 0; - - -void setup() -{ - Serial.begin(9600); -//Try DHCP first - - Ethernet.begin(mac, ip, dn, gw); - HDC.begin(HDC100X_TEMP_HUMI,HDC100X_14BIT,HDC100X_14BIT,DISABLE); - //Serial.println(luxsensor.begin()); - if(!bmp.begin()){ - Serial.println("BMP init failed!"); - while(1); - } - else Serial.println("BMP init success!"); - bmp.setOversampling(4); - Wire.begin(); - Wire.beginTransmission(UV_ADDR); - Wire.write((IT_1<<2) | 0x02); - Wire.endTransmission(); - delay(500); -} - -void loop() -{ - timeNew = millis(); - if (timeNew - timeOld > postInterval) - { - //-----Pressure-----// - messTyp = 1; - char result = bmp.startMeasurment(); - if(result!=0){ - delay(result); - result = bmp.getTemperatureAndPressure(tempBaro,pressure); - pressure = pressure * 100; - postObservation(pressure, PRESSURESENSOR_ID, SENSEBOX_ID); - } - //-----Humidity-----// - messTyp = 2; - humidity = HDC.getHumi(); - postObservation(humidity, HUMIDITYSENSOR_ID, SENSEBOX_ID); - //-----Temperature-----// - messTyp = 2; - temperature = HDC.getTemp(); - postObservation(temperature, TEMPERATURESENSOR_ID, SENSEBOX_ID); - //-----Lux-----// - messTyp = 1; - //lux = luxsensor.readLux(); - //Serial.print("Illumi = "); Serial.println(lux); - //postObservation(lux, LUXSENSOR_ID, SENSEBOX_ID); - //-----UV-Index-----// - messTyp = 2; - Wire.requestFrom(UV_ADDR+1, 1); //MSB - delay(1); - if(Wire.available()) - msb = Wire.read(); - Wire.requestFrom(UV_ADDR+0, 1); //LSB - delay(1); - if(Wire.available()) - lsb = Wire.read(); - uv = (msb<<8) | lsb; - uvIndex = uv * 5.625; - - postObservation(uvIndex, UVSENSOR_ID, SENSEBOX_ID); - - timeOld = millis(); - } -} - -void postObservation(float measurement, String sensorId, String boxId) -{ - char obs[10]; - if (messTyp == 1) dtostrf(measurement, 5, 0, obs); - else if (messTyp == 2) dtostrf(measurement, 5, 2, obs); - Serial.println(obs); - //json must look like: {"value":"12.5"} - //post observation to: http://opensensemap.org:8000/boxes/boxId/sensorId - Serial.println("connecting..."); - String value = "{\"value\":"; - value += obs; - value += "}"; - // if you get a connection, report back via serial: - boxId += "/"; - if (client.connect(server, 8000)) - { - Serial.println("connected"); - // Make a HTTP Post request: - client.print("POST /boxes/"); - client.print(boxId); - client.print("/"); - client.print(sensorId); - client.println(" HTTP/1.1"); - // Send the required header parameters - client.println("Host:opensensemap.org"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.print("Content-Length: "); - client.println(value.length()); - client.println(); - client.print(value); - client.println(); - } - waitForResponse(); -} - -void waitForResponse() -{ - // if there are incoming bytes available - // from the server, read them and print them: - boolean repeat = true; - do{ - if (client.available()) - { - char c = client.read(); - Serial.print(c); - } - // if the servers disconnected, stop the client: - if (!client.connected()) - { - Serial.println(); - Serial.println("disconnecting."); - client.stop(); - repeat = false; - } - } - while (repeat); -} diff --git a/files/deprecated/template_photonik/template_photonik_wifi/template_photonik_wifi.ino b/files/deprecated/template_photonik/template_photonik_wifi/template_photonik_wifi.ino deleted file mode 100644 index 8b6a18e4..00000000 --- a/files/deprecated/template_photonik/template_photonik_wifi/template_photonik_wifi.ino +++ /dev/null @@ -1,213 +0,0 @@ -/* -senseBox Citizen Sensingplatform - -Sensor Connections on Base-Shield - A0: UV-Sensor - A1: Temperature and Humidity Sensor Pro -I2C: Pressure Sensor -I2C: Digital Light Sensor - -Version: 1.3.2 -Date: 2015-02-04 -Homepage: http://www.sensebox.de -Author: Jan Wirwahn, Institute for Geoinformatics, University of Muenster -Note: Sketch for SB-Photonik-WiFi. -*/ -#include -#include -#include -#include -#include -#include -//#include - -//senseBox ID - -//Sensor IDs - -// WLAN parameters -#define WLAN_SSID "WiFi_Name" // cannot be longer than 32 characters! -#define WLAN_PASS "WiFi_Password" -// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2 -#define WLAN_SECURITY WLAN_SEC_WPA2 - -// These are the interrupt and control pins -#define ADAFRUIT_CC3000_IRQ 3 -#define ADAFRUIT_CC3000_VBAT 5 -#define ADAFRUIT_CC3000_CS 10 -#define IDLE_TIMEOUT_MS 3000 - -Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT, - SPI_CLOCK_DIVIDER); // you can change this clock speed -//Server values -#define WEBSITE "www.opensensemap.org" -uint32_t ip = 2159055603; - -//Sensor pin settings -#define UVPIN A0 -#define DHTPIN A1 -#define DHTTYPE DHT22 - -String currentSensorId = TEMPERATURESENSOR_ID; -float temperature, humidity, pressure; -unsigned long lux; -int analogUvLight,contLen; - -short phenomenonCount = 5; //1=temp,2=humi,3=pressure,4=lux,5=UV -int sampleType = 1; //begin with temperature -boolean uploadSuccess = true; -String sensorSample,jsonData; - -DHT dht(DHTPIN, DHTTYPE); -Barometer barometer; - -void setup(void) -{ - Serial.begin(9600); - Serial.println(F("\nInitializing WiFi...")); - if (!cc3000.begin()) - { - Serial.println(F("failed! Check your wiring?")); - while(1); - } - - Serial.print(F("\nAttempting to connect to ")); Serial.println(WLAN_SSID); - if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) { - Serial.println(F("Failed!")); - while(1); - } - - Serial.println(F("Connected!")); - - /* Wait for DHCP to complete */ - Serial.println(F("Requesting DHCP")); - while (!cc3000.checkDHCP()) - { - delay(100); // ToDo: Insert a DHCP timeout! - } - ip = 0; - Serial.print(WEBSITE); Serial.print(F(" -> ")); - while (ip == 0) { - if (! cc3000.getHostByName(WEBSITE, &ip)) { - Serial.println(F("Couldn't resolve!")); - } - delay(500); - } - cc3000.printIPdotsRev(ip); - Serial.println("\nSTARTING UP"); - barometer.init(); - dht.begin(); - TSL2561.init(); -} - -void loop(void) -{ - if (uploadSuccess){ - Serial.print("Reading sensor..."); - sensorSample = ""; - switch (sampleType) - { - case 1: - temperature = dht.readTemperature(); - //temperature = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - sensorSample = floatToString(temperature,0); - currentSensorId = TEMPERATURESENSOR_ID; - break; - case 2: - humidity = dht.readHumidity(); - sensorSample = floatToString(humidity,0); - currentSensorId = HUMIDITYSENSOR_ID;//bmp085ReadUT MUST be called first - break; - case 3: - pressure = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - pressure = barometer.bmp085GetPressure(barometer.bmp085ReadUP()); - sensorSample = floatToString(pressure,0); - currentSensorId = PRESSURESENSOR_ID; - break; - case 4: - TSL2561.getLux(); - lux = TSL2561.calculateLux(0,0,1); - sensorSample = (String)lux; - currentSensorId = LUXSENSOR_ID; - break; - case 5: - analogUvLight = analogRead(UVPIN); - sensorSample = (String)calcUVIndex(analogUvLight); - currentSensorId = UVSENSOR_ID; - break; - } - Serial.println("done..................."); - } - - jsonData = "{\"value\":"; - jsonData += sensorSample; - jsonData += "}"; - Serial.println(jsonData); - contLen = jsonData.length(); - Adafruit_CC3000_Client client = cc3000.connectTCP(ip, 8000); - - if (client.connected()) { - client.print("POST /boxes/"); - client.print(SENSEBOX_ID); - client.print("/"); - client.print(currentSensorId); - client.print(" HTTP/1.1\r\n"); - client.println("Host: opensensemap.org"); - client.println("Content-Type: application/json"); - client.print("Content-Length: "); - client.println(contLen); - client.println("Connection: close"); - client.println(); - client.print(jsonData); - client.println(); - Serial.println("done!"); - uploadSuccess = true; - } else { - Serial.println(F("Connection failed")); - uploadSuccess = false; - return; - } - - Serial.println(F("-------------------------------------")); - - unsigned long lastRead = millis(); - while (client.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) { - while (client.available()) { - char c = client.read(); - Serial.print(c); - lastRead = millis(); - } - } - client.close(); - Serial.println(F("-------------------------------------")); - if (uploadSuccess){ - if (sampleType == phenomenonCount) { - sampleType = 1; - }else sampleType++; - } - - Serial.println(F("\n\nConnection closed.")); - - delay(1000); -} - -String floatToString(float number, int precision) -{ - String stringNumber = ""; - char tempChar[10]; - dtostrf(number, 1, precision, tempChar); - stringNumber += tempChar; - return stringNumber; -} - -int calcUVIndex(int analogVal){ - // get voltage from analog value - float uvindex = map(analogVal, 0, 1023, 0, 5000); - uvindex /= 1000; - //illumination intensity => V_sig * 307 - uvindex = uvindex * 307; - //UVI => illumination intesity / 200 - uvindex = uvindex / 200; - - return int(uvindex+0.5); -} diff --git a/files/template_basic/template_basic.ino b/files/template_basic/template_basic.ino deleted file mode 100644 index 3c9bbec8..00000000 --- a/files/template_basic/template_basic.ino +++ /dev/null @@ -1,208 +0,0 @@ -/* -senseBox Citizen Sensingplatform -Version: 1.2 -Date: 2015-02-02 -Homepage: http://www.sensebox.de -Author: Jan Wirwahn, Institute for Geoinformatics, University of Muenster -Note: Sketch for SB-Home Edition 2015 -*/ - -#include -#include -#include -#include -#include -#include -#include - -//IDs are being generated by the OSM server -//If you want to update your old sketch, insert the IDs from your old file -//senseBox ID - -//Sensor IDs - -//Sensor pin settings -#define NOISEPIN A0 -#define DHTPIN A1 -#define DHTTYPE DHT11 -//Ethernet shield settings -#define W5200_CS 10 -#define SDCARD_CS 4 - -//Network settings -IPAddress ip(192,168,0,111);//If DHCP is disabled, specify an IP according to your network settings -byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; -char server[] = "@@OSEM_POST_DOMAIN@@"; -EthernetClient client; - -String currentSensorId = TEMPERATURESENSOR_ID; -float temperature, humidity, pressure; -unsigned long lux; -int analogNoise; - -DHT dht(DHTPIN, DHTTYPE); -Barometer barometer; - -//Variables for time sync and upload intervall -const unsigned long postingInterval = 10*1000*60; //1 minute -unsigned long lastConnectionTime = 0; -boolean lastConnected = false; -boolean uploadSuccess = true; -String sensorSample; -int sampleType = 1; -int phenomenonCount = 5; //1=temp,2=humi,3=baro,4=noise,5=lux - -void setup(){ - Wire.begin(); - Serial.begin(9600); - //Setup chip select and deselect the SD card - pinMode(SDCARD_CS,OUTPUT); - digitalWrite(SDCARD_CS,HIGH); - delay(500); - // start the Ethernet connection - if (Ethernet.begin(mac) == 0) { - Serial.println("Failed to configure Ethernet using DHCP."); - //If DHCP is disabled, use static IP - Ethernet.begin(mac, ip); - }else Serial.println("DHCP setup successful."); - //Print the IP to serial port - Serial.print("My IP address: "); - for (byte thisByte = 0; thisByte < 4; thisByte++) { - // print the value of each byte of the IP address: - Serial.print(Ethernet.localIP()[thisByte], DEC); - Serial.print("."); - } - Serial.println("STARTING UP"); - barometer.init(); - dht.begin(); - TSL2561.init(); -} - -void loop(){ - // if there's incoming data from the net connection. - // send it out the serial port. This is for debugging purposes only - if (client.available()) { - char c = client.read(); - Serial.print(c); - } - // if there's no net connection, but there was one last time - // through the loop, then stop the client: - if (!client.connected() && lastConnected) { - Serial.println(); - Serial.println(); - Serial.println("STARTING UP"); - client.stop(); - } - //If last upload was successful, switch case to select the next sensor - if (uploadSuccess){ - Serial.println(); - delay(1000); - sensorSample = ""; - switch (sampleType) - { - case 1: - //temperature = dht.readTemperature(); - temperature = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - sensorSample = floatToString(temperature,1); - currentSensorId = TEMPERATURESENSOR_ID; - break; - case 2: - humidity = dht.readHumidity(); - sensorSample = floatToString(humidity,0); - currentSensorId = HUMIDITYSENSOR_ID;//bmp085ReadUT MUST be called first - break; - case 3: - pressure = barometer.bmp085GetTemperature(barometer.bmp085ReadUT()); - pressure = barometer.bmp085GetPressure(barometer.bmp085ReadUP()); - sensorSample = floatToString(pressure,0); - currentSensorId = PRESSURESENSOR_ID; - break; - case 4: - analogNoise = analogRead(NOISEPIN); - sensorSample = (String)analogNoise; - currentSensorId = NOISESENSOR_ID; - break; - case 5: - TSL2561.getLux(); - lux = TSL2561.calculateLux(0,0,1); - sensorSample = (String)lux; - currentSensorId = LUXSENSOR_ID; - break; - } - } - // if you're not already connected, and 60 seconds have passed since - // your last connection, then connect again and send data: - uploadSuccess = false; - if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) { - Serial.println("-----------------------------------"); - Serial.print("Connecting and posting sensor sample (case "); - Serial.print(sampleType); - Serial.print(")..."); - Serial.println(); - postObservation(sensorSample, currentSensorId, SENSEBOX_ID); - } - // store the state of the connection for next time through the loop - lastConnected = client.connected(); -} - -String floatToString(float number, int precision) -{ - String stringNumber = ""; - //int prec; - //only temperature (case 1) has a decimal place - //if (sampleType == 1) prec = 1; else prec = 0; - char tempChar[10]; - dtostrf(number, 3, precision, tempChar); - stringNumber += tempChar; - return stringNumber; -} - -//Method for posting a measurement to OSM server -void postObservation(String measurement, String sensorId, String boxId) -{ - //if (measurement=="") return; - wdt_enable(WDTO_8S); - //json must look like {"value":"12.5"} - String valueJson = "{\"value\":"; - valueJson += measurement; - valueJson += "}"; - Serial.println(valueJson); - //post observation to: /boxes/boxId/sensorId - // if you get a connection, report back via serial: - wdt_reset(); - if (client.connect(server, 80)) - { - Serial.print("connected..."); - Serial.println(); - // Make a HTTP Post request: - client.print("POST /boxes/"); - client.print(boxId); - client.print("/"); - client.print(sensorId); - client.println(" HTTP/1.1"); - // Send the required header parameters - client.print("Host:"); - client.println(server); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.print("Content-Length: "); - client.println(valueJson.length()); - client.println(); - client.print(valueJson); - client.println(); - Serial.println("done!"); - uploadSuccess = true; - //Change case - if (sampleType == phenomenonCount) { - sampleType = 1; - // remember the time that the connection was made or attempted - lastConnectionTime = millis(); - }else sampleType++; - }else - { - // stop the client if you couldn't make a connection ! - Serial.println("failed...disconnecting."); - client.stop(); - } - wdt_disable(); -} diff --git a/files/template_custom_setup/template_custom_setup.ino b/files/template_custom_setup/template_custom_setup.ino deleted file mode 100644 index 45486846..00000000 --- a/files/template_custom_setup/template_custom_setup.ino +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -/* - Zusätzliche Sensorbibliotheken, -Variablen etc im Folgenden einfügen. -*/ - -//senseBox ID - -//Sensor IDs - -//Ethernet-Parameter -char server[] = "@@OSEM_POST_DOMAIN@@"; -byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -// Diese IP Adresse nutzen falls DHCP nicht möglich -IPAddress myIP(192, 168, 0, 42); -EthernetClient client; - -//Messparameter -const unsigned int postingInterval = 60000; //Uploadintervall in Millisekunden - -void setup () { - Serial.begin(9600); - Serial.print("Starting network..."); - //Ethernet Verbindung mit DHCP ausführen.. - if (Ethernet.begin(mac) == 0) - { - Serial.println("DHCP failed!"); - //Falls DHCP fehltschlägt, mit manueller IP versuchen - Ethernet.begin(mac, myIP); - } - Serial.println("done!"); - delay(1000); - Serial.println("Starting loop."); -} - -void loop () { - /* - Hier Sensoren auslesen und nacheinerander über postFloatValue(...) hochladen. Beispiel: - - float temperature = sensor.readTemperature(); - postFloatValue(temperature, 1, temperatureSensorID); - */ - sleep(postingInterval); -} - -void postFloatValue (float measurement, int digits, String sensorId) { - //Float zu String konvertieren - char obs[10]; - dtostrf(measurement, 5, digits, obs); - //Json erstellen - String jsonValue = "{\"value\":"; - jsonValue += obs; - jsonValue += "}"; - //Mit OSeM Server verbinden und POST Operation durchführen - Serial.println("-------------------------------------"); - Serial.print("Connectingto OSeM Server..."); - if (client.connect(server, 80)) { - Serial.println("connected!"); - Serial.println("-------------------------------------"); - //HTTP Header aufbauen - client.print("POST /boxes/"); client.print(SENSEBOX_ID); client.print("/"); client.print(sensorId); client.println(" HTTP/1.1"); - client.print("Host:"); - client.println(server); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.print("Content-Length: "); client.println(jsonValue.length()); - client.println(); - //Daten senden - client.println(jsonValue); - } else { - Serial.println("failed!"); - Serial.println("-------------------------------------"); - } - //Antwort von Server im seriellen Monitor anzeigen - waitForServerResponse(); -} - -void waitForServerResponse () { - //Ankommende Bytes ausgeben - boolean repeat = true; - do { - if (client.available()) { - char c = client.read(); - Serial.print(c); - } - //Verbindung beenden - if (!client.connected()) { - Serial.println(); - Serial.println("--------------"); - Serial.println("Disconnecting."); - Serial.println("--------------"); - client.stop(); - repeat = false; - } - } while (repeat); -} - -// millis() rollover fix - http://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover -void sleep(unsigned long ms) { // ms: duration - unsigned long start = millis(); // start: timestamp - for (;;) { - unsigned long now = millis(); // now: timestamp - unsigned long elapsed = now - start; // elapsed: duration - if (elapsed >= ms) // comparing durations: OK - return; - } -} diff --git a/files/template_home/template_home.ino b/files/template_home/template_home.ino deleted file mode 100644 index 363c27f8..00000000 --- a/files/template_home/template_home.ino +++ /dev/null @@ -1,243 +0,0 @@ -/* - senseBox Home - Citizen Sensingplatform - Version: 2.4 - Date: 2017-Mar-06 - Homepage: https://www.sensebox.de https://www.opensensemap.org - Author: Institute for Geoinformatics, University of Muenster - Note: Sketch for senseBox:home - Email: support@sensebox.de -*/ - -#include -#include "HDC100X.h" -#include "BMP280.h" -#include -#include -#include - -typedef struct sensor { - const uint8_t ID[12]; -} sensor; - -uint8_t sensorsIndex = 0; - -@-- tmpl ctSensors - -@-- tmpl IDs - -uint8_t contentLength = 0; -float values[NUM_SENSORS]; - -//Configure static IP setup (only needed if DHCP is disabled) -IPAddress myIp(192, 168, 0, 42); -IPAddress myDns(8, 8, 8, 8); -IPAddress myGateway(192, 168, 0, 177); -IPAddress mySubnet(255, 255, 255, 0); - -//Ethernet configuration -byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -@-- tmpl postDomain // const char *server = ""; -EthernetClient client; - -//Load sensors -Makerblog_TSL45315 TSL = Makerblog_TSL45315(TSL45315_TIME_M4); -HDC100X HDC(0x43); -BMP280 BMP; - -//measurement variables -float temperature = 0; -double tempBaro, pressure; -int count = 1; -char result; -#define UV_ADDR 0x38 -#define IT_1 0x1 - -const unsigned int postingInterval = 60000; - -void setup() { - sleep(2000); - Serial.begin(9600); - Serial.println("senseBox Home software version 2.4"); - Serial.println(); - sleep(1000); - Serial.print("Initializing DHCP connection..."); - if (Ethernet.begin(mac) == 0) { - Serial.println("failed! Trying static IP setup."); - Ethernet.begin(mac, myIp, myDns, myGateway, mySubnet); - //@TODO: Add reference to support site for network settings - } - else { - Serial.println("done!"); - } - sleep(1000); - Serial.print("Initializing sensors..."); - Wire.begin(); - Wire.beginTransmission(UV_ADDR); - Wire.write((IT_1 << 2) | 0x02); - Wire.endTransmission(); - sleep(500); - HDC.begin(HDC100X_TEMP_HUMI, HDC100X_14BIT, HDC100X_14BIT, DISABLE); - TSL.begin(); - BMP.begin(); - BMP.setOversampling(4); - Serial.println("done!"); - temperature = HDC.getTemp(); - Serial.println("Starting loop.\n"); -} - -void loop() { - addValue(HDC.getTemp()); - sleep(200); - addValue(HDC.getHumi()); - result = BMP.startMeasurment(); - if (result != 0) { - sleep(result); - result = BMP.getTemperatureAndPressure(tempBaro, pressure); - } - addValue(pressure); - addValue(TSL.readLux()); - addValue(getUV()); - - submitValues(); - - sleep(postingInterval); -} - -void addValue(const float &value) { - values[sensorsIndex] = value; - sensorsIndex = sensorsIndex + 1; -} - -uint16_t getUV() { - byte msb = 0, lsb = 0; - uint16_t uvValue; - Wire.requestFrom(UV_ADDR + 1, 1); //MSB - sleep(1); - if (Wire.available()) msb = Wire.read(); - Wire.requestFrom(UV_ADDR + 0, 1); //LSB - sleep(1); - if (Wire.available()) lsb = Wire.read(); - uvValue = (msb << 8) | lsb; - return uvValue * 5.625; -} - -int printHexToStream(const uint8_t *data, uint8_t length, Print &stream) // prints 8-bit data in hex -{ - byte first; - int j = 0; - for (uint8_t i=0; i> 4) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - - first = (data[i] & 0x0F) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - } - return j; -} - -int printCsvToStream(Print &stream) { - int len = 0; - for (uint8_t i = 0; i < sensorsIndex; i++) { - if (!isnan(values[i])) { - len = len + printHexToStream(sensors[i].ID, 12, stream); - len = len + stream.print(","); - //do not print digits for illuminance und uv-intensity - if (i < 3 || i > 4) len = len + stream.println(values[i],1); - else len = len + stream.println(values[i],0); - } - } - return len; -} - - -// millis() rollover fix - http://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover -void sleep(unsigned long ms) { // ms: duration - unsigned long start = millis(); // start: timestamp - for (;;) { - unsigned long now = millis(); // now: timestamp - unsigned long elapsed = now - start; // elapsed: duration - if (elapsed >= ms) // comparing durations: OK - return; - } -} - -void waitForResponse() -{ - // if there are incoming bytes from the server, read and print them - sleep(100); - String response = ""; - char c; - boolean repeat = true; - do { - if (client.available()) c = client.read(); - else repeat = false; - response += c; - if (response == "HTTP/1.1 ") response = ""; - if (c == '\n') repeat = false; - } - while (repeat); - - Serial.print("Server Response: "); Serial.print(response); - - client.flush(); - client.stop(); -} - -void submitValues() { - // close any connection before send a new request. - // This will free the socket on the WiFi shield - Serial.println("__________________________\n"); - if (client.connected()) { - client.stop(); - sleep(1000); - } - // if there's a successful connection: - if (client.connect(server, 80)) { - - Serial.println("connecting..."); - // send the HTTP POST request: - - client.print(F("POST /boxes/")); - printHexToStream(SENSEBOX_ID, 12, client); - client.println(F("/data HTTP/1.1")); - - // !!!!! DO NOT REMOVE !!!!! - // !!!!! NICHT LÖSCHEN !!!!! - // print once to Serial to get the content-length - int contentLen = printCsvToStream(Serial); - // !!!!! DO NOT REMOVE !!!!! - // !!!!! NICHT LÖSCHEN !!!!! - - // Send the required header parameters - client.print(F("Host: ")); - client.println(server); - client.print(F("Content-Type: text/csv\nConnection: close\nContent-Length: ")); - client.println(contentLen); - client.println(); - printCsvToStream(client); - client.println(); - Serial.println("done!"); - - waitForResponse(); - - // reset index - sensorsIndex = 0; - - } - else { - // if you couldn't make a connection: - Serial.println("connection failed. Restarting System."); - sleep(5000); - asm volatile (" jmp 0"); - } -} diff --git a/files/template_home_feinstaub/template_home_feinstaub.ino b/files/template_home_feinstaub/template_home_feinstaub.ino deleted file mode 100644 index 9b3282b2..00000000 --- a/files/template_home_feinstaub/template_home_feinstaub.ino +++ /dev/null @@ -1,255 +0,0 @@ -/* - senseBox Home - Citizen Sensingplatform - Version: 2.5 - Date: 2016-May-24 - Homepage: https://www.sensebox.de https://www.opensensemap.org - Author: Institute for Geoinformatics, University of Muenster - Note: Sketch for senseBox:home with dust particle upgrade - Email: support@sensebox.de - Code is in the public domain. -*/ - -#include -#include "HDC100X.h" -#include "BMP280.h" -#include -#include -#include -#include - -bool debug = 0; - -typedef struct sensor { - const uint8_t ID[12]; -} sensor; - -uint8_t sensorsIndex = 0; - -@-- tmpl ctSensors - -@-- tmpl IDs - -uint8_t contentLength = 0; -float values[NUM_SENSORS]; - -//Configure static IP setup (only needed if DHCP is disabled) -IPAddress myIp(192, 168, 0, 42); -IPAddress myDns(8, 8, 8, 8); -IPAddress myGateway(192, 168, 0, 177); -IPAddress mySubnet(255, 255, 255, 0); - -//Ethernet configuration -byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -@-- tmpl postDomain // const char *server = ""; -EthernetClient client; - -//Load sensors -Makerblog_TSL45315 TSL = Makerblog_TSL45315(TSL45315_TIME_M4); -HDC100X HDC(0x43); -BMP280 BMP; -SDS011 my_sds(Serial); - -//measurement variables -float temperature = 0; -double tempBaro, pressure; -int count = 1; -char result; -#define UV_ADDR 0x38 -#define IT_1 0x1 -float pm10,pm25; -int error; - -const unsigned int postingInterval = 60000; - -void setup() { - sleep(2000); - Serial.begin(9600); - sleep(1000); - if(debug) Serial.print("Initializing DHCP connection..."); - if (Ethernet.begin(mac) == 0) { - if(debug) Serial.println("failed! Trying static IP setup."); - Ethernet.begin(mac, myIp, myDns, myGateway, mySubnet); - //@TODO: Add reference to support site for network settings - } - else { - if(debug) Serial.println("done!"); - } - sleep(1000); - if(debug) Serial.print("Initializing sensors..."); - Wire.begin(); - Wire.beginTransmission(UV_ADDR); - Wire.write((IT_1 << 2) | 0x02); - Wire.endTransmission(); - sleep(500); - HDC.begin(HDC100X_TEMP_HUMI, HDC100X_14BIT, HDC100X_14BIT, DISABLE); - TSL.begin(); - BMP.begin(); - BMP.setOversampling(4); - if(debug) Serial.println("done!"); - temperature = HDC.getTemp(); - if(debug) Serial.println("Starting loop in 30 seconds.\n"); - sleep(30000); //heat-up time for dust particle sensor -} - -void loop() { - addValue(HDC.getTemp()); - sleep(200); - addValue(HDC.getHumi()); - result = BMP.startMeasurment(); - if (result != 0) { - sleep(result); - result = BMP.getTemperatureAndPressure(tempBaro, pressure); - } - else pressure = 0; - addValue(pressure); - addValue(TSL.readLux()); - addValue(getUV()); - error = my_sds.read(&pm25,&pm10); - if (error) { - pm25 = 0; - pm10 = 0; - } - addValue(pm10); - addValue(pm25); - - submitValues(); - - sleep(postingInterval); -} - -void addValue(const float &value) { - values[sensorsIndex] = value; - sensorsIndex = sensorsIndex + 1; -} - -uint16_t getUV() { - byte msb = 0, lsb = 0; - uint16_t uvValue; - Wire.requestFrom(UV_ADDR + 1, 1); //MSB - sleep(1); - if (Wire.available()) msb = Wire.read(); - Wire.requestFrom(UV_ADDR + 0, 1); //LSB - sleep(1); - if (Wire.available()) lsb = Wire.read(); - uvValue = (msb << 8) | lsb; - return uvValue * 5.625; -} - -int printHexToStream(const uint8_t *data, uint8_t length, Print &stream) // prints 8-bit data in hex -{ - byte first; - int j = 0; - for (uint8_t i=0; i> 4) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - - first = (data[i] & 0x0F) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - } - return j; -} - -int printCsvToStream(Print &stream) { - int len = 0; - for (uint8_t i = 0; i < sensorsIndex; i++) { - if (!isnan(values[i])) { - len = len + printHexToStream(sensors[i].ID, 12, stream); - len = len + stream.print(","); - //do not print digits for illuminance und uv-intensity - if (i < 3 || i > 4) len = len + stream.println(values[i],1); - else len = len + stream.println(values[i],0); - } - } - return len; -} - - -// millis() rollover fix - http://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover -void sleep(unsigned long ms) { // ms: duration - unsigned long start = millis(); // start: timestamp - for (;;) { - unsigned long now = millis(); // now: timestamp - unsigned long elapsed = now - start; // elapsed: duration - if (elapsed >= ms) // comparing durations: OK - return; - } -} - -void waitForResponse() -{ - // if there are incoming bytes from the server, read and print them - sleep(100); - String response = ""; - char c; - boolean repeat = true; - do { - if (client.available()) c = client.read(); - else repeat = false; - response += c; - if (response == "HTTP/1.1 ") response = ""; - if (c == '\n') repeat = false; - } - while (repeat); - - if(debug) {Serial.print("Server Response: "); Serial.print(response);} - - client.flush(); - client.stop(); -} - -void submitValues() { - // close any connection before send a new request. - // This will free the socket on the WiFi shield - if(debug) Serial.println("__________________________\n"); - if (client.connected()) { - client.stop(); - sleep(1000); - } - // if there's a successful connection: - if (client.connect(server, 80)) { - - if(debug) Serial.println("connecting..."); - // send the HTTP POST request: - - client.print(F("POST /boxes/")); - printHexToStream(SENSEBOX_ID, 12, client); - client.println(F("/data HTTP/1.1")); - - // !!!!! NICHT LÖSCHEN !!!!! - // print once to Serial to get the content-length - int contentLen = printCsvToStream(Serial); - // !!!!! DO NOT REMOVE !!!!! - - // Send the required header parameters - client.print(F("Host: ")); - client.println(server); - client.print(F("Content-Type: text/csv\nConnection: close\nContent-Length: ")); - client.println(contentLen); - client.println(); - printCsvToStream(client); - client.println(); - if(debug) Serial.println("done!"); - - waitForResponse(); - - // reset index - sensorsIndex = 0; - - } - else { - // if you couldn't make a connection: - if(debug)Serial.println("connection failed. Restarting System."); - sleep(5000); - asm volatile (" jmp 0"); - } -} diff --git a/files/template_home_wifi/template_home_wifi.ino b/files/template_home_wifi/template_home_wifi.ino deleted file mode 100644 index 18ad814c..00000000 --- a/files/template_home_wifi/template_home_wifi.ino +++ /dev/null @@ -1,249 +0,0 @@ -/* - senseBox Citizen Sensingplatform - WiFi Version: 1.2 - Date: 2017-02-28 - Homepage: http://www.sensebox.de - Author: Institute for Geoinformatics, University of Muenster - Note: Sketch for SB-Home WiFi Edition - Code is in the public domain. -*/ - -#include -#include "BMP280.h" -#include -#include -#include -#include -#include - -//Custom WiFi Parameters -const char ssid[] = ""; // your network SSID (name) -const char pass[] = ""; // your network password - -//Network settings -@-- tmpl postDomain // const char *server = ""; -uint8_t status = WL_IDLE_STATUS; -WiFiClient client; - -//Sensor Instances -Makerblog_TSL45315 TSL = Makerblog_TSL45315(TSL45315_TIME_M4); -HDC100X HDC(0x43); -BMP280 BMP; - -//measurement variables -#define UV_ADDR 0x38 -#define IT_1 0x1 -double tempBaro, pressure; -char result; - -typedef struct sensor { - const uint8_t ID[12]; -} sensor; - -uint8_t sensorsIndex = 0; - -@-- tmpl ctSensors - -@-- tmpl IDs - -uint8_t contentLength = 0; - -float values[NUM_SENSORS]; - -void addValue(const float &value) { - values[sensorsIndex] = value; - sensorsIndex = sensorsIndex + 1; -} - -uint16_t getUV() { - byte msb = 0, lsb = 0; - uint16_t uvValue; - Wire.requestFrom(UV_ADDR + 1, 1); //MSB - sleep(1); - if (Wire.available()) msb = Wire.read(); - Wire.requestFrom(UV_ADDR + 0, 1); //LSB - sleep(1); - if (Wire.available()) lsb = Wire.read(); - uvValue = (msb << 8) | lsb; - return uvValue * 5.625; -} - -int printHexToStream(const uint8_t *data, uint8_t length, Print &stream) // prints 8-bit data in hex -{ - byte first; - int j = 0; - for (uint8_t i=0; i> 4) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - - first = (data[i] & 0x0F) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - } - return j; -} - -int printCsvToStream(Print &stream) { - int len = 0; - for (uint8_t i = 0; i < sensorsIndex; i++) { - if (!isnan(values[i])) { - len = len + printHexToStream(sensors[i].ID, 12, stream); - len = len + stream.print(","); - //do not print digits for illuminance und uv-intensity - if (i < 3 || i > 4) len = len + stream.println(values[i],1); - else len = len + stream.println(values[i],0); - } - } - return len; -} - - -// millis() rollover fix - http://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover -void sleep(unsigned long ms) { // ms: duration - unsigned long start = millis(); // start: timestamp - for (;;) { - unsigned long now = millis(); // now: timestamp - unsigned long elapsed = now - start; // elapsed: duration - if (elapsed >= ms) // comparing durations: OK - return; - } -} - -void waitForResponse() -{ - // if there are incoming bytes from the server, read and print them - sleep(100); - String response = ""; - char c; - boolean repeat = true; - do { - if (client.available()) c = client.read(); - else repeat = false; - response += c; - if (response == "HTTP/1.1 ") response = ""; - if (c == '\n') repeat = false; - } - while (repeat); - - Serial.print("Server Response: "); Serial.print(response); - - client.flush(); - client.stop(); -} - -void submitValues() { - // close any connection before send a new request. - // This will free the socket on the WiFi shield - Serial.println("__________________________\n"); - if (client.connected()) { - client.stop(); - sleep(1000); - } - // if there's a successful connection: - if (client.connect(server, 80)) { - - Serial.println("connecting..."); - // send the HTTP POST request: - - client.print(F("POST /boxes/")); - printHexToStream(SENSEBOX_ID, 12, client); - client.println(F("/data HTTP/1.1")); - - // !!!!! DO NOT REMOVE !!!!! - // !!!!! NICHT LÖSCHEN !!!!! - // print once to Serial to get the content-length - int contentLen = printCsvToStream(Serial); - // !!!!! DO NOT REMOVE !!!!! - // !!!!! NICHT LÖSCHEN !!!!! - - // Send the required header parameters - client.print(F("Host: ")); - client.println(server); - client.print(F("Content-Type: text/csv\nConnection: close\nContent-Length: ")); - client.println(contentLen); - client.println(); - printCsvToStream(client); - client.println(); - Serial.println("done!"); - - waitForResponse(); - - // reset index - sensorsIndex = 0; - - } - else { - // if you couldn't make a connection: - Serial.println("connection failed. Restarting System."); - sleep(5000); - asm volatile (" jmp 0"); - } -} - -void setup() { - //Initialize serial and wait for port to open: - Serial.begin(9600); - //Enable Wifi Shield - pinMode(4, INPUT); - digitalWrite(4, HIGH); - sleep(2000); - //Check WiFi Shield status - if (WiFi.status() == WL_NO_SHIELD) { - Serial.println("WiFi shield not present"); - // don't continue: - while (true); - } - // attempt to connect to Wifi network: - while ( status != WL_CONNECTED) { - Serial.print("Attempting to connect to SSID: "); - Serial.println(ssid); - // Connect to WPA/WPA2 network. Change this line if using open or WEP network - status = WiFi.begin(ssid, pass); - // wait 60 seconds for connection: - Serial.println(); - Serial.print("Waiting 10 seconds for connection..."); - sleep(10000); - Serial.println("done."); - } - Serial.print("Initializing sensors..."); - Wire.begin(); - Wire.beginTransmission(UV_ADDR); - Wire.write((IT_1 << 2) | 0x02); - Wire.endTransmission(); - sleep(500); - HDC.begin(HDC100X_TEMP_HUMI, HDC100X_14BIT, HDC100X_14BIT, DISABLE); - TSL.begin(); - BMP.begin(); - BMP.setOversampling(4); - Serial.println("done!"); - Serial.println("Starting loop."); - HDC.getTemp(); -} - -void loop() { - addValue(HDC.getTemp()); - sleep(200); - addValue(HDC.getHumi()); - result = BMP.startMeasurment(); - if (result != 0) { - sleep(result); - result = BMP.getTemperatureAndPressure(tempBaro, pressure); - } - addValue(pressure); - addValue(TSL.readLux()); - addValue(getUV()); - - submitValues(); - - sleep(60000); -} - diff --git a/files/template_home_wifi_feinstaub/template_home_wifi_feinstaub.ino b/files/template_home_wifi_feinstaub/template_home_wifi_feinstaub.ino deleted file mode 100644 index a0cbbb02..00000000 --- a/files/template_home_wifi_feinstaub/template_home_wifi_feinstaub.ino +++ /dev/null @@ -1,269 +0,0 @@ -/* - senseBox Home - Citizen Sensingplatform - WiFi Version: 1.3 - Date: 2017-05-31 - Homepage: https://www.sensebox.de https://www.opensensemap.org - Author: Institute for Geoinformatics, University of Muenster - Note: Sketch for senseBox:home WiFi with dust particle upgrade - Email: support@sensebox.de - Code is in the public domain. -*/ - -#include -#include "BMP280.h" -#include -#include -#include -#include -#include -#include - -//Custom WiFi Parameters -const char ssid[] = ""; // your network SSID (name) -const char pass[] = ""; // your network password - -bool debug = 0; - -//Network settings -@-- tmpl postDomain // const char *server = ""; -uint8_t status = WL_IDLE_STATUS; -WiFiClient client; -SDS011 my_sds(Serial); - -//Sensor Instances -Makerblog_TSL45315 TSL = Makerblog_TSL45315(TSL45315_TIME_M4); -HDC100X HDC(0x43); -BMP280 BMP; - -//measurement variables -#define UV_ADDR 0x38 -#define IT_1 0x1 -double tempBaro, pressure; -char result; -float pm10,pm25; -int error; - -const unsigned int postingInterval = 60000; - -typedef struct sensor { - const uint8_t ID[12]; -} sensor; - -uint8_t sensorsIndex = 0; - -@-- tmpl ctSensors - -@-- tmpl IDs -uint8_t contentLength = 0; - -float values[NUM_SENSORS]; - -void addValue(const float &value) { - values[sensorsIndex] = value; - sensorsIndex = sensorsIndex + 1; -} - -uint16_t getUV() { - byte msb = 0, lsb = 0; - uint16_t uvValue; - Wire.requestFrom(UV_ADDR + 1, 1); //MSB - sleep(1); - if (Wire.available()) msb = Wire.read(); - Wire.requestFrom(UV_ADDR + 0, 1); //LSB - sleep(1); - if (Wire.available()) lsb = Wire.read(); - uvValue = (msb << 8) | lsb; - return uvValue * 5.625; -} - -int printHexToStream(const uint8_t *data, uint8_t length, Print &stream) // prints 8-bit data in hex -{ - byte first; - int j = 0; - for (uint8_t i=0; i> 4) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - - first = (data[i] & 0x0F) | 48; - if (first > 57) { - stream.write(first + (byte)39); - } else { - stream.write(first); - } - j++; - } - return j; -} - -int printCsvToStream(Print &stream) { - int len = 0; - for (uint8_t i = 0; i < sensorsIndex; i++) { - if (!isnan(values[i])) { - len = len + printHexToStream(sensors[i].ID, 12, stream); - len = len + stream.print(","); - //do not print digits for illuminance und uv-intensity - if (i < 3 || i > 4) len = len + stream.println(values[i],1); - else len = len + stream.println(values[i],0); - } - } - return len; -} - - -// millis() rollover fix - http://arduino.stackexchange.com/questions/12587/how-can-i-handle-the-millis-rollover -void sleep(unsigned long ms) { // ms: duration - unsigned long start = millis(); // start: timestamp - for (;;) { - unsigned long now = millis(); // now: timestamp - unsigned long elapsed = now - start; // elapsed: duration - if (elapsed >= ms) // comparing durations: OK - return; - } -} - -void waitForResponse() -{ - // if there are incoming bytes from the server, read and print them - sleep(100); - String response = ""; - char c; - boolean repeat = true; - do { - if (client.available()) c = client.read(); - else repeat = false; - response += c; - if (response == "HTTP/1.1 ") response = ""; - if (c == '\n') repeat = false; - } - while (repeat); - - if (debug) { - Serial.print("Server Response: "); - Serial.print(response); - } - - client.flush(); - client.stop(); -} - -void submitValues() { - // close any connection before send a new request. - // This will free the socket on the WiFi shield - if (debug) Serial.println("__________________________\n"); - if (client.connected()) { - client.stop(); - sleep(1000); - } - // if there's a successful connection: - if (client.connect(server, 80)) { - - if (debug) Serial.println("connecting..."); - // send the HTTP POST request: - - client.print(F("POST /boxes/")); - printHexToStream(SENSEBOX_ID, 12, client); - client.println(F("/data HTTP/1.1")); - - // !!!!! NICHT LÖSCHEN !!!!! - // print once to Serial to get the content-length - int contentLen = printCsvToStream(Serial); - // !!!!! DO NOT REMOVE !!!!! - - // Send the required header parameters - client.print(F("Host: ")); - client.println(server); - client.print(F("Content-Type: text/csv\nConnection: close\nContent-Length: ")); - client.println(contentLen); - client.println(); - printCsvToStream(client); - client.println(); - if (debug) Serial.println("done!"); - - waitForResponse(); - - // reset index - sensorsIndex = 0; - - } - else { - // if you couldn't make a connection: - if (debug) Serial.println("connection failed. Restarting System."); - sleep(5000); - asm volatile (" jmp 0"); - } -} - -void setup() { - //Initialize serial and wait for port to open: - Serial.begin(9600); - //Enable Wifi Shield - pinMode(4, INPUT); - digitalWrite(4, HIGH); - sleep(2000); - //Check WiFi Shield status - if (WiFi.status() == WL_NO_SHIELD) { - if (debug) Serial.println("WiFi shield not present"); - // don't continue: - while (true); - } - // attempt to connect to Wifi network: - while ( status != WL_CONNECTED) { - if (debug) Serial.print("Attempting to connect to SSID: "); - if (debug) Serial.println(ssid); - // Connect to WPA/WPA2 network. Change this line if using open or WEP network - status = WiFi.begin(ssid, pass); - // wait 60 seconds for connection: - if (debug) { - Serial.println(); - Serial.print("Waiting 10 seconds for connection..."); - } - sleep(10000); - if (debug) Serial.println("done."); - } - if (debug) Serial.print("Initializing sensors..."); - Wire.begin(); - Wire.beginTransmission(UV_ADDR); - Wire.write((IT_1 << 2) | 0x02); - Wire.endTransmission(); - sleep(500); - HDC.begin(HDC100X_TEMP_HUMI, HDC100X_14BIT, HDC100X_14BIT, DISABLE); - TSL.begin(); - BMP.begin(); - BMP.setOversampling(4); - if (debug) { - Serial.println("done!"); - Serial.println("Starting loop in 30 seconds."); - } - HDC.getTemp(); - sleep(30000); -} - -void loop() { - addValue(HDC.getTemp()); - sleep(200); - addValue(HDC.getHumi()); - result = BMP.startMeasurment(); - if (result != 0) { - sleep(result); - result = BMP.getTemperatureAndPressure(tempBaro, pressure); - }else pressure = 0; - addValue(pressure); - addValue(TSL.readLux()); - addValue(getUV()); - error = my_sds.read(&pm25,&pm10); - if (error) { - pm25 = 0; - pm10 = 0; - } - addValue(pm10); - addValue(pm25); - - submitValues(); - - sleep(postingInterval); -} diff --git a/lib/controllers/boxesController.js b/lib/controllers/boxesController.js index 34393df9..bb6a7a28 100644 --- a/lib/controllers/boxesController.js +++ b/lib/controllers/boxesController.js @@ -8,7 +8,6 @@ const restify = require('restify'), jsonstringify = require('stringify-stream'), { config, Honeybadger } = require('../utils'), decodeHandlers = require('../decoding'), - sketches = require('../sketches'), { retrieveParameter, retrieveParameters, @@ -174,7 +173,6 @@ const updateBox = function updateBox (req, res, next) { box.save() .then(function (savedBox) { - sketches.generateSketch(savedBox); res.send(200, { code: 'Ok', data: savedBox.toJSON({ includeSecrets: true }) }); clearCache(['findAllBoxes']); @@ -891,15 +889,10 @@ const postNewBox = function postNewBox (req, res, next) { * @apiUse BoxIdParam */ const getScript = function getScript (req, res, next) { + res.header('Content-Type', 'text/plain; charset=utf-8'); Box.findById(req._userParams.boxId) .then(function (box) { - const file = `${config.targetFolder}${box._id}.ino`; - - if (!fs.existsSync(file)) { - sketches.generateSketch(box); - } - - return res.send(200, fs.readFileSync(file, 'utf-8')); + return res.send(200, box.getSketch()); }) .catch(function (err) { Honeybadger.notify(err); diff --git a/lib/mails.js b/lib/mails.js index 82325da6..d8849862 100644 --- a/lib/mails.js +++ b/lib/mails.js @@ -14,22 +14,18 @@ module.exports = { if (config.mailer_url && config.mailer_url.trim() !== '') { /* eslint-disable global-require */ - const fs = require('fs'), - request = require('request-promise-native'); + const request = require('request-promise-native'); /* eslint-enable global-require */ const mailTemplates = { 'newBox' (user, box) { - // read script before sending.. - const script = fs.readFileSync(`${config.targetFolder}${box._id}.ino`, 'utf-8'); - return { payload: { box }, attachment: { filename: 'senseBox.ino', - contents: new Buffer(script).toString('base64') + contents: box.getSketch({ encoding: 'base64' }) } }; }, @@ -93,16 +89,13 @@ if (config.mailer_url && config.mailer_url.trim() !== '') { }; }, 'newSketch' (user, box) { - // read script before sending.. - const script = fs.readFileSync(`${config.targetFolder}${box._id}.ino`, 'utf-8'); - return { payload: { box }, attachment: { filename: 'senseBox.ino', - contents: new Buffer(script).toString('base64') + contents: box.getSketch({ encoding: 'base64' }) } }; }, diff --git a/lib/models/box.js b/lib/models/box.js index ff0b0047..420a9d70 100644 --- a/lib/models/box.js +++ b/lib/models/box.js @@ -33,10 +33,13 @@ const { mongoose } = require('../db'), sensorLayouts = require('../sensorLayouts'), mqttClient = require('../mqtt'), Measurement = require('./measurement').model, - { parseTimestamp } = require('../utils'), + { parseTimestamp, config: { api_measurements_post_domain } } = require('../utils'), transform = require('stream-transform'), log = require('../log'), - ModelError = require('./modelError'); + ModelError = require('./modelError'), + Sketcher = require('@sensebox/sketch-templater'); + +const templateSketcher = new Sketcher(api_measurements_post_domain); //Location schema const locationSchema = new Schema({ @@ -476,7 +479,10 @@ boxSchema.methods.addAddon = function addAddon (addon) { this.set('model', `${oldModel}${addonNameInModel}`); } } +}; +boxSchema.methods.getSketch = function getSketch ({ encoding } = {}) { + return templateSketcher.generateSketch(this, { encoding }); }; // add integrations Schema as box.integrations & register hooks diff --git a/lib/models/user.js b/lib/models/user.js index 2917f3ae..e232da77 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -41,7 +41,6 @@ const { mongoose } = require('../db'), jwt = require('jsonwebtoken'), Box = require('./box').model, mails = require('../mails'), - sketches = require('../sketches'), moment = require('moment'), hashJWT = require('../helpers/jwtRefreshTokenHasher'), tokenBlacklist = require('../helpers/tokenBlacklist'), @@ -364,7 +363,6 @@ userSchema.methods.addBox = function addBox (params) { mailTemplate = 'newBoxLuftdaten'; } - sketches.generateSketch(savedBox); postToSlack(`Eine neue Modell "${savedBox.model}" wurde registriert (${savedBox.name})`); user.mail(mailTemplate, savedBox); diff --git a/lib/sketches/index.js b/lib/sketches/index.js deleted file mode 100644 index 4f0c4196..00000000 --- a/lib/sketches/index.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -const fs = require('fs'), - { config, Honeybadger } = require('../utils'), - processSketchGeneric = require('./processGeneric'), - processSketchWifi = require('./processWifi'), - log = require('../log'); - -module.exports = { - // generate Arduino sketch - generateSketch (box) { - const output = `${config.targetFolder}${box._id}.ino`; - // remove old script it it exists - try { - if (fs.statSync(output)) { - fs.unlinkSync(output); - log.debug(`deleted old sketch. (${output}) bye bye!`); - } - } catch (e) { - // don't notify honeybadger on ENOENT. The file isn't there if the script is first generated.. - if (e.code !== 'ENOENT') { - Honeybadger.notify(e); - } - } - - let filename, - sketchProcessor = processSketchGeneric; - switch (box.model) { - case 'homeEthernet': - filename = 'files/template_home/template_home.ino'; - sketchProcessor = processSketchWifi; - break; - case 'basicEthernet': - filename = 'files/template_basic/template_basic.ino'; - break; - case 'homeWifi': - filename = 'files/template_home_wifi/template_home_wifi.ino'; - sketchProcessor = processSketchWifi; - break; - case 'homeEthernetFeinstaub': - filename = 'files/template_home_feinstaub/template_home_feinstaub.ino'; - sketchProcessor = processSketchWifi; - break; - case 'homeWifiFeinstaub': - filename = 'files/template_home_wifi_feinstaub/template_home_wifi_feinstaub.ino'; - sketchProcessor = processSketchWifi; - break; - default: - box._isCustom = true; - filename = 'files/template_custom_setup/template_custom_setup.ino'; - break; - } - - sketchProcessor( - fs.readFileSync(filename) - .toString() - .split('\n'), - output, - box - ); - } -}; diff --git a/lib/sketches/processGeneric.js b/lib/sketches/processGeneric.js deleted file mode 100644 index 366195c7..00000000 --- a/lib/sketches/processGeneric.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -const fs = require('fs'), - cfg = require('../utils').config; - -const appendSensors = function appendSensors (output, box) { - let customSensorindex = 1; - for (let i = box.sensors.length - 1; i >= 0; i--) { - const sensor = box.sensors[i]; - if (!box._isCustom && sensor.title === 'Temperatur') { - fs.appendFileSync(output, `#define TEMPSENSOR_ID "${sensor._id}"\n`); - } else if (!box._isCustom && sensor.title === 'rel. Luftfeuchte') { - fs.appendFileSync(output, `#define HUMISENSOR_ID "${sensor._id}"\n`); - } else if (!box._isCustom && sensor.title === 'Luftdruck') { - fs.appendFileSync(output, `#define PRESSURESENSOR_ID "${sensor._id}"\n`); - } else if (!box._isCustom && sensor.title === 'Lautstärke') { - fs.appendFileSync(output, `#define NOISESENSOR_ID "${sensor._id}"\n`); - } else if (!box._isCustom && sensor.title === 'Helligkeit') { - fs.appendFileSync(output, `#define LIGHTSENSOR_ID "${sensor._id}"\n`); - } else if (!box._isCustom && sensor.title === 'Beleuchtungsstärke') { - fs.appendFileSync(output, `#define LUXSENSOR_ID "${sensor._id}"\n`); - } else if (!box._isCustom && sensor.title === 'UV-Intensität') { - fs.appendFileSync(output, `#define UVSENSOR_ID "${sensor._id}"\n`); - } else { - fs.appendFileSync(output, `#define SENSOR${customSensorindex}_ID "${sensor._id}" // ${sensor.title}\n`); - customSensorindex++; - } - } -}; - -module.exports = function processSketchGeneric (templateLines, output, box) { - for (const line of templateLines) { - if (line.indexOf('//senseBox ID') !== -1) { - fs.appendFileSync(output, `${line.toString()}\n`); - fs.appendFileSync(output, `#define SENSEBOX_ID "${box._id}"\n`); - } else if (line.indexOf('//Sensor IDs') !== -1) { - fs.appendFileSync(output, `${line.toString()}\n`); - appendSensors(output, box); - } else if (line.indexOf('@@OSEM_POST_DOMAIN@@') !== -1) { - const newLine = line.toString().replace('@@OSEM_POST_DOMAIN@@', cfg.api_measurements_post_domain); - fs.appendFileSync(output, `${newLine}\n`); - } else { - fs.appendFileSync(output, `${line.toString()}\n`); - } - } -}; diff --git a/lib/sketches/processWifi.js b/lib/sketches/processWifi.js deleted file mode 100644 index ac7fc103..00000000 --- a/lib/sketches/processWifi.js +++ /dev/null @@ -1,90 +0,0 @@ -'use strict'; - -const fs = require('fs'), - cfg = require('../utils').config; - -const substitutions = { - 'ctSensors' (box) { - return `// Number of sensors\nstatic const uint8_t NUM_SENSORS = ${box.sensors.length};\n`; - }, - 'postDomain' () { - return `const char *server = "${cfg.api_measurements_post_domain}";\n`; - }, - 'IDs' (box) { - // sensors should follow this order strictly - // when using the standard senseBox:home wifi setup - // 0 Temperature - // 1 Humidity - // 2 Pressure - // 3 Lux - // 4 UV - // the rest - - - const homeSensors = [], - otherSensors = []; - for (const sensor of box.sensors) { - switch (sensor.title) { - case 'Temperatur': - homeSensors[0] = `// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`; - break; - case 'rel. Luftfeuchte': - homeSensors[1] = `// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`; - break; - case 'Luftdruck': - homeSensors[2] = `// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`; - break; - case 'Beleuchtungsstärke': - homeSensors[3] = `// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`; - break; - case 'UV-Intensität': - homeSensors[4] = `// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`; - break; - case 'PM10': - homeSensors[5] = `// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`; - break; - case 'PM2.5': - homeSensors[6] = `// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`; - break; - - default: - otherSensors.push(`// ${sensor.title}\n{ ${idToHex(sensor._id.toString())} }`); - break; - } - } - const allSensors = homeSensors.concat(otherSensors); - - return `// senseBox ID and sensor IDs -const uint8_t SENSEBOX_ID[12] = {${idToHex(box._id.toString())}}; - -// Do not change order of sensor IDs -const sensor sensors[NUM_SENSORS] = { -${allSensors.join(',\n')} -}; -`; - } -}; - -const idToHex = function idToHex (idStr) { - return idStr.replace(/(..)/g, ', 0x$1').substr(2); -}; - - -const createSubstitution = function createSubstitution (line, box) { - const substitutionKey = line.split(' ')[2]; - if (substitutions[substitutionKey]) { - return substitutions[substitutionKey](box); - } - - return '\n'; -}; - -module.exports = function processSketchWifi (templateLines, outputFilename, box) { - for (const line of templateLines) { - if (line.startsWith('@-- tmpl')) { - fs.appendFileSync(outputFilename, createSubstitution(line, box)); - } else { - fs.appendFileSync(outputFilename, `${line.toString()}\n`); - } - } -}; diff --git a/package.json b/package.json index 96180ae0..934e9ccc 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "API for opensensemap.org", "main": "index.js", "dependencies": { + "@sensebox/sketch-templater": "^1.0.2", "@turf/area": "^4.4.0", "@turf/bbox": "^4.4.0", "@turf/centroid": "^4.4.0", diff --git a/tests-docker-compose.yml b/tests-docker-compose.yml index e72429cd..ff1aba7f 100644 --- a/tests-docker-compose.yml +++ b/tests-docker-compose.yml @@ -13,7 +13,6 @@ services: env_file: .env environment: OSEM_dbhost: db - OSEM_targetFolder: ./usersketches OSEM_imageFolder: ./userimages OSEM_mailer_url: https://mailer:3924/ OSEM_mailer_origin: http://osem-api:8000 diff --git a/yarn.lock b/yarn.lock index 7bd918a0..ea786724 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,6 +8,12 @@ dependencies: wgs84 "0.0.0" +"@sensebox/sketch-templater@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@sensebox/sketch-templater/-/sketch-templater-1.0.2.tgz#fce6398f39623429a8fd3b915eb3cf8760bab96d" + dependencies: + dedent "^0.7.0" + "@turf/area@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@turf/area/-/area-4.4.0.tgz#b10ab02c7bf955abb30adefef6585b4f2f4d36d6" @@ -523,6 +529,10 @@ debug@~2.2.0: dependencies: ms "0.7.1" +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + deep-eql@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" From 5d0226ffd77b668af680252ae6ce085bbf77a38f Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Mon, 24 Jul 2017 19:48:29 +0200 Subject: [PATCH 03/12] move updateBox logic to model --- lib/controllers/boxesController.js | 138 +++++--------------------- lib/models/box.js | 152 ++++++++++++++++++++++++++++- lib/models/sensor.js | 5 + lib/requestUtils.js | 25 +++-- tests/tests.js | 5 +- 5 files changed, 195 insertions(+), 130 deletions(-) diff --git a/lib/controllers/boxesController.js b/lib/controllers/boxesController.js index bb6a7a28..6b57f1e7 100644 --- a/lib/controllers/boxesController.js +++ b/lib/controllers/boxesController.js @@ -1,17 +1,15 @@ 'use strict'; const restify = require('restify'), - fs = require('fs'), - { Measurement, Box, Sensor } = require('../models'), + { Measurement, Box } = require('../models'), csvstringify = require('csv-stringify'), streamTransform = require('stream-transform'), jsonstringify = require('stringify-stream'), - { config, Honeybadger } = require('../utils'), + { Honeybadger } = require('../utils'), decodeHandlers = require('../decoding'), { retrieveParameter, retrieveParameters, - decodeBase64Image, clearCache, addCache, parseAndValidateTimeParamsForFindAllBoxes, @@ -86,121 +84,35 @@ const restify = require('restify'), * */ const updateBox = function updateBox (req, res, next) { - const deleteMeasurementsOf = []; - const { mqtt, ttn, loc, image, sensors, boxId, addons } = req._userParams; - let shouldSendSketch = false; - - if (sensors && addons) { - return next(new restify.BadRequestError('sensors and addons can not appear in the same request.')); - } - - Box.findById(boxId).then(function (box) { - // easy string properties, not nullable - for (const prop of ['name', 'exposure', 'grouptag', 'description', 'weblink']) { - if (typeof req._userParams[prop] !== 'undefined' && req._userParams[prop] !== '') { - box.set(prop, req._userParams[prop]); - } - } - - // these properties can be set to undefined by submitting '' (empty string) - for (const prop of ['grouptag', 'description', 'weblink']) { - if (typeof req._userParams[prop] !== 'undefined' && req._userParams[prop] === '') { - box.set(prop, undefined); - } - } - // integrations - if (mqtt) { - const { enabled, url, topic, decodeOptions, connectionOptions, messageFormat } = mqtt; - box.set('integrations.mqtt', { enabled, url, topic, decodeOptions, connectionOptions, messageFormat }); - } - if (ttn) { - const { app_id, dev_id, port, profile, decodeOptions } = ttn; - box.set('integrations.ttn', { app_id, dev_id, port, profile, decodeOptions }); - } - - if (loc) { - let { lng, lat } = loc; - lng = parseFloat(lng); - lat = parseFloat(lat); - if (!isNaN(lng) && !isNaN(lat) && (box.loc[0].geometry.coordinates[0] !== lng || box.loc[0].geometry.coordinates[1] !== lat)) { - box.set('loc.0.geometry.coordinates', [lng, lat]); + Box.findBoxByIdAndUpdate(req._userParams.boxId, req._userParams) + .then(function (updatedBox) { + if (updatedBox._sensorsChanged === true) { + req.user.mail('newSketch', updatedBox); } - } - - if (image) { - const imageBuffer = decodeBase64Image(image); - const extension = (imageBuffer.type === 'image/jpeg') ? '.jpg' : '.png'; - try { - fs.writeFileSync(`${config.imageFolder}${req._userParams.boxId}${extension}`, imageBuffer.data); - box.set('image', `${req._userParams.boxId + extension}?${new Date().getTime()}`); - } catch (e) { - return next(new restify.InternalServerError(JSON.stringify(e.message))); - } - } - if (sensors) { - for (const updatedsensor of sensors) { - const sensorIndex = box.sensors.findIndex(s => s._id.equals(updatedsensor._id)); - if (sensorIndex !== -1 && updatedsensor.deleted) { - deleteMeasurementsOf.push(updatedsensor._id); - box.sensors.pull({ _id: updatedsensor._id }); - shouldSendSketch = true; - } else if (updatedsensor.edited && updatedsensor.new && sensorIndex === -1) { - box.sensors.push(new Sensor({ - title: updatedsensor.title, - unit: updatedsensor.unit, - sensorType: updatedsensor.sensorType, - icon: updatedsensor.icon - })); - shouldSendSketch = true; - } else if (sensorIndex !== -1 && updatedsensor.edited && !updatedsensor.deleted) { - box.sensors.set(sensorIndex, { - _id: updatedsensor._id, - title: updatedsensor.title, - unit: updatedsensor.unit, - sensorType: updatedsensor.sensorType, - icon: updatedsensor.icon - }); - shouldSendSketch = true; + res.send(200, { code: 'Ok', data: updatedBox.toJSON({ includeSecrets: true }) }); + clearCache(['findAllBoxes']); + }) + .catch(function (err) { + if (err.name === 'ValidationError') { + const msgs = []; + for (const field in err.errors) { + if (!err.errors[field].errors) { + msgs.push(err.errors[field].message); + } } - } - } - if (typeof addons === 'object' && addons.add && addons.add) { - box.addAddon(addons.add.trim().toLowerCase()); - shouldSendSketch = true; - } - - box.save() - .then(function (savedBox) { - res.send(200, { code: 'Ok', data: savedBox.toJSON({ includeSecrets: true }) }); - clearCache(['findAllBoxes']); + return next(new restify.UnprocessableEntityError(`validation failed: ${msgs.join(' ')}`)); + } - if (deleteMeasurementsOf.length !== 0) { - Measurement.remove({ sensor_id: { $in: deleteMeasurementsOf } }).exec(); + if (err.name === 'ModelError') { + if (err.data && err.data.type) { + return next(new restify[err.data.type](err.message)); } - if (shouldSendSketch === true) { - return req.user.mail('newSketch', savedBox); - } - }) - .catch(function (err) { - if (err.name === 'ValidationError') { - const msgs = []; - for (const field in err.errors) { - if (!err.errors[field].errors) { - msgs.push(err.errors[field].message); - } - } - - return next(new restify.UnprocessableEntityError(`validation failed: ${msgs.join(' ')}`)); - } - Honeybadger.notify(err); + return next(new restify.BadRequestError(err.message)); + } - return next(new restify.InternalServerError(JSON.stringify(err.message))); - }); - }) - .catch(function (err) { Honeybadger.notify(err); return next(new restify.InternalServerError(JSON.stringify(err.message))); @@ -867,7 +779,7 @@ const postNewBox = function postNewBox (req, res, next) { if (err.errors) { const msg = Object.keys(err.errors) - .map(f => `Parameter ${f} ${err.errors[f].message}`) + .map(f => `${err.errors[f].message}`) .join(', '); return next(new restify.UnprocessableEntityError(msg)); @@ -973,9 +885,9 @@ module.exports = { { name: 'name' }, { name: 'grouptag', dataType: 'StringWithEmpty' }, { name: 'description', dataType: 'StringWithEmpty' }, - { name: 'image' }, { name: 'weblink', dataType: 'StringWithEmpty' }, { name: 'exposure', allowedValues: ['indoor', 'outdoor'] }, + { name: 'image', dataType: 'base64Image' }, { name: 'mqtt', dataType: 'object' }, { name: 'ttn', dataType: 'object' }, { name: 'loc', dataType: 'object' }, diff --git a/lib/models/box.js b/lib/models/box.js index 420a9d70..3fb29a76 100644 --- a/lib/models/box.js +++ b/lib/models/box.js @@ -28,16 +28,17 @@ const { mongoose } = require('../db'), timestamp = require('mongoose-timestamp'), Schema = mongoose.Schema, - sensorSchema = require('./sensor').schema, + { schema: sensorSchema, model: Sensor } = require('./sensor'), integrations = require('./integrations'), sensorLayouts = require('../sensorLayouts'), mqttClient = require('../mqtt'), Measurement = require('./measurement').model, - { parseTimestamp, config: { api_measurements_post_domain } } = require('../utils'), + { parseTimestamp, config: { api_measurements_post_domain, imageFolder } } = require('../utils'), transform = require('stream-transform'), log = require('../log'), ModelError = require('./modelError'), - Sketcher = require('@sensebox/sketch-templater'); + Sketcher = require('@sensebox/sketch-templater'), + fs = require('fs'); const templateSketcher = new Sketcher(api_measurements_post_domain); @@ -87,39 +88,62 @@ const boxSchema = new Schema({ }, boxType: { type: String, + trim: true, required: true, enum: ['fixed'] }, exposure: { type: String, + trim: true, required: true, enum: ['unknown', 'indoor', 'outdoor'] }, grouptag: { type: String, + trim: true, required: false }, model: { type: String, required: true, + trim: true, default: 'custom', enum: ['custom', ...sensorLayouts.models] }, weblink: { type: String, + trim: true, required: false }, description: { type: String, + trim: true, required: false }, image: { type: String, - required: false + trim: true, + required: false, + /* eslint-disable func-name-matching */ + set: function imageSetter ({ type, data }) { + /* eslint-enable func-name-matching */ + if (type && data) { + const filename = `${this._id}_${Math.round(Date.now() / 1000).toString(36)}.${type}`; + try { + fs.writeFileSync(`${imageFolder}${filename}`, data); + } catch (err) { + log.warn(err); + + return; + } + + return filename; + } + } }, sensors: { type: [sensorSchema], - required: [true, 'is required if model is invalid or missing.'], + required: [true, 'sensors are required if model is invalid or missing.'], } }); boxSchema.plugin(timestamp); @@ -165,6 +189,38 @@ boxSchema.set('toJSON', { } }); +boxSchema.pre('save', function boxPreSave (next) { + // check if sensors have been changed + if (this.modifiedPaths && typeof this.modifiedPaths === 'function') { + this._sensorsChanged = this.modifiedPaths().some(function eachPath (path) { + return path.includes('sensors'); + }); + } + + // if sensors have been changed + if (this._sensorsChanged === true) { + // find out if sensors are marked for deletion + this._deleteMeasurementsOf = []; + for (const sensor of this.sensors) { + if (sensor._deleteMe === true) { + this._deleteMeasurementsOf.push(sensor._id); + this.sensors.pull({ _id: sensor._id }); + } + } + } + next(); +}); + +boxSchema.post('save', function boxPostSave (savedBox) { + // only run if sensors have changed.. + if (this._sensorsChanged === true) { + // delete measurements of deleted sensors + if (savedBox._deleteMeasurementsOf && savedBox._deleteMeasurementsOf.length !== 0) { + Measurement.remove({ sensor_id: { $in: savedBox._deleteMeasurementsOf } }).exec(); + } + } +}); + // initializes and saves new box document boxSchema.statics.initNew = function ({ name, @@ -454,6 +510,7 @@ boxSchema.statics.findMeasurementsOfBoxesStream = function (opts) { // try to add sensors defined in addons to the box. If the sensors already exist, // nothing is done. boxSchema.methods.addAddon = function addAddon (addon) { + addon = addon.trim().toLowerCase(); const addonSensors = sensorLayouts.getSensorsForAddon(addon); if (!addonSensors) { @@ -481,10 +538,95 @@ boxSchema.methods.addAddon = function addAddon (addon) { } }; +boxSchema.methods.addSensor = function addSensor ({ title, unit, sensorType, icon }) { + this.sensors.push(new Sensor({ title, unit, sensorType, icon })); +}; + +boxSchema.methods.updateImage = function updateImage ({ type, data }) { + if (type && data) { + const extension = (type === 'image/jpeg') ? '.jpg' : '.png'; + fs.writeFileSync(`${imageFolder}${this._id}${extension}`, data); + this.set('image', `${this._id}${extension}?${new Date().getTime()}`); + } +}; + boxSchema.methods.getSketch = function getSketch ({ encoding } = {}) { return templateSketcher.generateSketch(this, { encoding }); }; +boxSchema.statics.findBoxByIdAndUpdate = function findBoxByIdAndUpdate (boxId, args) { + const { + mqtt: { + enabled, + url, + topic, + decodeOptions: mqttDecodeOptions, + connectionOptions, + messageFormat + } = {}, + ttn: { + app_id, + dev_id, + port, + profile, + decodeOptions: ttnDecodeOptions + } = {}, + loc = {}, + sensors, + addons: { add: addonToAdd } = {} + } = args; + + + if (sensors && addonToAdd) { + return Promise.reject(new ModelError('sensors and addons can not appear in the same request.')); + } + + let { lng, lat } = loc; + if (lng && lat) { + lng = parseFloat(lng); + lat = parseFloat(lat); + + if (isNaN(lng) || isNaN(lat)) { + return Promise.reject(new ModelError('Location invalid.')); + } + args['loc.0.geometry.coordinates'] = [lng, lat]; + } + + if (args.mqtt) { + args['integrations.mqtt'] = { enabled, url, topic, decodeOptions: mqttDecodeOptions, connectionOptions, messageFormat }; + } + if (args.ttn) { + args['integrations.ttn'] = { app_id, dev_id, port, profile, decodeOptions: ttnDecodeOptions }; + } + + return this.findById(boxId) + .then(function (box) { + // only grouptag, description and weblink can removed through setting them to empty string ('') + for (const prop of ['name', 'exposure', 'grouptag', 'description', 'weblink', 'image', 'loc.0.geometry.coordinates', 'integrations.mqtt', 'integrations.ttn']) { + if (typeof args[prop] !== 'undefined') { + box.set(prop, (args[prop] === '' ? undefined : args[prop])); + } + } + + if (sensors) { + for (const { _id, title, unit, sensorType, icon, deleted, edited, new: isNew } of sensors) { + const sensorIndex = box.sensors.findIndex(s => s._id.equals(_id)); + if (sensorIndex !== -1 && deleted) { + box.sensors[sensorIndex].markForDeletion(); + } else if (edited && isNew && sensorIndex === -1) { + box.addSensor({ _id, title, unit, sensorType, icon }); + } else if (sensorIndex !== -1 && edited && !deleted) { + box.sensors.set(sensorIndex, { _id, title, unit, sensorType, icon }); + } + } + } else if (addonToAdd) { + box.addAddon(addonToAdd); + } + + return box.save(); + }); +}; + // add integrations Schema as box.integrations & register hooks integrations.addToSchema(boxSchema); diff --git a/lib/models/sensor.js b/lib/models/sensor.js index c0c40313..2ad88159 100644 --- a/lib/models/sensor.js +++ b/lib/models/sensor.js @@ -60,6 +60,11 @@ sensorSchema.methods.equals = function equals ({ unit, sensorType, title, _id }) return unit === this.unit && sensorType === this.sensorType && title === this.title; }; +sensorSchema.methods.markForDeletion = function markForDeletion () { + this.markModified('deleteMe'); // markModified to be picked up by box pre save hook modifiedPaths + this._deleteMe = true; // gets checked +}; + const sensorModel = mongoose.model('Sensor', sensorSchema); module.exports = { diff --git a/lib/requestUtils.js b/lib/requestUtils.js index 888ab746..1652d071 100644 --- a/lib/requestUtils.js +++ b/lib/requestUtils.js @@ -24,17 +24,21 @@ const checkContentType = function (req, res, next) { }; const decodeBase64Image = function (dataString) { - const matches = dataString.match(/^data:([A-Za-z-+/]+);base64,(.+)$/), - response = {}; - - if (matches.length !== 3) { - return new Error('Invalid input string'); + const matches = dataString.match(/^data:(?:image\/(jpeg|png|gif));base64,(.+)$/m); + + if (matches) { + /* eslint-disable no-unused-vars */ + const [ _, type, data ] = matches; + /* eslint-enable no-unused-vars */ + if (type && data) { + return { + type: (type === 'jpeg' ? 'jpg' : type), + data: Buffer.from(data, 'base64') + }; + } } - response.type = matches[1]; - response.data = new Buffer(matches[2], 'base64'); - - return response; + return false; }; const getParam = function getParam (req, paramNames, paramDataType) { @@ -106,6 +110,9 @@ const castParam = function castParam (param, paramDataType, dataTypeIsArray) { parser = obj => obj; check = obj => Boolean(!obj) || typeof obj !== 'object'; break; + case 'base64Image': + parser = decodeBase64Image; + check = img => Boolean(!img); } return execCastParam(param, parser, check, failWhenCheckIs); diff --git a/tests/tests.js b/tests/tests.js index 5327b39d..31adcb11 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -782,7 +782,7 @@ describe('openSenseMap API', function () { .then(function (response) { expect(response).to.have.status(422); expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response).json({ code: 'UnprocessableEntityError', message: 'Parameter sensors is required if model is invalid or missing.' }); + expect(response).json({ code: 'UnprocessableEntityError', message: 'sensors are required if model is invalid or missing.' }); return chakram.wait(); }); @@ -793,7 +793,6 @@ describe('openSenseMap API', function () { return chakram.post(`${BASE_URL}/boxes`, box, { headers: { 'Authorization': `Bearer ${jwt2}` } }) .then(function (response) { - console.log(response.body); expect(response).to.have.status(422); expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); expect(response).json({ code: 'UnprocessableEntityError', message: 'Parameters model and sensors cannot be specified at the same time.' }); @@ -1343,7 +1342,7 @@ describe('openSenseMap API', function () { expect(response).to.comprise.of.json('data.loc', [ { type: 'Feature', geometry: { type: 'Point', coordinates: [ update_payload.loc.lng, update_payload.loc.lat ] } }]); expect(response).to.comprise.of.json('data.image', function (image) { - return expect(moment().diff(moment(parseInt(image.split('?')[1], 10)))).to.be.below(50); + return expect(moment().diff(moment(parseInt(image.split('_')[1].slice(0, -4), 36) * 1000))).to.be.below(50); }); return chakram.wait(); From 20795852f219981538489602b0a5fab064e69e05 Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Mon, 24 Jul 2017 20:08:54 +0200 Subject: [PATCH 04/12] fix typo, do not export decodeBase64Image --- lib/requestUtils.js | 1 - lib/utils.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/requestUtils.js b/lib/requestUtils.js index 1652d071..65dff54b 100644 --- a/lib/requestUtils.js +++ b/lib/requestUtils.js @@ -570,7 +570,6 @@ const clearCache = function clearCache (identifiers) { }; module.exports = { - decodeBase64Image, checkContentType, validateIdParams, preRequest, diff --git a/lib/utils.js b/lib/utils.js index 147a47ea..63c39c1c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -66,7 +66,7 @@ const softwareRevision = (function () { return `${parts[0]} ${parts[1]} ${parts[2]}`; } catch (err) { - return 'unkown revision'; + return 'unknown revision'; } })(); From c03e739df54aef8ce15d01c237d4df1fbe96134f Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Mon, 24 Jul 2017 20:19:08 +0200 Subject: [PATCH 05/12] add more slack notifications on new box, new user, delete box and delete user --- lib/models/user.js | 7 +++++-- lib/utils.js | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/models/user.js b/lib/models/user.js index e232da77..dc0c17ec 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -36,7 +36,7 @@ const { mongoose } = require('../db'), Schema = mongoose.Schema, bcrypt = require('bcrypt'), crypto = require('crypto'), - { config: { jwt_algorithm, jwt_secret, jwt_issuer, password_min_length, password_salt_factor, jwt_validity_ms, refresh_token_validity_ms }, Honeybadger, postToSlack } = require('../utils'), + { config: { jwt_algorithm, jwt_secret, jwt_issuer, password_min_length, password_salt_factor, jwt_validity_ms, refresh_token_validity_ms }, Honeybadger, postToSlack, redactEmail } = require('../utils'), uuid = require('uuid'), jwt = require('jsonwebtoken'), Box = require('./box').model, @@ -219,6 +219,7 @@ userSchema.pre('save', function userPreSave (next) { userSchema.post('save', function userPostSaveSendMails (user) { if (user.wasNew) { user.mail('newUser'); + postToSlack(`New User: ${user.name} (${redactEmail(user.email)})`); return; } @@ -363,7 +364,7 @@ userSchema.methods.addBox = function addBox (params) { mailTemplate = 'newBoxLuftdaten'; } - postToSlack(`Eine neue Modell "${savedBox.model}" wurde registriert (${savedBox.name})`); + postToSlack(`New Box: ${user.name} (${redactEmail(user.email)}) just registered "${savedBox.name}" (${savedBox.model}): `); user.mail(mailTemplate, savedBox); return savedBox; @@ -401,6 +402,7 @@ userSchema.methods.removeBox = function removeBox (boxId) { // remove from boxes user.boxes.pull({ _id: boxId }); + postToSlack(`Box deleted: ${user.name} (${redactEmail(user.email)}) just deleted "${box.name}" (${box.model})`); return user.save(); }); @@ -416,6 +418,7 @@ userSchema.methods.destroyUser = function destroyUser (req) { .execPopulate() .then(function (userWithBoxes) { userWithBoxes.mail('deleteUser'); + postToSlack(`User deleted: ${user.name} (${redactEmail(user.email)})`); // delete the boxes.. for (const box of userWithBoxes.boxes) { box.removeSelfAndMeasurements(); diff --git a/lib/utils.js b/lib/utils.js index 63c39c1c..d4305419 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -88,6 +88,20 @@ const isNonEmptyString = function isNonEmptyString (val) { return !(typeof val === 'undefined' || val === ''); }; +const redactEmail = function redactEmail (email) { + /* eslint-disable prefer-const */ + let [ name = '', domain = '' ] = email.split('@'); + /* eslint-enable prefer-const */ + + let [ hostname = '', tld = '' ] = domain.split('.'); + + tld = `${tld.slice(0, 1)}**`; + hostname = `${hostname.slice(0, 3)}****`; + name = `${name.slice(0, 3)}****`; + + return `${name}@${hostname}.${tld}`; +}; + module.exports = { config, Honeybadger, @@ -97,5 +111,6 @@ module.exports = { timeIsValid, utcNowDate, postToSlack, - softwareRevision + softwareRevision, + redactEmail }; From ba4cb6d155e36d752388b62484c1b68d721654f9 Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Tue, 25 Jul 2017 08:51:07 +0200 Subject: [PATCH 06/12] fix #96. Ignore case of email when signing in --- lib/controllers/usersController.js | 8 ++++---- lib/helpers/authHelpers.js | 3 ++- lib/requestUtils.js | 9 ++++++++- package.json | 1 + tests/tests.js | 21 +++++++++++++++++++++ yarn.lock | 10 ++++++++++ 6 files changed, 46 insertions(+), 6 deletions(-) diff --git a/lib/controllers/usersController.js b/lib/controllers/usersController.js index cc1e7046..3dfbcf2b 100644 --- a/lib/controllers/usersController.js +++ b/lib/controllers/usersController.js @@ -302,7 +302,7 @@ module.exports = { registerUser: [ checkContentType, retrieveParameters([ - { name: 'email', required: true }, + { name: 'email', dataType: 'email', required: true }, { name: 'password', required: true }, { name: 'name', required: true, dataType: 'as-is' }, { name: 'language', defaultValue: 'en_US' } @@ -325,7 +325,7 @@ module.exports = { requestResetPassword: [ checkContentType, retrieveParameters([ - { name: 'email', required: true } + { name: 'email', dataType: 'email', required: true } ]), requestResetPassword ], @@ -333,7 +333,7 @@ module.exports = { checkContentType, retrieveParameters([ { name: 'token', required: true }, - { name: 'email', required: true }, + { name: 'email', dataType: 'email', required: true }, ]), confirmEmailAddress ], @@ -342,7 +342,7 @@ module.exports = { updateUser: [ checkContentType, retrieveParameters([ - { name: 'email' }, + { name: 'email', dataType: 'email' }, { name: 'currentPassword', dataType: 'as-is' }, { name: 'newPassword', dataType: 'as-is' }, { name: 'name', dataType: 'as-is' }, diff --git a/lib/helpers/authHelpers.js b/lib/helpers/authHelpers.js index a098542e..366a5e72 100644 --- a/lib/helpers/authHelpers.js +++ b/lib/helpers/authHelpers.js @@ -29,7 +29,8 @@ const jwtOptions = { const MSG_CREDENTIALS_WRONG = 'Your login details could not be verified. Please try again.'; const localLogin = new LocalStrategy(localOptions, function verifiyLocalLogin (emailOrName, password, done) { - User.findOne({ $or: [ { email: emailOrName }, { name: emailOrName } ] }) + // lowercase for email + User.findOne({ $or: [ { email: emailOrName.toLowerCase() }, { name: emailOrName } ] }) .exec() .then(function (user) { if (!user) { diff --git a/lib/requestUtils.js b/lib/requestUtils.js index 65dff54b..d67e56c8 100644 --- a/lib/requestUtils.js +++ b/lib/requestUtils.js @@ -4,7 +4,8 @@ const restify = require('restify'), { config, Honeybadger, parseTimestamp, timeIsValid } = require('./utils'), { mongoose } = require('./db'), moment = require('moment'), - apicache = require('apicache'); + apicache = require('apicache'), + isemail = require('isemail'); /** * @apiDefine ContentTypeJSON @@ -113,6 +114,12 @@ const castParam = function castParam (param, paramDataType, dataTypeIsArray) { case 'base64Image': parser = decodeBase64Image; check = img => Boolean(!img); + break; + case 'email': + parser = e => e.toString().toLowerCase(); + check = isemail.validate; + failWhenCheckIs = false; + break; } return execCastParam(param, parser, check, failWhenCheckIs); diff --git a/package.json b/package.json index 934e9ccc..dbcf205f 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "bunyan": "^1.8.10", "csv-stringify": "^1.0.4", "honeybadger": "^1.2.1", + "isemail": "^3.0.0", "jsonpath": "^0.2.11", "jsonwebtoken": "^7.4.1", "millify": "^2.0.1", diff --git a/tests/tests.js b/tests/tests.js index 31adcb11..ef6c871e 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -358,6 +358,27 @@ describe('openSenseMap API', function () { }); }); + it('should allow to sign in an user with email (different case) and password', function () { + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'TESTER@TEST.TEST', password: 'some secure password' }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + expect(response.body.refreshToken).to.exist; + + return chakram.wait(); + }); + }); + + it('should deny to sign in with name in different case', function () { + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'This Is Just A Nickname', password: 'some secure password' }) + .then(function (response) { + expect(response).to.have.status(401); + + return chakram.wait(); + }); + }); + it('should allow to sign out with jwt', function () { return chakram.post(`${BASE_URL}/users/sign-out`, {}, { headers: { 'Authorization': `Bearer ${jwt}` } }) .then(function (response) { diff --git a/yarn.lock b/yarn.lock index ea786724..38c95b93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1266,6 +1266,12 @@ isemail@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/isemail/-/isemail-1.2.0.tgz#be03df8cc3e29de4d2c5df6501263f1fa4595e9a" +isemail@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.0.0.tgz#c89a46bb7a3361e1759f8028f9082488ecce3dff" + dependencies: + punycode "2.x.x" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -1877,6 +1883,10 @@ pumpify@^1.3.5: inherits "^2.0.1" pump "^1.0.0" +punycode@2.x.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + q@1.x.x: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" From 68c66b9be0841a5dc3e4b814389885c7673d3871 Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Tue, 25 Jul 2017 13:36:20 +0200 Subject: [PATCH 07/12] clean up & split tests, upgrade dev deps --- package.json | 6 +- tests/data/byte_submit_data.js | 8 +- tests/data/csv_example_data.js | 9 +- tests/data/custom_valid_sensebox.js | 2 + tests/data/findAllSchema.js | 2 + tests/data/getUserBoxesSchema.js | 2 + tests/data/getUserSchema.js | 2 + tests/data/json_submit_data.js | 2 - tests/data/luftdaten_example_data.js | 2 + tests/data/randomApiKey.js | 6 - tests/data/senseBoxCreateSchema.js | 35 -- tests/data/senseBoxSchema.js | 2 + tests/data/senseBoxSchemaAllFieldsUsers.js | 118 ----- tests/data/valid_sensebox.js | 3 +- tests/mail_tests.js | 3 +- tests/routes/002-idw-test.js | 3 +- tests/routes/003-users-test.js | 512 +++++++++++++++++++ tests/tests.js | 543 ++------------------- tests/waitForHttp.js | 3 + yarn.lock | 72 ++- 20 files changed, 643 insertions(+), 692 deletions(-) delete mode 100644 tests/data/randomApiKey.js delete mode 100644 tests/data/senseBoxCreateSchema.js delete mode 100644 tests/data/senseBoxSchemaAllFieldsUsers.js create mode 100644 tests/routes/003-users-test.js diff --git a/package.json b/package.json index dbcf205f..104f71a7 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ "devDependencies": { "@turf/invariant": "^4.5.2", "chakram": "^1.5.0", - "cheerio": "^1.0.0-rc.1", - "eslint": "^4.1.1", + "cheerio": "^1.0.0-rc.2", + "eslint": "^4.3.0", "mimelib": "^0.3.1", "mocha": "^3.4.2", "randomgeojson": "^1.0.0" @@ -50,7 +50,7 @@ "start": "node app.js", "test": "node node_modules/.bin/mocha tests/tests.js", "pretest": "node tests/waitForHttp", - "lint": "eslint --fix app.js \"lib/**/*.js\"" + "lint": "./node_modules/.bin/eslint --fix app.js \"{tests,lib}/**/*.js\"" }, "repository": { "type": "git", diff --git a/tests/data/byte_submit_data.js b/tests/data/byte_submit_data.js index f1bd146c..f9813653 100644 --- a/tests/data/byte_submit_data.js +++ b/tests/data/byte_submit_data.js @@ -11,7 +11,13 @@ module.exports = function (sensors, withTimestamps) { buffer.write(sensor._id, i * len, 12, 'hex'); buffer.writeFloatLE(i, (i * len) + 12); if (withTimestamps) { - buffer.writeUInt32LE(moment.utc().subtract(i, 'minute').unix(), (i * len) + 12 + 4); + buffer + .writeUInt32LE( + moment + .utc() + .subtract(i, 'minute') + .unix(), (i * len) + 12 + 4 + ); } } diff --git a/tests/data/csv_example_data.js b/tests/data/csv_example_data.js index c9be1743..513ffed4 100644 --- a/tests/data/csv_example_data.js +++ b/tests/data/csv_example_data.js @@ -12,7 +12,8 @@ const no_timestamps = function (sensors) { const with_timestamps = function (sensors) { const str = sensors.map(function (sensor, index) { - return `${sensor._id},${index},${moment.utc().subtract(index, 'minutes').toISOString()}`; + return `${sensor._id},${index},${moment.utc().subtract(index, 'minutes') + .toISOString()}`; }).join('\n'); return str; @@ -20,7 +21,8 @@ const with_timestamps = function (sensors) { const with_timestamps_future = function (sensors) { const str = sensors.map(function (sensor, index) { - return `${sensor._id},${index},${moment.utc().add(index, 'minutes').toISOString()}`; + return `${sensor._id},${index},${moment.utc().add(index, 'minutes') + .toISOString()}`; }).join('\n'); return str; @@ -28,7 +30,8 @@ const with_timestamps_future = function (sensors) { const with_too_many = function (sensors) { const str = sensors.map(function (sensor, index) { - return `${sensor._id},${index},${moment.utc().add(index, 'minutes').toISOString()}`; + return `${sensor._id},${index},${moment.utc().add(index, 'minutes') + .toISOString()}`; }).join('\n'); return str; diff --git a/tests/data/custom_valid_sensebox.js b/tests/data/custom_valid_sensebox.js index 5b5c6593..63187387 100644 --- a/tests/data/custom_valid_sensebox.js +++ b/tests/data/custom_valid_sensebox.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { 'name': 'Wetterstation der AG Klimatologie Uni Münster', 'boxType': 'fixed', diff --git a/tests/data/findAllSchema.js b/tests/data/findAllSchema.js index c497cc00..4f73a012 100644 --- a/tests/data/findAllSchema.js +++ b/tests/data/findAllSchema.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'array', diff --git a/tests/data/getUserBoxesSchema.js b/tests/data/getUserBoxesSchema.js index 29a3095d..e140050f 100644 --- a/tests/data/getUserBoxesSchema.js +++ b/tests/data/getUserBoxesSchema.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', diff --git a/tests/data/getUserSchema.js b/tests/data/getUserSchema.js index b07d3522..bc06f824 100644 --- a/tests/data/getUserSchema.js +++ b/tests/data/getUserSchema.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', diff --git a/tests/data/json_submit_data.js b/tests/data/json_submit_data.js index 205d20b6..b0e2f834 100644 --- a/tests/data/json_submit_data.js +++ b/tests/data/json_submit_data.js @@ -1,7 +1,5 @@ 'use strict'; -const moment = require('moment'); - const json_obj = function (sensors) { const obj = {}; sensors.forEach(function (sensor, index) { diff --git a/tests/data/luftdaten_example_data.js b/tests/data/luftdaten_example_data.js index 65ac2f9a..cee6f1b2 100644 --- a/tests/data/luftdaten_example_data.js +++ b/tests/data/luftdaten_example_data.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { 'software_version': 'NRZ-2016-058', 'sensordatavalues': [ diff --git a/tests/data/randomApiKey.js b/tests/data/randomApiKey.js deleted file mode 100644 index d928a354..00000000 --- a/tests/data/randomApiKey.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function () { - return (new Date().getTime() / 1000 | 0).toString(16) - + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, () => (Math.random() * 16 | 0) - .toString(16)) - .toLowerCase(); -}; diff --git a/tests/data/senseBoxCreateSchema.js b/tests/data/senseBoxCreateSchema.js deleted file mode 100644 index 7fc193f8..00000000 --- a/tests/data/senseBoxCreateSchema.js +++ /dev/null @@ -1,35 +0,0 @@ -module.exports = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'type': 'object', - 'properties': { - 'firstname': { - 'type': 'string' - }, - 'lastname': { - 'type': 'string' - }, - 'email': { - 'type': 'string' - }, - 'apikey': { - 'type': 'string' - }, - '_id': { - 'type': 'string' - }, - 'boxes': { - 'type': 'array', - 'items': { - 'type': 'string' - } - } - }, - 'required': [ - 'firstname', - 'lastname', - 'email', - 'apikey', - '_id', - 'boxes' - ] -}; diff --git a/tests/data/senseBoxSchema.js b/tests/data/senseBoxSchema.js index 945952fe..02734b70 100644 --- a/tests/data/senseBoxSchema.js +++ b/tests/data/senseBoxSchema.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', diff --git a/tests/data/senseBoxSchemaAllFieldsUsers.js b/tests/data/senseBoxSchemaAllFieldsUsers.js deleted file mode 100644 index 1cf7090c..00000000 --- a/tests/data/senseBoxSchemaAllFieldsUsers.js +++ /dev/null @@ -1,118 +0,0 @@ -module.exports = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'type': 'object', - 'properties': { - '_id': { - 'type': 'string' - }, - 'createdAt': { - 'type': 'string' - }, - 'updatedAt': { - 'type': 'string' - }, - 'name': { - 'type': 'string' - }, - 'boxType': { - 'type': 'string' - }, - 'grouptag': { - 'type': 'string' - }, - 'exposure': { - 'type': 'string' - }, - 'model': { - 'type': 'string' - }, - 'description': { - 'type': 'string' - }, - 'image': { - 'type': 'string' - }, - 'sensors': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'title': { - 'type': 'string' - }, - 'unit': { - 'type': 'string' - }, - 'sensorType': { - 'type': 'string' - }, - '_id': { - 'type': 'string' - } - }, - 'required': [ - 'title', - 'unit', - 'sensorType', - '_id' - ] - } - }, - 'loc': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'geometry': { - 'type': 'object', - 'properties': { - 'coordinates': { - 'type': 'array', - 'items': { - 'type': 'number' - } - }, - 'type': { - 'type': 'string' - } - }, - 'required': [ - 'coordinates', - 'type' - ] - }, - 'type': { - 'type': 'string' - } - }, - 'required': [ - 'geometry', - 'type' - ] - } - }, - 'mqtt': { - 'type': 'object', - 'properties': { - 'enabled': { - 'type': 'boolean' - } - }, - 'required': [ - 'enabled' - ] - } - }, - 'required': [ - '_id', - 'createdAt', - 'updatedAt', - 'name', - 'boxType', - 'exposure', - 'model', - 'sensors', - 'loc', - 'mqtt' - ] -}; diff --git a/tests/data/valid_sensebox.js b/tests/data/valid_sensebox.js index 4837f75d..45444d87 100644 --- a/tests/data/valid_sensebox.js +++ b/tests/data/valid_sensebox.js @@ -1,3 +1,5 @@ +'use strict'; + const randomGeojson = require('randomgeojson'); module.exports = function ({ bbox, model, sensors, lonlat } = {}) { const randomGeojsonOptions = { featureTypes: ['Point'] }; @@ -14,7 +16,6 @@ module.exports = function ({ bbox, model, sensors, lonlat } = {}) { loc = [ randomGeojson.generateGeoJSON(randomGeojsonOptions).features[0] ]; } - if (!sensors && !model) { model = 'homeEthernet'; } diff --git a/tests/mail_tests.js b/tests/mail_tests.js index 0bbb923c..1e121fc9 100644 --- a/tests/mail_tests.js +++ b/tests/mail_tests.js @@ -335,7 +335,8 @@ describe('mails', function () { const liTexts = []; mailbody('li').each(function (_, li) { - liTexts.push($(li).text().slice(0, 15)); + liTexts.push($(li).text() + .slice(0, 15)); }); expect(liTexts).to.include.members(['PM2.5 (SDS 011)', 'PM10 (SDS 011):']); diff --git a/tests/routes/002-idw-test.js b/tests/routes/002-idw-test.js index 15664021..1ba29149 100644 --- a/tests/routes/002-idw-test.js +++ b/tests/routes/002-idw-test.js @@ -10,7 +10,8 @@ const chakram = require('chakram'), const BASE_URL = `${process.env.OSEM_TEST_BASE_URL}/statistics/idw`, baseTimestamp = moment.utc('2017-01-01T12:00:00Z'), - hasDataParameters = `exposure=indoor&phenomenon=Temperatur&from-date=${baseTimestamp.clone().toISOString()}&to-date=${baseTimestamp.clone().add(12, 'minute').toISOString()}`; + hasDataParameters = `exposure=indoor&phenomenon=Temperatur&from-date=${baseTimestamp.clone().toISOString()}&to-date=${baseTimestamp.clone().add(12, 'minute') + .toISOString()}`; describe('openSenseMap API Routes: /statistics/idw', function () { let firstRequest, requestNoData; diff --git a/tests/routes/003-users-test.js b/tests/routes/003-users-test.js new file mode 100644 index 00000000..dca558d6 --- /dev/null +++ b/tests/routes/003-users-test.js @@ -0,0 +1,512 @@ +'use strict'; + +/* global describe it */ + +const chakram = require('chakram'), + expect = chakram.expect, + valid_sensebox = require('../data/valid_sensebox'), + valid_user = require('../data/valid_user'), + getUserSchema = require('../data/getUserSchema'); + +const BASE_URL = process.env.OSEM_TEST_BASE_URL; + +describe('openSenseMap API Routes: /users', function () { + let jwt, refreshToken; + it('should allow to register an user via POST', function () { + return chakram.post(`${BASE_URL}/users/register`, valid_user) + .then(function (response) { + expect(response).to.have.status(201); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + + return chakram.wait(); + }); + }); + + it('should deny to register an user with the same email', function () { + return chakram.post(`${BASE_URL}/users/register`, valid_user) + .then(function (response) { + expect(response).to.have.status(400); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to register an user with too short password', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: 'tester', password: 'short', email: 'address@email.com' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to register an user with no name', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: '', password: 'longenough', email: 'address@email.com' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to register an user with missing name parameter', function () { + return chakram.post(`${BASE_URL}/users/register`, { password: 'longenough', email: 'address@email.com' }) + .then(function (response) { + expect(response).to.have.status(400); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to register an user with invalid email address', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: 'tester mc testmann', password: 'longenough', email: 'invalid' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to register a too short username', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: 't', password: 'longenough', email: 'address@email.com' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to register an user with username not starting with a letter or number', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: ' username', password: 'longenough', email: 'address@email.com' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to register an user with username with invalid characters', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: 'user () name', password: 'longenough', email: 'address@email.com' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + + it('should deny to register a too long username', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: 'Really Long User Name which is definetely too long to be accepted', password: 'longenough', email: 'address@email.com' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should allow to register a second user via POST', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: 'mrtest', email: 'tester2@test.test', password: '12345678' }) + .then(function (response) { + expect(response).to.have.status(201); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + + jwt = response.body.token; + refreshToken = response.body.refreshToken; + + return chakram.wait(); + }); + }); + + it('should deny to change email and password at the same time', function () { + return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www', newPassword: '87654321' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(400); + expect(response).to.have.json('message', 'You cannot change your email address and password in the same request.'); + + return chakram.wait(); + }); + }); + + it('should deny to change email without current passsword', function () { + return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(400); + expect(response).to.have.json('message', 'To change your password or email address, please supply your current password.'); + + return chakram.wait(); + }); + }); + + it('should deny to change email with wrong current passsword', function () { + return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www', currentPassword: 'wrongpassword' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(400); + expect(response).to.have.json('message', 'Password not correct.'); + + return chakram.wait(); + }); + }); + + it('should allow to change email with correct current passsword', function () { + return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('message', 'User successfully saved. E-Mail changed. Please confirm your new address. Until confirmation, sign in using your old address'); + + return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('data', function (data) { + expect(data.me.email).to.equal('tester2@test.test'); + }); + + return chakram.wait(); + }); + }); + + it('should allow to change name', function () { + return chakram.put(`${BASE_URL}/users/me`, { name: 'new Name' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('message', 'User successfully saved.'); + + return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('data', function (data) { + expect(data.me.name).to.equal('new Name'); + }); + + return chakram.wait(); + }); + }); + + it('should deny to change name to existing name', function () { + return chakram.put(`${BASE_URL}/users/me`, { name: 'this is just a nickname', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(400); + expect(response).to.have.json('message', 'Duplicate user detected'); + + return chakram.wait(); + }); + }); + + it('should deny to change password with too short new password', function () { + return chakram.put(`${BASE_URL}/users/me`, { newPassword: 'short', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(400); + expect(response).to.have.json('message', 'New password should have at least 8 characters'); + + return chakram.wait(); + }); + }); + + it('should deny to change email to invalid email', function () { + return chakram.put(`${BASE_URL}/users/me`, { email: 'invalid email', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(422); + + return chakram.wait(); + }); + }); + + it('should deny to change name to invalid name', function () { + return chakram.put(`${BASE_URL}/users/me`, { name: ' invalid name', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(422); + + return chakram.wait(); + }); + }); + + it('should allow to change to a password with leading and trailing spaces', function () { + return chakram.put(`${BASE_URL}/users/me`, { newPassword: ' leading and trailing spaces ', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('message', 'User successfully saved. Password changed. Please log in with your new password'); + + // try to log in with old token + return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }); + }) + .then(function (response) { + expect(response).to.have.status(401); + + // try to sign in with new password + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'tester2@test.test', password: ' leading and trailing spaces ' }); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('data', function (data) { + expect(data.user.email).to.equal('tester2@test.test'); + }); + expect(response.body.token).to.exist; + + jwt = response.body.token; + + return chakram.wait(); + }); + }); + + it('should allow to change password with correct current password', function () { + return chakram.put(`${BASE_URL}/users/me`, { newPassword: '12345678910', currentPassword: ' leading and trailing spaces ' }, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('message', 'User successfully saved. Password changed. Please log in with your new password'); + + // try to log in with old token + return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }); + }) + .then(function (response) { + expect(response).to.have.status(401); + + // try to sign in with new password + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'tester2@test.test', password: '12345678910' }); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.json('data', function (data) { + expect(data.user.email).to.equal('tester2@test.test'); + }); + expect(response.body.token).to.exist; + + jwt = response.body.token; + + return chakram.wait(); + }); + }); + + it('should deny to request a fresh jwt using refresh token after changing the password', function () { + return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken }) + .then(function (response) { + expect(response).to.have.status(403); + + return chakram.wait(); + }); + }); + + it('should deny to sign in with wrong password', function () { + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'tester@test.test', password: 'wrong password' }) + .then(function (response) { + expect(response).to.have.status(401); + + return chakram.wait(); + }); + }); + + it('should allow to sign in an user with email and password', function () { + return chakram.post(`${BASE_URL}/users/sign-in`, valid_user) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + expect(response.body.refreshToken).to.exist; + + return chakram.wait(); + }); + }); + + it('should allow to sign in an user with name and password', function () { + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'this is just a nickname', password: 'some secure password' }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + expect(response.body.refreshToken).to.exist; + + jwt = response.body.token; + refreshToken = response.body.refreshToken; + + return chakram.wait(); + }); + }); + + it('should allow to sign in an user with email (different case) and password', function () { + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'TESTER@TEST.TEST', password: 'some secure password' }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + expect(response.body.refreshToken).to.exist; + + return chakram.wait(); + }); + }); + + it('should deny to sign in with name in different case', function () { + return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'This Is Just A Nickname', password: 'some secure password' }) + .then(function (response) { + expect(response).to.have.status(401); + + return chakram.wait(); + }); + }); + + it('should allow to sign out with jwt', function () { + return chakram.post(`${BASE_URL}/users/sign-out`, {}, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); + + it('should deny to use the refreshToken after signing out', function () { + return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken }) + .then(function (response) { + expect(response).to.have.status(403); + + return chakram.wait(); + }); + }); + + it('should deny to use revoked jwt', function () { + return chakram.post(`${BASE_URL}/boxes`, valid_sensebox(), { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(401); + + return chakram.post(`${BASE_URL}/users/sign-in`, valid_user); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + expect(response.body.refreshToken).to.exist; + + jwt = response.body.token; + refreshToken = response.body.refreshToken; + + return chakram.wait(); + }); + }); + + it('should allow to refresh jwt using the refresh token', function () { + return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + expect(response.body.refreshToken).to.exist; + + const jwt = response.body.token; + + return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response).to.have.schema(getUserSchema); + expect(response).to.comprise.of.json({ code: 'Ok', data: { me: { email: 'tester@test.test' } } }); + + return chakram.wait(); + }); + }); + + it('should deny to use an refresh token twice', function () { + return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken }) + .then(function (response) { + expect(response).to.have.status(403); + + return chakram.wait(); + }); + }); + + it('should deny to use an old jwt after using a refresh token', function () { + return chakram.post(`${BASE_URL}/boxes`, valid_sensebox(), { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(401); + + return chakram.post(`${BASE_URL}/users/sign-in`, valid_user); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + expect(response.body.refreshToken).to.exist; + + jwt = response.body.token; + refreshToken = response.body.refreshToken; + + return chakram.wait(); + }); + }); + + it('should allow to request a password reset token', function () { + return chakram.post(`${BASE_URL}/users/request-password-reset`, valid_user) + .then(function (response) { + expect(response).to.have.status(200); + + return chakram.wait(); + }); + }); + + it('should deny password request with wrong token', function () { + return chakram.post(`${BASE_URL}/users/password-reset`, { password: 'ignored_anyway', token: 'invalid_password-reset_token', email: 'tester@test.test' }) + .then(function (response) { + expect(response).to.have.status(403); + expect(response).to.have.json({ code: 'ForbiddenError', + message: 'Password reset for this user not possible' }); + + return chakram.wait(); + }); + }); + + it('should deny password change with empty token parameter', function () { + return chakram.post(`${BASE_URL}/users/password-reset`, { password: 'ignored_anyway', token: ' ', email: 'tester@test.test' }) + .then(function (response) { + expect(response).to.have.status(422); + }); + }); + + it('should deny email confirmation with wrong token', function () { + return chakram.post(`${BASE_URL}/users/confirm-email`, { token: 'invalid_password-reset_token', email: 'tester@test.test' }) + .then(function (response) { + expect(response).to.have.status(403); + + return chakram.wait(); + }); + }); + + it('should allow users to request their details', function () { + return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response).to.have.schema(getUserSchema); + expect(response).to.comprise.of.json({ code: 'Ok', data: { me: { email: 'tester@test.test' } } }); + + return chakram.wait(); + }); + }); + + it('should allow users request a resend of the email confirmation', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: 'mrtest', email: 'tester4@test.test', password: '12345678' }) + .then(function (response) { + expect(response).to.have.status(201); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response.body.token).to.exist; + + return chakram.post(`${BASE_URL}/users/me/resend-email-confirmation`, {}, { headers: { 'Authorization': `Bearer ${response.body.token}` } }); + }) + .then(function (response) { + expect(response).to.have.status(200); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response).to.comprise.of.json({ code: 'Ok', message: 'Email confirmation has been sent to tester4@test.test' }); + + return chakram.wait(); + }); + }); +}); diff --git a/tests/tests.js b/tests/tests.js index ef6c871e..16f7f2ca 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,6 +1,6 @@ 'use strict'; -/* global describe it */ +/* global describe it before */ const chakram = require('chakram'), expect = chakram.expect, @@ -12,14 +12,11 @@ const BASE_URL = process.env.OSEM_TEST_BASE_URL, valid_sensebox = require('./data/valid_sensebox'), valid_user = require('./data/valid_user'), senseBoxSchema = require('./data/senseBoxSchema'), - senseBoxSchemaAllFields = require('./data/senseBoxSchemaAllFieldsUsers'), findAllSchema = require('./data/findAllSchema'), csv_example_data = require('./data/csv_example_data'), json_submit_data = require('./data/json_submit_data'), byte_submit_data = require('./data/byte_submit_data'), getUserBoxesSchema = require('./data/getUserBoxesSchema'), - getUserSchema = require('./data/getUserSchema'), - luftdaten_example_data = require('./data/luftdaten_example_data'), publishMqttMessage = require('./helpers/mqtt'), custom_valid_sensebox = require('./data/custom_valid_sensebox'); @@ -34,511 +31,26 @@ require('fs') }); describe('openSenseMap API', function () { - let jwt, jwt2, refreshToken, refreshToken2; - - describe('/users', function () { - it('should allow to register an user via POST', function () { - return chakram.post(`${BASE_URL}/users/register`, valid_user) - .then(function (response) { - expect(response).to.have.status(201); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - - return chakram.wait(); - }); - }); - - it('should deny to register an user with the same email', function () { - return chakram.post(`${BASE_URL}/users/register`, valid_user) - .then(function (response) { - expect(response).to.have.status(400); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to register an user with too short password', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: 'tester', password: 'short', email: 'address@email.com' }) - .then(function (response) { - expect(response).to.have.status(422); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to register an user with no name', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: '', password: 'longenough', email: 'address@email.com' }) - .then(function (response) { - expect(response).to.have.status(422); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to register an user with missing name parameter', function () { - return chakram.post(`${BASE_URL}/users/register`, { password: 'longenough', email: 'address@email.com' }) - .then(function (response) { - expect(response).to.have.status(400); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to register an user with invalid email address', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: 'tester mc testmann', password: 'longenough', email: 'invalid' }) - .then(function (response) { - expect(response).to.have.status(422); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to register a too short username', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: 't', password: 'longenough', email: 'address@email.com' }) - .then(function (response) { - expect(response).to.have.status(422); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to register an user with username not starting with a letter or number', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: ' username', password: 'longenough', email: 'address@email.com' }) - .then(function (response) { - expect(response).to.have.status(422); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to register an user with username with invalid characters', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: 'user () name', password: 'longenough', email: 'address@email.com' }) - .then(function (response) { - expect(response).to.have.status(422); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - - it('should deny to register a too long username', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: 'Really Long User Name which is definetely too long to be accepted', password: 'longenough', email: 'address@email.com' }) - .then(function (response) { - expect(response).to.have.status(422); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should allow to register a second user via POST', function () { - return chakram.post(`${BASE_URL}/users/register`, { name: 'mrtest', email: 'tester2@test.test', password: '12345678' }) - .then(function (response) { - expect(response).to.have.status(201); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - - jwt2 = response.body.token; - refreshToken2 = response.body.refreshToken; - - return chakram.wait(); - }); - }); - - it('should deny to change email and password at the same time', function () { - return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www', newPassword: '87654321' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(400); - expect(response).to.have.json('message', 'You cannot change your email address and password in the same request.'); - - return chakram.wait(); - }); - }); - - it('should deny to change email without current passsword', function () { - return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(400); - expect(response).to.have.json('message', 'To change your password or email address, please supply your current password.'); - - return chakram.wait(); - }); - }); - - it('should deny to change email with wrong current passsword', function () { - return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www', currentPassword: 'wrongpassword' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(400); - expect(response).to.have.json('message', 'Password not correct.'); - - return chakram.wait(); - }); - }); - - it('should allow to change email with correct current passsword', function () { - return chakram.put(`${BASE_URL}/users/me`, { email: 'new-email@email.www', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('message', 'User successfully saved. E-Mail changed. Please confirm your new address. Until confirmation, sign in using your old address'); - - return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt2}` } }); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('data', function (data) { - expect(data.me.email).to.equal('tester2@test.test'); - }); - - return chakram.wait(); - }); - }); - - it('should allow to change name', function () { - return chakram.put(`${BASE_URL}/users/me`, { name: 'new Name' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('message', 'User successfully saved.'); - - return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt2}` } }); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('data', function (data) { - expect(data.me.name).to.equal('new Name'); - }); - - return chakram.wait(); - }); - }); - - it('should deny to change name to existing name', function () { - return chakram.put(`${BASE_URL}/users/me`, { name: 'this is just a nickname', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(400); - expect(response).to.have.json('message', 'Duplicate user detected'); - - return chakram.wait(); - }); - }); - - it('should deny to change password with too short new password', function () { - return chakram.put(`${BASE_URL}/users/me`, { newPassword: 'short', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(400); - expect(response).to.have.json('message', 'New password should have at least 8 characters'); - - return chakram.wait(); - }); - }); - - it('should deny to change email to invalid email', function () { - return chakram.put(`${BASE_URL}/users/me`, { email: 'invalid email', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(422); - - return chakram.wait(); - }); - }); - - it('should deny to change name to invalid name', function () { - return chakram.put(`${BASE_URL}/users/me`, { name: ' invalid name', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(422); - - return chakram.wait(); - }); - }); - - it('should allow to change to a password with leading and trailing spaces', function () { - return chakram.put(`${BASE_URL}/users/me`, { newPassword: ' leading and trailing spaces ', currentPassword: '12345678' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('message', 'User successfully saved. Password changed. Please log in with your new password'); - - // try to log in with old token - return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt2}` } }); - }) - .then(function (response) { - expect(response).to.have.status(401); - - // try to sign in with new password - return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'tester2@test.test', password: ' leading and trailing spaces ' }); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('data', function (data) { - expect(data.user.email).to.equal('tester2@test.test'); - }); - expect(response.body.token).to.exist; - - jwt2 = response.body.token; - - return chakram.wait(); - }); - }); - - it('should allow to change password with correct current password', function () { - return chakram.put(`${BASE_URL}/users/me`, { newPassword: '12345678910', currentPassword: ' leading and trailing spaces ' }, { headers: { 'Authorization': `Bearer ${jwt2}` } }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('message', 'User successfully saved. Password changed. Please log in with your new password'); - - // try to log in with old token - return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt2}` } }); - }) - .then(function (response) { - expect(response).to.have.status(401); - - // try to sign in with new password - return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'tester2@test.test', password: '12345678910' }); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.json('data', function (data) { - expect(data.user.email).to.equal('tester2@test.test'); - }); - expect(response.body.token).to.exist; - - jwt2 = response.body.token; - - return chakram.wait(); - }); - }); - - it('should deny to request a fresh jwt using refresh token after changing the password', function () { - return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken2 }) - .then(function (response) { - expect(response).to.have.status(403); - - return chakram.wait(); - }); - }); - - it('should deny to sign in with wrong password', function () { - return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'tester@test.test', password: 'wrong password' }) - .then(function (response) { - expect(response).to.have.status(401); - - return chakram.wait(); - }); - }); - - it('should allow to sign in an user with email and password', function () { - return chakram.post(`${BASE_URL}/users/sign-in`, valid_user) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - expect(response.body.refreshToken).to.exist; - - return chakram.wait(); - }); - }); - - it('should allow to sign in an user with name and password', function () { - return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'this is just a nickname', password: 'some secure password' }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - expect(response.body.refreshToken).to.exist; - - jwt = response.body.token; - refreshToken = response.body.refreshToken; - - return chakram.wait(); - }); - }); - - it('should allow to sign in an user with email (different case) and password', function () { - return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'TESTER@TEST.TEST', password: 'some secure password' }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - expect(response.body.refreshToken).to.exist; - - return chakram.wait(); - }); - }); - - it('should deny to sign in with name in different case', function () { - return chakram.post(`${BASE_URL}/users/sign-in`, { email: 'This Is Just A Nickname', password: 'some secure password' }) - .then(function (response) { - expect(response).to.have.status(401); - - return chakram.wait(); - }); - }); - - it('should allow to sign out with jwt', function () { - return chakram.post(`${BASE_URL}/users/sign-out`, {}, { headers: { 'Authorization': `Bearer ${jwt}` } }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - - return chakram.wait(); - }); - }); - - it('should deny to use the refreshToken after signing out', function () { - return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken }) - .then(function (response) { - expect(response).to.have.status(403); - - return chakram.wait(); - }); - }); - - it('should deny to use revoked jwt', function () { - return chakram.post(`${BASE_URL}/boxes`, valid_sensebox(), { headers: { 'Authorization': `Bearer ${jwt}` } }) - .then(function (response) { - expect(response).to.have.status(401); - - return chakram.post(`${BASE_URL}/users/sign-in`, valid_user); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - expect(response.body.refreshToken).to.exist; - - jwt = response.body.token; - refreshToken = response.body.refreshToken; - - return chakram.wait(); - }); - }); - - it('should allow to refresh jwt using the refresh token', function () { - return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - expect(response.body.refreshToken).to.exist; - - const jwt = response.body.token; - - return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response).to.have.schema(getUserSchema); - expect(response).to.comprise.of.json({ code: 'Ok', data: { me: { email: 'tester@test.test' } } }); - - return chakram.wait(); - }); - }); - - it('should deny to use an refresh token twice', function () { - return chakram.post(`${BASE_URL}/users/refresh-auth`, { 'token': refreshToken }) - .then(function (response) { - expect(response).to.have.status(403); - - return chakram.wait(); - }); - }); - - it('should deny to use an old jwt after using a refresh token', function () { - return chakram.post(`${BASE_URL}/boxes`, valid_sensebox(), { headers: { 'Authorization': `Bearer ${jwt}` } }) - .then(function (response) { - expect(response).to.have.status(401); - - return chakram.post(`${BASE_URL}/users/sign-in`, valid_user); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - expect(response.body.refreshToken).to.exist; - - jwt = response.body.token; - refreshToken = response.body.refreshToken; - - return chakram.wait(); - }); - }); - - it('should allow to request a password reset token', function () { - return chakram.post(`${BASE_URL}/users/request-password-reset`, valid_user) - .then(function (response) { - expect(response).to.have.status(200); - - return chakram.wait(); - }); - }); - - it('should deny password request with wrong token', function () { - return chakram.post(`${BASE_URL}/users/password-reset`, { password: 'ignored_anyway', token: 'invalid_password-reset_token', email: 'tester@test.test' }) - .then(function (response) { - expect(response).to.have.status(403); - expect(response).to.have.json({ code: 'ForbiddenError', - message: 'Password reset for this user not possible' }); - - return chakram.wait(); - }); - }); - - it('should deny password change with empty token parameter', function () { - return chakram.post(`${BASE_URL}/users/password-reset`, { password: 'ignored_anyway', token: ' ', email: 'tester@test.test' }) - .then(function (response) { - expect(response).to.have.status(422); - }); - }); - - it('should deny email confirmation with wrong token', function () { - return chakram.post(`${BASE_URL}/users/confirm-email`, { token: 'invalid_password-reset_token', email: 'tester@test.test' }) - .then(function (response) { - expect(response).to.have.status(403); - - return chakram.wait(); - }); - }); - - it('should allow users to request their details', function () { - return chakram.get(`${BASE_URL}/users/me`, { headers: { 'Authorization': `Bearer ${jwt}` } }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response).to.have.schema(getUserSchema); - expect(response).to.comprise.of.json({ code: 'Ok', data: { me: { email: 'tester@test.test' } } }); - - return chakram.wait(); - }); - }); - - it('should allow users request a resend of the email confirmation', function () { - let jwt3; - - return chakram.post(`${BASE_URL}/users/register`, { name: 'mrtest', email: 'tester4@test.test', password: '12345678' }) - .then(function (response) { - expect(response).to.have.status(201); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response.body.token).to.exist; - - jwt3 = response.body.token; - - return chakram.post(`${BASE_URL}/users/me/resend-email-confirmation`, {}, { headers: { 'Authorization': `Bearer ${jwt3}` } }); - }) - .then(function (response) { - expect(response).to.have.status(200); - expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); - expect(response).to.comprise.of.json({ code: 'Ok', message: 'Email confirmation has been sent to tester4@test.test' }); - - return chakram.wait(); - }); - }); - + let jwt, jwt2; + + before(function () { + return Promise.all([ + chakram.post(`${BASE_URL}/users/sign-in`, valid_user), + chakram.post(`${BASE_URL}/users/sign-in`, { email: 'tester2@test.test', password: '12345678910' }) + ]) + .then(function ([response1, response2]) { + expect(response1).to.have.status(200); + expect(response1).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response1.body.token).to.exist; + expect(response2).to.have.status(200); + expect(response2).to.have.header('content-type', 'application/json; charset=utf-8'); + expect(response2.body.token).to.exist; + + jwt = response1.body.token; + jwt2 = response2.body.token; + + return chakram.wait(); + }); }); describe('/boxes', function () { @@ -1166,7 +678,8 @@ describe('openSenseMap API', function () { it('should return the correct count and correct schema of boxes for /boxes GET with two date parameters', function () { const now = moment.utc(); - return chakram.get(`${BASE_URL}/boxes?date=${now.clone().subtract(1, 'minute').toISOString()},${now.toISOString()}`) + return chakram.get(`${BASE_URL}/boxes?date=${now.clone().subtract(1, 'minute') + .toISOString()},${now.toISOString()}`) .then(function (response) { expect(response).to.have.status(200); expect(Array.isArray(response.body)).to.be.true; @@ -1174,7 +687,9 @@ describe('openSenseMap API', function () { expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); expect(response).to.have.schema(findAllSchema); - return chakram.get(`${BASE_URL}/boxes?date=${now.clone().subtract(10, 'days').subtract(10, 'minutes').toISOString()},${now.toISOString()}`); + return chakram.get(`${BASE_URL}/boxes?date=${now.clone().subtract(10, 'days') + .subtract(10, 'minutes') + .toISOString()},${now.toISOString()}`); }) .then(function (response) { expect(response).to.have.status(200); @@ -1258,7 +773,8 @@ describe('openSenseMap API', function () { it('should return the correct count and correct schema of boxes for /boxes GET with two date parameters after deleted sensor', function () { const now = moment.utc(); - return chakram.get(`${BASE_URL}/boxes?date=${now.clone().subtract(5, 'minutes').toISOString()},${now.toISOString()}&phenomenon=Temperatur`) + return chakram.get(`${BASE_URL}/boxes?date=${now.clone().subtract(5, 'minutes') + .toISOString()},${now.toISOString()}&phenomenon=Temperatur`) .then(function (response) { expect(response).to.have.status(200); expect(Array.isArray(response.body)).to.be.true; @@ -1499,7 +1015,8 @@ describe('openSenseMap API', function () { it('should allow to delete all data for a single sensor through specifying from-date and to-date', function () { const sensor_id = boxes[boxIds[1]].sensors[boxes[boxIds[1]].sensors.findIndex(s => s.title === 'rel. Luftfeuchte')]._id; - const payload = { 'from-date': moment.utc().subtract(1, 'year').toISOString(), 'to-date': moment.utc().toISOString() }; + const payload = { 'from-date': moment.utc().subtract(1, 'year') + .toISOString(), 'to-date': moment.utc().toISOString() }; return chakram.delete(`${BASE_URL}/boxes/${boxIds[1]}/${sensor_id}/measurements`, payload, { headers: { 'content-type': 'application/json', 'Authorization': `Bearer ${jwt2}` } }) .then(function (response) { diff --git a/tests/waitForHttp.js b/tests/waitForHttp.js index 9c6a821a..e00c628f 100644 --- a/tests/waitForHttp.js +++ b/tests/waitForHttp.js @@ -2,12 +2,14 @@ const request = require('request-promise-native'); +/*eslint-disable no-console */ const connectWithRetry = function (success) { return request('http://localhost:8000/boxes') .then(function () { success(); }) .catch(function () { + /*eslint-disable no-console */ console.log('wait for http...'); setTimeout(connectWithRetry, 500, success); }); @@ -16,3 +18,4 @@ const connectWithRetry = function (success) { connectWithRetry(function () { console.log('http available'); }); +/*eslint-enable no-console */ diff --git a/yarn.lock b/yarn.lock index 38c95b93..8fdcdfe1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -120,6 +120,15 @@ ajv@^4.7.0, ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" +ajv@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + json-schema-traverse "^0.3.0" + json-stable-stringify "^1.0.1" + ansi-escapes@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" @@ -371,9 +380,9 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -cheerio@^1.0.0-rc.1: - version "1.0.0-rc.1" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.1.tgz#2af37339eab713ef6b72cde98cefa672b87641fe" +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" dependencies: css-select "~1.2.0" dom-serializer "~0.1.0" @@ -455,6 +464,14 @@ core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -709,13 +726,15 @@ eslint-scope@^3.7.1: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.1.1.tgz#facbdfcfe3e0facd3a8b80dc98c4e6c13ae582df" +eslint@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.3.0.tgz#fcd7c96376bbf34c85ee67ed0012a299642b108f" dependencies: + ajv "^5.2.0" babel-code-frame "^6.22.0" chalk "^1.1.3" concat-stream "^1.6.0" + cross-spawn "^5.1.0" debug "^2.6.8" doctrine "^2.0.0" eslint-scope "^3.7.1" @@ -724,12 +743,12 @@ eslint@^4.1.1: estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" glob "^7.1.2" globals "^9.17.0" ignore "^3.3.3" imurmurhash "^0.1.4" inquirer "^3.0.6" - is-my-json-valid "^2.16.0" is-resolvable "^1.0.0" js-yaml "^3.8.4" json-stable-stringify "^1.0.1" @@ -743,6 +762,7 @@ eslint@^4.1.1: pluralize "^4.0.0" progress "^2.0.0" require-uncached "^1.0.3" + semver "^5.3.0" strip-json-comments "~2.0.1" table "^4.0.1" text-table "~0.2.0" @@ -829,6 +849,10 @@ extsprintf@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.2.0.tgz#5ad946c22f5b32ba7f8cd7426711c6e8a3fc2529" +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -892,6 +916,10 @@ fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: mkdirp ">=0.5 0" rimraf "2" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + gauge@~2.7.1: version "2.7.2" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.2.tgz#15cecc31b02d05345a5d6b0e171cdb3ad2307774" @@ -1191,7 +1219,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-my-json-valid@^2.12.4, is-my-json-valid@^2.16.0: +is-my-json-valid@^2.12.4: version "2.16.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" dependencies: @@ -1272,6 +1300,10 @@ isemail@^3.0.0: dependencies: punycode "2.x.x" +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -1330,6 +1362,10 @@ jschardet@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.4.2.tgz#2aa107f142af4121d145659d44f50830961e699a" +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -2150,10 +2186,24 @@ semver@^5.1.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -2517,6 +2567,12 @@ wgs84@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/wgs84/-/wgs84-0.0.0.tgz#34fdc555917b6e57cf2a282ed043710c049cdc76" +which@^1.2.9: + version "1.2.14" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" From 4a6c545fbaa9d29d2cf65fb5c00d310901706adf Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Tue, 25 Jul 2017 15:43:33 +0200 Subject: [PATCH 08/12] use different username regex to prevent multiline usernames, use isemail in schema validation --- lib/models/user.js | 13 ++++++------- tests/routes/003-users-test.js | 11 +++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/models/user.js b/lib/models/user.js index dc0c17ec..252d35e8 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -45,13 +45,13 @@ const { mongoose } = require('../db'), hashJWT = require('../helpers/jwtRefreshTokenHasher'), tokenBlacklist = require('../helpers/tokenBlacklist'), timestamp = require('mongoose-timestamp'), - ModelError = require('./modelError'); + ModelError = require('./modelError'), + isemail = require('isemail'); const userNameRequirementsText = 'Parameter name must consist of at least 3 and up to 40 alphanumerics (a-zA-Z0-9), dot (.), dash (-), underscore (_) and spaces.', userEmailRequirementsText = 'Parameter {PATH} is not a valid email address.'; -const nameValidRegex = /^[\u00C0-\u1FFF\u2C00-\uD7FF\w-.][\u00C0-\u1FFF\u2C00-\uD7FF\w\s-.]+[\u00C0-\u1FFF\u2C00-\uD7FF\w.]$/, - emailValidRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; +const nameValidRegex = /^[^~`!@#$%^&*()+=£€{}[\]|\\:;"'<>,?/\n\r\t\s][^~`!@#$%^&*()+=£€{}[\]|\\:;"'<>,?/\n\r\t]{1,39}[^~`!@#$%^&*()+=£€{}[\]|\\:;"'<>,?/\n\r\t\s]$/; const userSchema = new Schema({ name: { @@ -74,9 +74,8 @@ const userSchema = new Schema({ lowercase: true, unique: true, validate: { - validator: function (v) { - return emailValidRegex.test(v); - }, + isAsync: false, + validator: isemail.validate, message: userEmailRequirementsText } }, @@ -188,7 +187,7 @@ userSchema.path('unconfirmedEmail').validate({ // only run this validation if the new Email has just been set if (this._newEmail) { const user = this; - if (!emailValidRegex.test(user._newEmail)) { + if (!isemail.validate(user._newEmail)) { return callback(false); } diff --git a/tests/routes/003-users-test.js b/tests/routes/003-users-test.js index dca558d6..cf154c0f 100644 --- a/tests/routes/003-users-test.js +++ b/tests/routes/003-users-test.js @@ -509,4 +509,15 @@ describe('openSenseMap API Routes: /users', function () { return chakram.wait(); }); }); + + it('should deny to register with multiline username', function () { + return chakram.post(`${BASE_URL}/users/register`, { name: `multi + line name`, email: 'tester5@test.test', password: '12345678' }) + .then(function (response) { + expect(response).to.have.status(422); + expect(response).to.have.header('content-type', 'application/json; charset=utf-8'); + + return chakram.wait(); + }); + }); }); From a2d78c9c929493cef010ebd47ee5416e6b7d1ca5 Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Tue, 25 Jul 2017 15:43:55 +0200 Subject: [PATCH 09/12] fix failing test of box image --- tests/tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.js b/tests/tests.js index 16f7f2ca..52f2fbca 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -879,7 +879,7 @@ describe('openSenseMap API', function () { expect(response).to.comprise.of.json('data.loc', [ { type: 'Feature', geometry: { type: 'Point', coordinates: [ update_payload.loc.lng, update_payload.loc.lat ] } }]); expect(response).to.comprise.of.json('data.image', function (image) { - return expect(moment().diff(moment(parseInt(image.split('_')[1].slice(0, -4), 36) * 1000))).to.be.below(50); + return expect(moment().diff(moment(parseInt(image.split('_')[1].slice(0, -4), 36) * 1000))).to.be.below(1000); }); return chakram.wait(); From 51920537ef3ad1a6187935e062cbb953184a6c4a Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Wed, 26 Jul 2017 11:49:01 +0200 Subject: [PATCH 10/12] add parameters to run-tests.sh --- run-tests.sh | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/run-tests.sh b/run-tests.sh index d7c0a88e..833bf496 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -1,35 +1,54 @@ #!/bin/bash set -euo pipefail -IFS=$'\n\t' # Disclaimer: I am no bash wizard dont_clean_up=${dont_clean_up:-} -dont_recreate=${dont_recreate:-} +show_logs=${show_logs:-} + +cmd=${1:-} +logs_service=${2:-} + +case "$cmd" in +"build" ) + docker-compose -p osemapitest -f tests-docker-compose.yml build osem-api + exit 0; + ;; +"logs" ) + shift # remove "logs" from $@ + show_logs="true" + if [[ "$logs_service" == "all" ]]; then + logs_service="" + fi + ;; +"dont_clean_up" ) + dont_clean_up="true" +esac mailer_tag=$(git rev-parse --abbrev-ref HEAD) -export SENSEBOX_MAILER_TAG="$mailer_tag" +if [[ "$mailer_tag" == "master" || "$mailer_tag" == "development" ]]; then + export SENSEBOX_MAILER_TAG="$mailer_tag" +else + export SENSEBOX_MAILER_TAG="development" +fi function cleanup { + if [[ -n "$show_logs" ]]; then + if [[ -z "$logs_service" ]]; then + docker-compose -p osemapitest -f tests-docker-compose.yml logs + else + docker-compose -p osemapitest -f tests-docker-compose.yml logs "$logs_service" + fi + fi + if [[ -z "$dont_clean_up" ]]; then echo 'cleanup!' -# docker-compose -p osemapitest -f tests-docker-compose.yml logs osem-api mailer docker-compose -p osemapitest -f tests-docker-compose.yml down -v fi } trap cleanup EXIT -#docker-compose -p osemapitest -f tests-docker-compose.yml up -d --force-rec db -#if [[ -n "$dont_recreate" ]]; then -# echo 'no recreate' -# docker-compose -p osemapitest -f tests-docker-compose.yml up -d --remove-orphans --no-deps osem-api -#else -# #docker-compose -p osemapitest -f tests-docker-compose.yml up -d --force-recreate --remove-orphans --no-deps --build osem-api -# docker-compose -p osemapitest -f tests-docker-compose.yml up -d --force-recreate --remove-orphans --no-deps osem-api -#fi - docker-compose -p osemapitest -f tests-docker-compose.yml down -v -#docker-compose -p osemapitest -f tests-docker-compose.yml up -d --build --force-recreate --remove-orphans docker-compose -p osemapitest -f tests-docker-compose.yml up -d --force-recreate --remove-orphans docker-compose -p osemapitest -f tests-docker-compose.yml exec osem-api npm test From 33333c2e5898938d0789b3bac18bc56511eb8d3d Mon Sep 17 00:00:00 2001 From: Norwin Date: Thu, 27 Jul 2017 12:56:25 +0000 Subject: [PATCH 11/12] fix openSenseBox + links --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 72112db6..76ccc05d 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ openSenseMap-API ================ [![Join the chat at https://gitter.im/sensebox/openSenseMap-API](https://badges.gitter.im/sensebox/openSenseMap-API.svg)](https://gitter.im/sensebox/openSenseMap-API?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -This is the back-end for [openSenseMap](http://opensensemap.org). +This is the back-end for [openSenseMap](https://opensensemap.org). -openSenseMap is part of the [senseBox](http//sensebox.de) project. -To get more information about openSenseBox and senseBox visit the before mentioned links or have a look at this [video](https://www.youtube.com/watch?v=uTOWYa42_rI). +openSenseMap is part of the [senseBox](https://sensebox.de) project. +To get more information about openSenseMap and senseBox visit the before mentioned links or have a look at this [video](https://www.youtube.com/watch?v=uTOWYa42_rI). Originally, this API has been built as part the bachelor thesis of @mpfeil at the ifgi (Institute for Geoinformatics, WWU Münster). From 4d5e66daaf2911be48bc7c7b5a193614ca9a07c1 Mon Sep 17 00:00:00 2001 From: ubergesundheit Date: Sun, 30 Jul 2017 12:47:52 +0200 Subject: [PATCH 12/12] upgrade sketch-templater --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 104f71a7..e2dd8bdb 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "API for opensensemap.org", "main": "index.js", "dependencies": { - "@sensebox/sketch-templater": "^1.0.2", + "@sensebox/sketch-templater": "^1.0.3", "@turf/area": "^4.4.0", "@turf/bbox": "^4.4.0", "@turf/centroid": "^4.4.0", diff --git a/yarn.lock b/yarn.lock index 8fdcdfe1..5de25666 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,9 +8,9 @@ dependencies: wgs84 "0.0.0" -"@sensebox/sketch-templater@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@sensebox/sketch-templater/-/sketch-templater-1.0.2.tgz#fce6398f39623429a8fd3b915eb3cf8760bab96d" +"@sensebox/sketch-templater@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@sensebox/sketch-templater/-/sketch-templater-1.0.3.tgz#5e2efb91d1705cd82af6bec9b95ed488cafeb7ef" dependencies: dedent "^0.7.0"