Browse Source

JUnit with a bypass of SecuritySchema, Added basic test for the endpoint 'campaignsGET'

Lukas Cerny 1 năm trước cách đây
mục cha
commit
cd49a2209f

+ 28 - 2
src/main/java/cz/senslog/telemetry/app/Application.java

@@ -1,10 +1,17 @@
 package cz.senslog.telemetry.app;
 
+import cz.senslog.telemetry.database.repository.CachedMapLogRepository;
+import cz.senslog.telemetry.database.repository.MapLogRepository;
+import cz.senslog.telemetry.database.repository.SensLogRepository;
 import cz.senslog.telemetry.server.HttpVertxServer;
 import cz.senslog.telemetry.server.TCPVertxServer;
 import io.vertx.core.DeploymentOptions;
 import io.vertx.core.Vertx;
 import io.vertx.core.json.JsonObject;
+import io.vertx.sqlclient.Pool;
+import io.vertx.sqlclient.PoolOptions;
+import io.vertx.sqlclient.RowSet;
+import io.vertx.sqlclient.SqlConnectOptions;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -50,18 +57,37 @@ public final class Application {
         return String.format("%02d:%02d:%02d", HH, MM, SS);
     }
 
+    public static void terminate(String message) {
+        logger.error(message);
+        System.exit(1);
+    }
+
     public static void start() {
         logger.info("Starting app '{}', version '{}', build '{}'.", PROJECT_NAME, COMPILED_VERSION, BUILD_VERSION);
 
         PropertyConfig config = PropertyConfig.getInstance();
         DeploymentOptions options = new DeploymentOptions().setConfig(JsonObject.of(
                 "server", config.server(),
-                "db", config.db(),
                 "auth", config.auth()
         ));
 
+        JsonObject dbConfig = config.db();
+        Pool dbPool = Pool.pool(new SqlConnectOptions()
+                .setPort(dbConfig.getInteger("port"))
+                .setHost(dbConfig.getString("host"))
+                .setDatabase(dbConfig.getString("database"))
+                .setUser(dbConfig.getString("user"))
+                .setPassword(dbConfig.getString("password")), new PoolOptions()
+                .setMaxSize(dbConfig.getInteger("pool.size")));
+
+        dbPool.query("SELECT version()").execute().map(RowSet::iterator)
+                .map(it -> it.hasNext() ? it.next().getString(0) : null)
+                .onSuccess(version -> logger.info("Successful database connection to {}.", version))
+                .onFailure(fail -> terminate(fail.getMessage()));
+
         Vertx.vertx().deployVerticle(VertxDeployer.deploy(
-                new HttpVertxServer(), new TCPVertxServer()
+                new HttpVertxServer(MapLogRepository.create(dbPool)),
+                new TCPVertxServer(CachedMapLogRepository.create(dbPool))
         ), options, res -> {
             if(res.succeeded()) {
                 logger.info("Deployment id is: {}", res.result());

+ 5 - 1
src/main/java/cz/senslog/telemetry/app/PropertyConfig.java

@@ -80,6 +80,9 @@ public final class PropertyConfig {
 
     private static class Auth {
 
+        public boolean getDisabled() {
+            return Boolean.parseBoolean(System.getenv("AUTH_DISABLED"));
+        }
         public String getKeyStorePath() {
             String user = System.getenv("AUTH_KEYSTORE_PATH");
             return Objects.requireNonNull(user, "System environmental variable 'AUTH_KEYSTORE_PATH' is not set.");
@@ -107,7 +110,8 @@ public final class PropertyConfig {
         return JsonObject.of(
                 "keystore.path", authConfig.getKeyStorePath(),
                 "keystore.type", authConfig.getKeyStoreType(),
-                "keystore.password", authConfig.getKeyStorePassword()
+                "keystore.password", authConfig.getKeyStorePassword(),
+                "disabled", authConfig.getDisabled()
         );
     }
 

+ 0 - 29
src/main/java/cz/senslog/telemetry/database/ConnectionPool.java

@@ -1,29 +0,0 @@
-package cz.senslog.telemetry.database;
-
-import io.vertx.core.Vertx;
-import io.vertx.core.json.JsonObject;
-import io.vertx.pgclient.PgBuilder;
-import io.vertx.pgclient.PgConnectOptions;
-import io.vertx.pgclient.PgPool;
-import io.vertx.sqlclient.Pool;
-import io.vertx.sqlclient.PoolOptions;
-import io.vertx.sqlclient.SqlConnectOptions;
-
-public final class ConnectionPool {
-
-    public static Pool createWithVertx(Vertx vertx, JsonObject config) {
-        JsonObject dbConfig = config.getJsonObject("db");
-
-        return PgBuilder.pool().with(new PoolOptions()
-                    .setMaxSize(dbConfig.getInteger("pool.size"))
-                    .setShared(true)
-                    .setName("maplog-db-pool"))
-                .connectingTo(new SqlConnectOptions()
-                    .setPort(dbConfig.getInteger("port"))
-                    .setHost(dbConfig.getString("host"))
-                    .setDatabase(dbConfig.getString("database"))
-                    .setUser(dbConfig.getString("user"))
-                    .setPassword(dbConfig.getString("password")))
-                .using(vertx).build();
-    }
-}

+ 0 - 279
src/main/java/cz/senslog/telemetry/database/repository/MockMapLogRepository.java

@@ -1,279 +0,0 @@
-package cz.senslog.telemetry.database.repository;
-
-import cz.senslog.telemetry.database.PagingRetrieve;
-import cz.senslog.telemetry.database.SortType;
-import cz.senslog.telemetry.database.domain.*;
-import cz.senslog.telemetry.database.domain.Filter;
-import io.vertx.core.Future;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.time.OffsetDateTime;
-import java.time.ZoneId;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class MockMapLogRepository implements SensLogRepository {
-
-    private static final Logger logger = LogManager.getLogger(MockMapLogRepository.class);
-
-    @Override
-    public Future<Integer> updateActionByDriverUnit(DriverAction data) {
-        return Future.succeededFuture(1);
-    }
-
-    public Future<Integer> saveTelemetry(UnitTelemetry data) {
-        logger.info("SAVE: timestamp {} | unit_id {} | observed_value {} | lon {} | lat {}",
-                data.getTimestamp(), data.getUnitId(), data.getObservedValues().toString(), data.getLocation().getLongitude(), data.getLocation().getLatitude());
-        return Future.succeededFuture(1);
-    }
-
-    public Future<Integer> saveAllTelemetry(List<UnitTelemetry> data) {
-        data.forEach(this::saveTelemetry);
-        return Future.succeededFuture(data.size());
-    }
-
-    @Override
-    public Future<Boolean> createSensor(Sensor sensor, long unitId) {
-        return Future.succeededFuture(true);
-    }
-
-    public Future<List<Unit>> allUnits() {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Unit> findUnitById(long unitId) {
-        return Future.succeededFuture(Unit.of(unitId, "mock unit", "imei", "description"));
-    }
-
-    public Future<Unit> findUnitByIMEI(String imei) {
-        return Future.succeededFuture(Unit.of(1000L, imei));
-    }
-
-    @Override
-    public Future<List<Sensor>> allSensors() {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Sensor> findSensorById(long sensorId) {
-        return Future.succeededFuture(Sensor.of(sensorId, "mock(name)", "mock(type)", -1, null, "mock(description)"));
-    }
-
-    @Override
-    public Future<List<Unit>> findUnitsBySensorId(long sensorId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Sensor> findSensorByIOAndUnitId(int ioID, long unitId) {
-        return Future.succeededFuture(Sensor.of(12345, "no name", ioID));
-    }
-
-    public Future<List<Campaign>> allCampaigns() {
-        return Future.succeededFuture(List.of(
-                Campaign.of(1L, "mock campaign", OffsetDateTime.MIN, OffsetDateTime.MAX)
-        ));
-    }
-
-    public Future<Campaign> findCampaignById(long campaignId) {
-        return Future.succeededFuture(Campaign.of(campaignId, "mock campaign", OffsetDateTime.MIN, OffsetDateTime.MAX));
-    }
-
-    public Future<List<Long>> findUnitIdsByCampaignId(long campaignId) {
-        return Future.succeededFuture(List.of(1L));
-    }
-
-    @Override
-    public Future<List<UnitTelemetry>> findObservationsByCampaignId(long campaignId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<PagingRetrieve<List<UnitTelemetry>>> findObservationsByCampaignIdWithPaging(long campaignId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(new PagingRetrieve<>(false, 0, Collections.emptyList()));
-    }
-
-    @Override
-    public Future<List<UnitTelemetry>> findObservationsByCampaignIdAndUnitId(long campaignId, long unitId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<PagingRetrieve<List<UnitTelemetry>>> findObservationsByCampaignIdAndUnitIdWithPaging(long campaignId, long unitId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(new PagingRetrieve<>(false, 0, Collections.emptyList()));
-    }
-
-    @Override
-    public Future<List<UnitTelemetry>> findObservationsByEventId(long eventId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<PagingRetrieve<List<UnitTelemetry>>> findObservationsByEventIdWithPaging(long eventId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(new PagingRetrieve<>(false, 0, Collections.emptyList()));
-    }
-
-    @Override
-    public Future<List<SensorTelemetry>> findObservationsByCampaignIdAndUnitIdAndSensorId(long campaignId, long unitId, long sensorId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<PagingRetrieve<List<SensorTelemetry>>> findObservationsByCampaignIdAndUnitIdAndSensorIdWithPaging(long campaignId, long unitId, long sensorId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(new PagingRetrieve<>(false, 0, Collections.emptyList()));
-    }
-
-    @Override
-    public Future<List<UnitLocation>> findLocationsByCampaignIdAndUnitId(long campaignId, long unitId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<PagingRetrieve<List<UnitLocation>>> findLocationsByCampaignIdAndUnitIdWithPaging(long campaignId, long unitId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(new PagingRetrieve<>(false, 0, Collections.emptyList()));
-    }
-
-    @Override
-    public Future<List<UnitLocation>> findLocationsByEventId(long eventId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<PagingRetrieve<List<UnitLocation>>> findLocationsByEventIdWithPaging(long eventId, OffsetDateTime from, OffsetDateTime to, ZoneId zone, int offset, int limit, List<Filter> filters) {
-        return Future.succeededFuture(new PagingRetrieve<>(false, 0, Collections.emptyList()));
-    }
-
-    @Override
-    public Future<Map<Long, Sensor>> findSensorsByUnitIdGroupById(long unitId) {
-        return Future.succeededFuture(Collections.emptyMap());
-    }
-
-    @Override
-    public Future<Map<Long, List<Sensor>>> findSensorsByCampaignIdGroupByUnitId(long campaignId) {
-        return Future.succeededFuture(Collections.emptyMap());
-    }
-
-    @Override
-    public Future<List<Sensor>> findSensorsByUnitId(long unitId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<List<Phenomenon>> allPhenomenons() {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Phenomenon> findPhenomenonById(long phenomenonId) {
-        return Future.succeededFuture(Phenomenon.of(phenomenonId, "mock(name)"));
-    }
-
-    @Override
-    public Future<List<Sensor>> findSensorsByPhenomenonId(long phenomenonId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<List<Sensor>> findSensorsByCampaignIdAndUnitId(long campaignId, long unitId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Sensor> findSensorByCampaignIdAndUnitId(long campaignId, long unitId, long sensorId) {
-        return Future.succeededFuture(Sensor.of(sensorId, "mock(name)", "mock(type)", -1, Phenomenon.of(-1, "mock(name)"), "mock(description)"));
-    }
-
-    @Override
-    public Future<List<Campaign>> findCampaignsByUnitId(long unitId, ZoneId zone) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<List<Driver>> allDrivers() {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Driver> findDriverById(int driverId) {
-        return Future.succeededFuture(Driver.of(driverId, "mock(name)"));
-    }
-
-    @Override
-    public Future<List<Driver>> findDriversByUnitId(long unitId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<List<Action>> findActionsByDriverIdAndUnitId(int driverId, long unitId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<List<Action>> findActionsByDriverId(int driverId, OffsetDateTime from, OffsetDateTime to) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Action> findActionByIdAndDriverId(int actionId, int driverId) {
-        return Future.succeededFuture(Action.of(actionId, "mock(name)"));
-    }
-
-    @Override
-    public Future<Action> findActionByIdAndDriverIdAndUnitId(int actionId, int driverId, long unitId) {
-        return Future.succeededFuture(Action.of(actionId, "mock(name)"));
-    }
-
-    @Override
-    public Future<List<Event>> findEventsByDriverIdAndUnitIdAndActionId(int driverId, long unitId, int actionId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Event> findEventById(long eventId) {
-        return Future.succeededFuture(Event.of(eventId, -1, -1, -1, null, null));
-    }
-
-    @Override
-    public Future<List<Unit>> findUnitsByDriverId(int driverId, OffsetDateTime from, OffsetDateTime to) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<List<CampaignUnit>> findUnitsByCampaignId(long campaignId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<Set<Long>> findUnitsIDByCampaignId(long campaignId) {
-        return Future.succeededFuture(Collections.emptySet());
-    }
-
-    @Override
-    public Future<CampaignUnit> findUnitByIdAndCampaignId(long unitId, long campaignId) {
-        return Future.succeededFuture(CampaignUnit.of(unitId, campaignId, "mock(name)", "mock(description)", OffsetDateTime.now(), OffsetDateTime.now().plusDays(1)));
-    }
-
-    @Override
-    public Future<Unit> findUnitByIdAndDriverId(long unitId, int driverId) {
-        return Future.succeededFuture(Unit.of(unitId, "mock(name)", "mock(imei)", "mock(description)"));
-    }
-
-    @Override
-    public Future<Unit> findUnitByIdAndDriverIdAndActionId(long unitId, int driverId, int actionId) {
-        return Future.succeededFuture(Unit.of(unitId, "mock(name)", "mock(imei)", "mock(description)"));
-    }
-
-    @Override
-    public Future<List<Unit>> findUnitsByDriverIdAndActionId(int driverId, int actionId) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-
-    @Override
-    public Future<List<UnitLocation>> findUnitsLocationsByCampaignId(long campaignId, int limitPerUnit, OffsetDateTime from, OffsetDateTime to, ZoneId zone, SortType sort, List<Filter> filters) {
-        return Future.succeededFuture(Collections.emptyList());
-    }
-}

+ 62 - 95
src/main/java/cz/senslog/telemetry/server/HttpVertxServer.java

@@ -1,8 +1,7 @@
 package cz.senslog.telemetry.server;
 
-import cz.senslog.telemetry.database.ConnectionPool;
-import cz.senslog.telemetry.database.repository.MapLogRepository;
 import cz.senslog.telemetry.database.repository.SensLogRepository;
+import cz.senslog.telemetry.server.ws.AuthorizationType;
 import cz.senslog.telemetry.server.ws.ExceptionHandler;
 import cz.senslog.telemetry.server.ws.OpenAPIHandler;
 import cz.senslog.telemetry.utils.ResourcesUtils;
@@ -15,31 +14,31 @@ import io.vertx.ext.auth.jwt.JWTAuth;
 import io.vertx.ext.auth.jwt.JWTAuthOptions;
 import io.vertx.ext.web.Router;
 import io.vertx.ext.web.handler.*;
+import io.vertx.ext.web.openapi.OpenAPILoaderOptions;
 import io.vertx.ext.web.openapi.RouterBuilder;
-import io.vertx.sqlclient.Pool;
-import io.vertx.sqlclient.RowSet;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.nio.file.Path;
+import java.util.Optional;
 
+import static cz.senslog.telemetry.server.ws.AuthorizationType.OAUTH;
 import static java.util.Objects.requireNonNull;
 
 public final class HttpVertxServer extends AbstractVerticle {
     private static final Logger logger = LogManager.getLogger(HttpVertxServer.class);
 
+    private final SensLogRepository repo;
+
+    public HttpVertxServer(SensLogRepository repo) {
+        this.repo = repo;
+    }
+
     @Override
     public void start(Promise<Void> startPromise) {
         Path openApiUrl = ResourcesUtils.getPath("openAPISpec.yaml");
         logger.info("Loading the OpenAPI spec from '{}'", openApiUrl);
 
-        Pool dbPool = ConnectionPool.createWithVertx(vertx, config());
-        dbPool.query("SELECT version()").execute().map(RowSet::iterator)
-                .map(it -> it.hasNext() ? it.next().getString(0) : null)
-                .onSuccess(version -> logger.info("Successful database connection to {}.", version))
-                .onFailure(startPromise::fail);
-        SensLogRepository repo = MapLogRepository.create(dbPool);
-
         RouterBuilder.create(vertx, requireNonNull(openApiUrl, "Open API Specification was not found as a resource.").toString())
                 .onSuccess(openAPIRouterBuilder -> {
                     logger.info("The OpenAPI specification was loaded successfully.");
@@ -57,14 +56,19 @@ public final class HttpVertxServer extends AbstractVerticle {
                             .allowedHeader("Accept")
                     );
 
-                    final String securitySchemaKey = "hubJwtAuth";
-
                     JsonObject authConfig = config().getJsonObject("auth");
-                    JWTAuthHandler authHandler = JWTAuthHandler.create(JWTAuth.create(vertx, new JWTAuthOptions()
-                            .setKeyStore(new KeyStoreOptions()
-                                    .setPath(authConfig.getString("keystore.path"))
-                                    .setType(authConfig.getString("keystore.type"))
-                                    .setPassword(authConfig.getString("keystore.password")))));
+                    if (authConfig.getBoolean("disabled")) {
+                        openAPIRouterBuilder.getOptions().setRequireSecurityHandlers(false);
+                    } else {
+                        JWTAuthHandler authHandler = JWTAuthHandler.create(JWTAuth.create(vertx, new JWTAuthOptions()
+                                .setKeyStore(new KeyStoreOptions()
+                                        .setPath(authConfig.getString("keystore.path"))
+                                        .setType(authConfig.getString("keystore.type"))
+                                        .setPassword(authConfig.getString("keystore.password")))));
+
+                        openAPIRouterBuilder.securityHandler(OAUTH.getSecuritySchemaKey(), authHandler);
+                    }
+
 
                     // The order matters, so adding the body handler should happen after any PLATFORM or SECURITY_POLICY handler(s).
                     openAPIRouterBuilder.rootHandler(BodyHandler.create());
@@ -73,85 +77,48 @@ public final class HttpVertxServer extends AbstractVerticle {
 
                     openAPIRouterBuilder.operation("infoGET").handler(apiHandler::info);
 
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignsGET").handler(apiHandler::campaignsGET);
-
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdGET").handler(apiHandler::campaignIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitsGET").handler(apiHandler::campaignIdUnitsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitsObservationsGET").handler(apiHandler::campaignIdUnitsObservationsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitsObservationsPOST").handler(apiHandler::campaignIdUnitsObservationsPOST);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitsObservationsLocationsGET").handler(apiHandler::campaignIdUnitsObservationsLocationsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitIdObservationsGET").handler(apiHandler::campaignIdUnitIdObservationsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitIdLocationsGET").handler(apiHandler::campaignIdUnitIdLocationsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitIdGET").handler(apiHandler::campaignIdUnitIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitIdSensorsGET").handler(apiHandler::campaignIdUnitIdSensorsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitIdSensorIdGET").handler(apiHandler::campaignIdUnitIdSensorIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("campaignIdUnitIdSensorIdObservationsGET").handler(apiHandler::campaignIdUnitIdSensorIdObservationsGET);
-
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("unitsGET").handler(apiHandler::unitsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("unitIdGET").handler(apiHandler::unitIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("unitIdSensorsGET").handler(apiHandler::unitIdSensorsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("unitIdCampaignsGET").handler(apiHandler::unitIdCampaignsGET);
+                    openAPIRouterBuilder.operation("campaignsGET").handler(apiHandler::campaignsGET);
+                    openAPIRouterBuilder.operation("campaignIdGET").handler(apiHandler::campaignIdGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitsGET").handler(apiHandler::campaignIdUnitsGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitsObservationsGET").handler(apiHandler::campaignIdUnitsObservationsGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitsObservationsPOST").handler(apiHandler::campaignIdUnitsObservationsPOST);
+                    openAPIRouterBuilder.operation("campaignIdUnitsObservationsLocationsGET").handler(apiHandler::campaignIdUnitsObservationsLocationsGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitIdObservationsGET").handler(apiHandler::campaignIdUnitIdObservationsGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitIdLocationsGET").handler(apiHandler::campaignIdUnitIdLocationsGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitIdGET").handler(apiHandler::campaignIdUnitIdGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitIdSensorsGET").handler(apiHandler::campaignIdUnitIdSensorsGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitIdSensorIdGET").handler(apiHandler::campaignIdUnitIdSensorIdGET);
+                    openAPIRouterBuilder.operation("campaignIdUnitIdSensorIdObservationsGET").handler(apiHandler::campaignIdUnitIdSensorIdObservationsGET);
+
+                    openAPIRouterBuilder.operation("unitsGET").handler(apiHandler::unitsGET);
+                    openAPIRouterBuilder.operation("unitIdGET").handler(apiHandler::unitIdGET);
+                    openAPIRouterBuilder.operation("unitIdSensorsGET").handler(apiHandler::unitIdSensorsGET);
+                    openAPIRouterBuilder.operation("unitIdCampaignsGET").handler(apiHandler::unitIdCampaignsGET);
                     openAPIRouterBuilder.operation("unitIdDriversGET").handler(apiHandler::unitIdDriversGET);
 
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("sensorsGET").handler(apiHandler::sensorsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("sensorIdGET").handler(apiHandler::sensorIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("sensorIdUnitsGET").handler(apiHandler::sensorIdUnitsGET);
-
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("phenomenonsGET").handler(apiHandler::phenomenonsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("phenomenonIdGET").handler(apiHandler::phenomenonIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("phenomenonIdSensorsGET").handler(apiHandler::phenomenonIdSensorsGET);
-
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driversGET").handler(apiHandler::driversGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdGET").handler(apiHandler::driverIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdUnitsGET").handler(apiHandler::driverIdUnitsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdUnitIdGET").handler(apiHandler::driverIdUnitIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdUnitIdActionsGET").handler(apiHandler::driverIdUnitIdActionsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdActionsGET").handler(apiHandler::driverIdActionsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdActionIdGET").handler(apiHandler::driverIdActionIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdActionIdUnitsGET").handler(apiHandler::driverIdActionIdUnitsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdActionIdUnitIdGET").handler(apiHandler::driverIdActionIdUnitIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdUnitIdActionIdGET").handler(apiHandler::driverIdUnitIdActionIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("driverIdUnitIdActionIdEventsGET").handler(apiHandler::driverIdUnitIdActionIdEventsGET);
-
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("eventIdGET").handler(apiHandler::eventIdGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("eventIdObservationsGET").handler(apiHandler::eventIdObservationsGET);
-                    openAPIRouterBuilder.securityHandler(securitySchemaKey, authHandler)
-                            .operation("eventIdLocationsGET").handler(apiHandler::eventIdLocationsGET);
+                    openAPIRouterBuilder.operation("sensorsGET").handler(apiHandler::sensorsGET);
+                    openAPIRouterBuilder.operation("sensorIdGET").handler(apiHandler::sensorIdGET);
+                    openAPIRouterBuilder.operation("sensorIdUnitsGET").handler(apiHandler::sensorIdUnitsGET);
+
+                    openAPIRouterBuilder.operation("phenomenonsGET").handler(apiHandler::phenomenonsGET);
+                    openAPIRouterBuilder.operation("phenomenonIdGET").handler(apiHandler::phenomenonIdGET);
+                    openAPIRouterBuilder.operation("phenomenonIdSensorsGET").handler(apiHandler::phenomenonIdSensorsGET);
+
+                    openAPIRouterBuilder.operation("driversGET").handler(apiHandler::driversGET);
+                    openAPIRouterBuilder.operation("driverIdGET").handler(apiHandler::driverIdGET);
+                    openAPIRouterBuilder.operation("driverIdUnitsGET").handler(apiHandler::driverIdUnitsGET);
+                    openAPIRouterBuilder.operation("driverIdUnitIdGET").handler(apiHandler::driverIdUnitIdGET);
+                    openAPIRouterBuilder.operation("driverIdUnitIdActionsGET").handler(apiHandler::driverIdUnitIdActionsGET);
+                    openAPIRouterBuilder.operation("driverIdActionsGET").handler(apiHandler::driverIdActionsGET);
+                    openAPIRouterBuilder.operation("driverIdActionIdGET").handler(apiHandler::driverIdActionIdGET);
+                    openAPIRouterBuilder.operation("driverIdActionIdUnitsGET").handler(apiHandler::driverIdActionIdUnitsGET);
+                    openAPIRouterBuilder.operation("driverIdActionIdUnitIdGET").handler(apiHandler::driverIdActionIdUnitIdGET);
+                    openAPIRouterBuilder.operation("driverIdUnitIdActionIdGET").handler(apiHandler::driverIdUnitIdActionIdGET);
+                    openAPIRouterBuilder.operation("driverIdUnitIdActionIdEventsGET").handler(apiHandler::driverIdUnitIdActionIdEventsGET);
+
+                    openAPIRouterBuilder.operation("eventIdGET").handler(apiHandler::eventIdGET);
+                    openAPIRouterBuilder.operation("eventIdObservationsGET").handler(apiHandler::eventIdObservationsGET);
+                    openAPIRouterBuilder.operation("eventIdLocationsGET").handler(apiHandler::eventIdLocationsGET);
 
                     Router mainRouter = openAPIRouterBuilder.createRouter();
 

+ 5 - 5
src/main/java/cz/senslog/telemetry/server/TCPVertxServer.java

@@ -1,14 +1,11 @@
 package cz.senslog.telemetry.server;
 
-import cz.senslog.telemetry.database.ConnectionPool;
-import cz.senslog.telemetry.database.repository.CachedMapLogRepository;
 import cz.senslog.telemetry.database.repository.SensLogRepository;
 import io.vertx.core.AbstractVerticle;
 import io.vertx.core.Promise;
 import io.vertx.core.json.JsonObject;
 import io.vertx.core.net.NetServer;
 import io.vertx.core.net.NetServerOptions;
-import io.vertx.sqlclient.Pool;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -16,14 +13,17 @@ public final class TCPVertxServer extends AbstractVerticle {
 
     private static final Logger logger = LogManager.getLogger(TCPVertxServer.class);
 
+    private final SensLogRepository repo;
+
+    public TCPVertxServer(SensLogRepository repo) {
+        this.repo = repo;
+    }
 
     @Override
     public void start(Promise<Void> startPromise) {
         NetServerOptions serverOpt = new NetServerOptions().setRegisterWriteHandler(true);
         NetServer server = vertx.createNetServer(serverOpt);
 
-        Pool dbPool = ConnectionPool.createWithVertx(vertx, config());
-        SensLogRepository repo = CachedMapLogRepository.create(dbPool);
         Fm4exSocketHandler socHandler = Fm4exSocketHandler.create(repo);
         server.connectHandler(socket -> socket.handler(
                         buffer -> socHandler.process(socket.writeHandlerID(), buffer)

+ 16 - 0
src/main/java/cz/senslog/telemetry/server/ws/AuthorizationType.java

@@ -0,0 +1,16 @@
+package cz.senslog.telemetry.server.ws;
+
+public enum AuthorizationType {
+    OAUTH   ("bearerAuth")
+
+    ;
+    private final String securitySchemaKey;
+
+    AuthorizationType(String securitySchemaKey) {
+        this.securitySchemaKey = securitySchemaKey;
+    }
+
+    public String getSecuritySchemaKey() {
+        return securitySchemaKey;
+    }
+}

+ 12 - 0
src/main/java/cz/senslog/telemetry/server/ws/ContentType.java

@@ -27,4 +27,16 @@ public enum ContentType {
     public String contentType() {
         return contentType;
     }
+
+    public static String[] contentTypes() {
+        return contentTypes(values());
+    }
+
+    public static String[] contentTypes(ContentType... types) {
+        String[] values = new String[types.length];
+        for (ContentType value : types) {
+            values[value.ordinal()] = value.contentType;
+        }
+        return values;
+    }
 }

+ 72 - 0
src/main/java/cz/senslog/telemetry/server/ws/ContentTypeHandler.java

@@ -0,0 +1,72 @@
+package cz.senslog.telemetry.server.ws;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public final class ContentTypeHandler {
+
+    private static Set<ContentType> parseContentTypes(String contentTypes) {
+        if (contentTypes == null || contentTypes.isBlank()) {
+            return Collections.emptySet();
+        }
+        String[] types = contentTypes.split(",");
+        Set<ContentType> res = new HashSet<>(ContentType.values().length);
+        for (String type : types) {
+            String mediaType = type.split(";")[0];
+            for (ContentType value : ContentType.values()) {
+                if (mediaType.equalsIgnoreCase(value.contentType())) {
+                    res.add(value);
+                }
+            }
+        }
+        return res;
+    }
+
+    public static ContentTypeHandler createWithSupportedTypes(ContentType... supportedTypes) {
+        return new ContentTypeHandler(supportedTypes);
+    }
+    
+    private final ContentType[] supportedTypes;
+
+    private ContentTypeHandler(ContentType[] supportedTypes) {
+        this.supportedTypes = supportedTypes;
+    }
+    
+    public FluentHandler accept(String accept) {
+        if (accept == null || accept.isBlank()) {
+            throw new HttpIllegalArgumentException(400, "The Media Type is missing. Add 'Accept' attribute in header.");
+        }
+
+        Set<ContentType> parsedTypes = parseContentTypes(accept);
+        Set<ContentType> contentTypes = new HashSet<>();
+        for (ContentType supportedType : supportedTypes) {
+            if (parsedTypes.contains(supportedType)) {
+                contentTypes.add(supportedType);
+            }
+        }
+        if (contentTypes.isEmpty()) {
+            throw new HttpIllegalArgumentException(415,
+                    String.format("The Media Type '%s' is not supported. Supported are %s.", accept, Arrays.toString(ContentType.contentTypes(supportedTypes))));
+        }
+        return new FluentHandler(contentTypes);
+    }
+
+    public static final class FluentHandler {
+        private final Set<ContentType> contentTypes;
+
+        private FluentHandler(Set<ContentType> contentTypes) {
+            this.contentTypes = contentTypes;
+        }
+
+        public FluentHandler handle(ContentType contentType, Consumer<ContentType> handler) {
+            if (contentTypes.contains(contentType) && handler != null) {
+                handler.accept(contentType);
+            }
+            return this;
+        }
+    }
+}

+ 3 - 1
src/main/java/cz/senslog/telemetry/server/ws/ExceptionHandler.java

@@ -27,6 +27,8 @@ public final class ExceptionHandler implements io.vertx.core.Handler<RoutingCont
             return  404;
         } else if (throwable instanceof ParameterProcessorException) {
             return  400;
+        } else if (throwable instanceof HttpIllegalArgumentException) {
+            return ((HttpIllegalArgumentException)throwable).getCode();
         }
         return defaultCode;
     }
@@ -41,7 +43,7 @@ public final class ExceptionHandler implements io.vertx.core.Handler<RoutingCont
         logger.error(message);
         rc.response()
                 .putHeader(CONTENT_TYPE, ContentType.JSON.contentType())
-                .setStatusCode(rc.statusCode())
+                .setStatusCode(code)
                 .end(JsonObject.of(
                         "code", code,
                         "message", message

+ 20 - 0
src/main/java/cz/senslog/telemetry/server/ws/HttpIllegalArgumentException.java

@@ -0,0 +1,20 @@
+package cz.senslog.telemetry.server.ws;
+
+public class HttpIllegalArgumentException extends IllegalArgumentException {
+
+    private final int code;
+
+    public HttpIllegalArgumentException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+
+    public HttpIllegalArgumentException(int code, Throwable th) {
+        super(th);
+        this.code = code;
+    }
+
+    public int getCode() {
+        return code;
+    }
+}

+ 13 - 10
src/main/java/cz/senslog/telemetry/server/ws/OpenAPIHandler.java

@@ -7,7 +7,6 @@ import cz.senslog.telemetry.database.domain.UnitLocation;
 import cz.senslog.telemetry.database.domain.UnitTelemetry;
 import cz.senslog.telemetry.database.repository.SensLogRepository;
 import cz.senslog.telemetry.database.domain.Filter;
-import cz.senslog.telemetry.utils.ListUtils;
 import cz.senslog.telemetry.utils.TernaryCondition;
 import io.vertx.core.http.HttpServerRequest;
 import io.vertx.core.json.JsonArray;
@@ -27,13 +26,13 @@ import static cz.senslog.telemetry.server.ws.ContentType.JSON;
 import static cz.senslog.telemetry.utils.FluentInvoke.fluentlyOf;
 import static cz.senslog.telemetry.utils.ListUtils.*;
 import static cz.senslog.telemetry.utils.TernaryCondition.ternaryIf;
+import static io.vertx.core.http.HttpHeaders.ACCEPT;
 import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE;
 import static java.lang.Boolean.parseBoolean;
 import static java.time.OffsetDateTime.ofInstant;
 import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 import static java.util.Collections.emptyList;
 import static java.util.Comparator.comparing;
-import static java.util.Optional.ofNullable;
 import static java.util.stream.Collectors.groupingBy;
 import static java.util.stream.Collectors.toList;
 
@@ -58,18 +57,22 @@ public class OpenAPIHandler {
     }
 
     private static String hostURLFull(HttpServerRequest req) {
-        return String.format("%s://%s", req.scheme(), req.host());
+        return String.format("%s://%s", req.scheme(), req.authority().host());
     }
 
 
     public void info(RoutingContext rc) {
-        rc.response().end(JsonObject.of(
-                    "name", Application.PROJECT_NAME,
-                    "version", Application.COMPILED_VERSION,
-                    "build", Application.BUILD_VERSION,
-                    "uptime", Application.uptimeFormatted(),
-                    "uptimeMillis", Application.uptime()
-            ).encode());
+        ContentTypeHandler.createWithSupportedTypes(JSON)
+                .accept(rc.request().getHeader(ACCEPT))
+                .handle(JSON, type -> rc.response().putHeader(CONTENT_TYPE, type.contentType())
+                        .end(JsonObject.of(
+                            "name", Application.PROJECT_NAME,
+                            "version", Application.COMPILED_VERSION,
+                            "build", Application.BUILD_VERSION,
+                            "uptime", Application.uptimeFormatted(),
+                            "uptimeMillis", Application.uptime(),
+                            "authType", AuthorizationType.OAUTH.name()
+                        ).encode()));
     }
 
     public void campaignsGET(RoutingContext rc) {

+ 0 - 12
src/main/java/cz/senslog/telemetry/server/ws/ParamParseException.java

@@ -1,12 +0,0 @@
-package cz.senslog.telemetry.server.ws;
-
-public class ParamParseException extends IllegalArgumentException {
-
-    public ParamParseException(String message) {
-        super(message);
-    }
-
-    public ParamParseException(Throwable th) {
-        super(th);
-    }
-}

+ 41 - 40
src/main/resources/openAPISpec.yaml

@@ -29,7 +29,7 @@ paths:
       operationId: campaignsGET
       summary: Publish info about all campaigns
       security:
-        - hubJwtAuth: [telemetry:read]
+        - bearerAuth: [telemetry:read]
       parameters:
         - $ref: '#/components/parameters/zoneParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -54,7 +54,7 @@ paths:
       operationId: campaignIdGET
       summary: Publish info about a campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/zoneParam'
@@ -78,7 +78,7 @@ paths:
       operationId: campaignIdUnitsGET
       summary: Publish info about the campaign's units
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/zoneParam'
@@ -104,7 +104,7 @@ paths:
       operationId: campaignIdUnitsObservationsGET
       summary: Publish info about all data of units merged together within the campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/fromParam'
@@ -134,7 +134,7 @@ paths:
     post:
       operationId: campaignIdUnitsObservationsPOST
       security:
-        - hubJwtAuth: [ telemetry:write ]
+        - bearerAuth: [ telemetry:write ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
       requestBody:
@@ -167,7 +167,7 @@ paths:
       operationId: campaignIdUnitsObservationsLocationsGET
       summary: Publish info about all data of units merged together within the campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/limitPerUnitParam'
@@ -200,7 +200,7 @@ paths:
       operationId: campaignIdUnitIdGET
       summary: Publish info about the unit within its campaign's scope
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -225,7 +225,7 @@ paths:
       operationId: campaignIdUnitIdObservationsGET
       summary: Publish info about all data of the unit within the campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -259,7 +259,7 @@ paths:
       operationId: campaignIdUnitIdLocationsGET
       summary: Publish locations of the unit within the campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -293,7 +293,7 @@ paths:
       operationId: campaignIdUnitIdSensorsGET
       summary: Publish info about all sensors of the unit within the campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -319,7 +319,7 @@ paths:
       operationId: campaignIdUnitIdSensorIdGET
       summary: Publish info about all sensors associated with the unit and the campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -344,7 +344,7 @@ paths:
       operationId: campaignIdUnitIdSensorIdObservationsGET
       summary: Publish info about all data of the unit within the campaign
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/campaignIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -379,7 +379,7 @@ paths:
       operationId: unitsGET
       summary: Publish info about all units
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/navigationLinksParam'
       responses:
@@ -403,7 +403,7 @@ paths:
       operationId: unitIdGET
       summary: Publish info about the unit
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/unitIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -426,7 +426,7 @@ paths:
       operationId: unitIdSensorsGET
       summary: Publish info about sensors assigned to the unit
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/unitIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -451,7 +451,7 @@ paths:
       operationId: unitIdCampaignsGET
       summary: Publish info about campaigns where the unit was/is assigned
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/unitIdParam'
         - $ref: '#/components/parameters/zoneParam'
@@ -477,7 +477,7 @@ paths:
       operationId: unitIdDriversGET
       summary: Publish basic info about drivers who performed actions upon the unit
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/unitIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -502,7 +502,7 @@ paths:
       operationId: sensorsGET
       summary: Publish info about all sensors
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/navigationLinksParam'
       responses:
@@ -526,7 +526,7 @@ paths:
       operationId: sensorIdGET
       summary: Publish info about the sensor
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/sensorIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -549,7 +549,7 @@ paths:
       operationId: sensorIdUnitsGET
       summary: Publish info about units to whom the sensor is assigned
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/sensorIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -575,7 +575,7 @@ paths:
       operationId: phenomenonsGET
       summary: Publish info about all phenomenons
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/navigationLinksParam'
       responses:
@@ -599,7 +599,7 @@ paths:
       operationId: phenomenonIdGET
       summary: Publish info about the phenomenon
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/phenomenonIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -622,7 +622,7 @@ paths:
       operationId: phenomenonIdSensorsGET
       summary: Publish info about sensors of the phenomenon
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/phenomenonIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -647,7 +647,7 @@ paths:
       operationId: driversGET
       summary: Publish basic info about all drivers
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/navigationLinksParam'
       responses:
@@ -671,7 +671,7 @@ paths:
       operationId: driverIdGET
       summary: Publish detailed info about the driver
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/navigationLinksParam'
@@ -694,7 +694,7 @@ paths:
       operationId: driverIdUnitsGET
       summary: Publish basic info about driver's units
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/fromParam'
@@ -721,7 +721,7 @@ paths:
       operationId: driverIdUnitIdGET
       summary: Publish detailed info about driver's unit
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -745,7 +745,7 @@ paths:
       operationId: driverIdUnitIdActionsGET
       summary: Publish basic info actions performed on the unit by the driver
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -771,7 +771,7 @@ paths:
       operationId: driverIdActionsGET
       summary: Publish basic info about driver's actions
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/fromParam'
@@ -798,7 +798,7 @@ paths:
       operationId: driverIdActionIdGET
       summary: Publish detailed info about the driver's action
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/actionIdParam'
@@ -822,7 +822,7 @@ paths:
       operationId: driverIdActionIdUnitsGET
       summary: Publish basic info about units on which the driver performed its action
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/actionIdParam'
@@ -848,7 +848,7 @@ paths:
       operationId: driverIdActionIdUnitIdGET
       summary: Publish detail info about the unit on which the driver performed the action
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/actionIdParam'
@@ -873,7 +873,7 @@ paths:
       operationId: driverIdUnitIdActionIdGET
       summary: Publish detailed info about the action performed on the unit by the driver
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -898,7 +898,7 @@ paths:
       operationId: driverIdUnitIdActionIdEventsGET
       summary: Publish basic info about events that where performed on the unit byt the driver with the specific action
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/driverIdParam'
         - $ref: '#/components/parameters/unitIdParam'
@@ -927,7 +927,7 @@ paths:
       operationId: eventIdGET
       summary: Publish basic info about events that where performed on the unit byt the driver with the specific action
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/eventIdParam'
         - $ref: '#/components/parameters/zoneParam'
@@ -951,7 +951,7 @@ paths:
       operationId: eventIdObservationsGET
       summary: Publish telemetry observations created by the driver while performing specific action on the unit at the time/event
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/eventIdParam'
         - $ref: '#/components/parameters/zoneParam'
@@ -982,7 +982,7 @@ paths:
       operationId: eventIdLocationsGET
       summary: Publish locations created by the driver while performing specific action on the unit at the time/event
       security:
-        - hubJwtAuth: [ telemetry:read ]
+        - bearerAuth: [ telemetry:read ]
       parameters:
         - $ref: '#/components/parameters/eventIdParam'
         - $ref: '#/components/parameters/fromParam'
@@ -1012,7 +1012,7 @@ paths:
 
 components:
   securitySchemes:
-    hubJwtAuth:
+    bearerAuth:
       type: http
       scheme: bearer
       bearerFormat: JWT
@@ -2017,7 +2017,7 @@ components:
           items:
             type: array
             items:
-              type: integer
+              type: number
               format: float
               minLength: 3
               maxLength: 3
@@ -2423,7 +2423,8 @@ components:
           format: int64
         ioId:
           type: integer
-          format: int16
+          format: int32
+          minimum: 1
         name:
           type: string
         phenomenon:

+ 11 - 2
src/test/java/cz/senslog/telemetry/database/repository/MapLogRepositoryTest.java

@@ -11,6 +11,8 @@ import io.vertx.core.json.JsonObject;
 import io.vertx.junit5.VertxExtension;
 import io.vertx.junit5.VertxTestContext;
 import io.vertx.sqlclient.Pool;
+import io.vertx.sqlclient.PoolOptions;
+import io.vertx.sqlclient.SqlConnectOptions;
 import org.junit.jupiter.api.*;
 import org.junit.jupiter.api.extension.ExtendWith;
 import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
@@ -23,7 +25,6 @@ import java.time.ZoneOffset;
 import java.util.Map;
 import java.util.Properties;
 
-import static cz.senslog.telemetry.database.ConnectionPool.createWithVertx;
 import static org.assertj.core.api.Assertions.assertThat;
 
 @ExtendWith({VertxExtension.class, SystemStubsExtension.class})
@@ -47,7 +48,15 @@ class MapLogRepositoryTest {
             envVariable.set(propEntry.getKey(), propEntry.getValue());
         }
 
-        dbPool = createWithVertx(vertx, JsonObject.of("db", PropertyConfig.getInstance().db()));
+        JsonObject dbConfig = PropertyConfig.getInstance().db();
+        dbPool = Pool.pool(new SqlConnectOptions()
+                .setPort(dbConfig.getInteger("port"))
+                .setHost(dbConfig.getString("host"))
+                .setDatabase(dbConfig.getString("database"))
+                .setUser(dbConfig.getString("user"))
+                .setPassword(dbConfig.getString("password")), new PoolOptions()
+                .setMaxSize(dbConfig.getInteger("pool.size")));
+
         repo = MapLogRepository.create(dbPool);
 
         testContext.completeNow();

+ 0 - 84
src/test/java/cz/senslog/telemetry/server/OpenAPIHandlerTest.java

@@ -1,84 +0,0 @@
-package cz.senslog.telemetry.server;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.vertx.core.DeploymentOptions;
-import io.vertx.core.Vertx;
-import io.vertx.core.http.HttpClient;
-import io.vertx.core.http.HttpClientResponse;
-import io.vertx.core.http.HttpMethod;
-import io.vertx.core.json.JsonObject;
-import io.vertx.junit5.VertxExtension;
-import io.vertx.junit5.VertxTestContext;
-import org.junit.jupiter.api.*;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.openapi4j.core.exception.ResolutionException;
-import org.openapi4j.core.validation.ValidationException;
-import org.openapi4j.parser.OpenApi3Parser;
-import org.openapi4j.parser.model.v3.OpenApi3;
-import org.openapi4j.parser.model.v3.Schema;
-import org.openapi4j.schema.validator.ValidationContext;
-import org.openapi4j.schema.validator.ValidationData;
-import org.openapi4j.schema.validator.v3.SchemaValidator;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-@ExtendWith(VertxExtension.class)
-class OpenAPIHandlerTest {
-
-    private static final int PORT = 8080;
-    private static final String HOST = "localhost";
-    private static OpenApi3 OPEN_API;
-
-    private final ObjectMapper mapper = new ObjectMapper();
-
-    @BeforeAll
-    static void setUp() throws MalformedURLException, ResolutionException, ValidationException {
-        OPEN_API = new OpenApi3Parser().parse(new URL("file:src/main/resources/openAPISpec.yaml"), true);
-    }
-
-    @AfterAll
-    static void tearDown() {
-    }
-
-    @BeforeEach
-    @DisplayName("Deploy the HTTP Server verticle")
-    void deploy_verticle(Vertx vertx, VertxTestContext testContext) {
-        vertx.deployVerticle(new HttpVertxServer(), new DeploymentOptions().setConfig(JsonObject.of(
-                "server.http.port", PORT
-        )), testContext.succeedingThenComplete());
-    }
-
-    @AfterEach
-    @DisplayName("Check that the verticle is still there")
-    void lastChecks(Vertx vertx) {
-        assertThat(vertx.deploymentIDs())
-                .isNotEmpty()
-                .hasSize(1);
-    }
-
-    @Test
-    @DisplayName("Test: /info")
-    void info(Vertx vertx, VertxTestContext testContext) {
-        HttpClient client = vertx.createHttpClient();
-        client.request(HttpMethod.GET, PORT, HOST, "/info")
-                .compose(req -> req.send().compose(HttpClientResponse::body))
-                .onComplete(testContext.succeeding(buffer -> testContext.verify(() -> {
-                    JsonNode contentNode = mapper.readTree(buffer.toString());
-
-                    Schema schema = OPEN_API.getComponents().getSchema( "Info");
-                    JsonNode schemaNode = schema.toNode();
-                    SchemaValidator schemaValidator = new SchemaValidator(new ValidationContext<>(OPEN_API.getContext()), null, schemaNode);
-                    ValidationData<Void> validation = new ValidationData<>();
-                    schemaValidator.validate(contentNode, validation);
-                    assertThat(validation.isValid()).overridingErrorMessage(validation.results().toString()).isTrue();
-
-                    testContext.completeNow();
-                })));
-    }
-
-
-}

+ 144 - 0
src/test/java/cz/senslog/telemetry/server/ws/OpenAPIHandlerTest.java

@@ -0,0 +1,144 @@
+package cz.senslog.telemetry.server.ws;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import cz.senslog.telemetry.TestPropertiesUtils;
+import cz.senslog.telemetry.app.PropertyConfig;
+import cz.senslog.telemetry.database.domain.Campaign;
+import cz.senslog.telemetry.database.repository.SensLogRepository;
+import cz.senslog.telemetry.server.HttpVertxServer;
+import io.vertx.core.*;
+import io.vertx.core.http.HttpClientResponse;
+import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.RequestOptions;
+import io.vertx.core.json.JsonObject;
+import io.vertx.junit5.VertxExtension;
+import io.vertx.junit5.VertxTestContext;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+import org.openapi4j.core.exception.EncodeException;
+import org.openapi4j.core.exception.ResolutionException;
+import org.openapi4j.core.validation.ValidationException;
+import org.openapi4j.parser.OpenApi3Parser;
+import org.openapi4j.parser.model.v3.OpenApi3;
+import org.openapi4j.parser.model.v3.Operation;
+import org.openapi4j.parser.model.v3.Schema;
+import org.openapi4j.schema.validator.ValidationContext;
+import org.openapi4j.schema.validator.ValidationData;
+import org.openapi4j.schema.validator.v3.SchemaValidator;
+import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
+import uk.org.webcompere.systemstubs.jupiter.SystemStub;
+import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import static cz.senslog.telemetry.server.ws.ContentType.JSON;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith({VertxExtension.class, SystemStubsExtension.class})
+class OpenAPIHandlerTest {
+
+    private static final int PORT = 8080;
+    private static final String HOST = "localhost";
+    private static OpenApi3 OPEN_API;
+
+    @SystemStub
+    private EnvironmentVariables envVariable;
+
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+    private static void validateSuccess(String bodyResponse, Operation operation, ContentType contentType) throws EncodeException, JsonProcessingException {
+        JsonNode responseResult = mapper.readTree(bodyResponse);
+        Schema schema = operation.getResponse("200").getContentMediaType(contentType.contentType()).getSchema();
+        JsonNode schemaNode = schema.toNode();
+        SchemaValidator schemaValidator = new SchemaValidator(new ValidationContext<>(OPEN_API.getContext()), null, schemaNode);
+        ValidationData<Void> validation = new ValidationData<>();
+        schemaValidator.validate(responseResult, validation);
+        assertThat(validation.isValid()).overridingErrorMessage(validation.results().toString()).isTrue();
+    }
+
+    @BeforeAll
+    static void setUp() throws MalformedURLException, ResolutionException, ValidationException {
+        OPEN_API = new OpenApi3Parser().parse(new URL("file:src/main/resources/openAPISpec.yaml"), false);
+    }
+
+    @AfterAll
+    static void tearDown() {
+    }
+
+    @BeforeEach
+    @DisplayName("Deploy the HTTP Server verticle")
+    void deploy_verticle(Vertx vertx, VertxTestContext testContext) {
+        Properties props = TestPropertiesUtils.loadFromResources("tests.junit.env");
+        for (Map.Entry<Object, Object> propEntry : props.entrySet()) {
+            envVariable.set(propEntry.getKey(), propEntry.getValue());
+        }
+
+        SensLogRepository repo = Mockito.mock(SensLogRepository.class);
+
+        OffsetDateTime baseTimestamp = OffsetDateTime.of(LocalDateTime.of(2023, 1, 1, 0, 0), ZoneOffset.UTC);
+
+        Mockito.when(repo.allCampaigns()).thenReturn(Future.succeededFuture(List.of(
+                Campaign.of(1, "mock(description)", baseTimestamp, baseTimestamp.plusYears(1)),
+                Campaign.of(2, "mock(description)", baseTimestamp, baseTimestamp.plusYears(2))
+        )));
+
+        PropertyConfig config = PropertyConfig.getInstance();
+        vertx.deployVerticle(new HttpVertxServer(repo), new DeploymentOptions().setConfig(JsonObject.of(
+                "server", config.server(),
+                "auth", config.auth()
+        )), testContext.succeedingThenComplete());
+    }
+
+    @AfterEach
+    @DisplayName("Check that the verticle is still there")
+    void lastChecks(Vertx vertx) {
+        assertThat(vertx.deploymentIDs())
+                .isNotEmpty()
+                .hasSize(1);
+    }
+
+    @Test
+    @DisplayName("API Test: infoGET")
+    void infoGET(Vertx vertx, VertxTestContext testContext) {
+        Operation operation = OPEN_API.getOperationById("infoGET");
+        RequestOptions reqOpt = new RequestOptions()
+                .setMethod(HttpMethod.GET).setPort(PORT).setHost(HOST).setURI("/info")
+                .setHeaders(MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.ACCEPT, JSON.contentType()));
+
+        vertx.createHttpClient().request(reqOpt)
+                .compose(req -> req.send().compose(HttpClientResponse::body))
+                .onComplete(testContext.succeeding(b -> testContext.verify(() -> {
+                    validateSuccess(b.toString(), operation, JSON);
+                    testContext.completeNow();
+                })));
+    }
+
+    @Test
+    @DisplayName("API Test: campaignsGET")
+    void campaignsGET(Vertx vertx, VertxTestContext testContext) {
+        Operation operation = OPEN_API.getOperationById("campaignsGET");
+        RequestOptions reqOpt = new RequestOptions()
+                .setMethod(HttpMethod.GET).setPort(PORT).setHost(HOST).setURI("/campaigns")
+                .setHeaders(MultiMap.caseInsensitiveMultiMap()
+                        .add(HttpHeaders.ACCEPT, JSON.contentType()));
+
+        vertx.createHttpClient().request(reqOpt)
+                .compose(req -> req.send().compose(HttpClientResponse::body))
+                .onComplete(testContext.succeeding(b -> testContext.verify(() -> {
+                    validateSuccess(b.toString(), operation, JSON);
+                    testContext.completeNow();
+                })));
+    }
+
+}

+ 7 - 1
src/test/resources/tests.junit.env

@@ -8,4 +8,10 @@ DATABASE_PORT=5432
 DATABASE_NAME=maplog
 DATABASE_USER=maplog_app
 DATABASE_PASSWORD=MAPlog
-DATABASE_POOL_SIZE=5
+DATABASE_POOL_SIZE=5
+
+# Auth
+AUTH_DISABLED=true
+AUTH_KEYSTORE_PATH=keystore.jceks
+AUTH_KEYSTORE_TYPE=PKCS12
+AUTH_KEYSTORE_PASSWORD=SENSlog