Kaynağa Gözat

Added API 'campaignIdUnitIdGET'

Lukas Cerny 1 yıl önce
ebeveyn
işleme
8549a01273

+ 24 - 5
src/main/java/cz/senslog/telemetry/database/domain/CampaignUnit.java

@@ -5,18 +5,25 @@ import java.time.OffsetDateTime;
 
 public class CampaignUnit {
 
-    private final long unitId;
-    private final String description, name;
+    private final long unitId, campaignId;
+    private final String description, name, type, imei;
     private final OffsetDateTime fromTime, toTime;
 
 
-    public static CampaignUnit of(long unitId, String name, String description, OffsetDateTime fromTime, OffsetDateTime toTime) {
-        return new CampaignUnit(unitId, name, description, fromTime, toTime);
+    public static CampaignUnit of(long unitId, long campaignId, String name, String type, String imei, String description, OffsetDateTime fromTime, OffsetDateTime toTime) {
+        return new CampaignUnit(unitId, campaignId, name, type, imei, description, fromTime, toTime);
     }
 
-    private CampaignUnit(long unitId, String name, String description, OffsetDateTime fromTime, OffsetDateTime toTime) {
+    public static CampaignUnit of(long unitId, long campaignId, String name, String description, OffsetDateTime fromTime, OffsetDateTime toTime) {
+        return new CampaignUnit(unitId, campaignId, name, null, null, description, fromTime, toTime);
+    }
+
+    private CampaignUnit(long unitId, long campaignId, String name, String type, String imei, String description, OffsetDateTime fromTime, OffsetDateTime toTime) {
         this.unitId = unitId;
+        this.campaignId = campaignId;
         this.name = name;
+        this.type = type;
+        this.imei = imei;
         this.description = description;
         this.fromTime = fromTime;
         this.toTime = toTime;
@@ -30,6 +37,14 @@ public class CampaignUnit {
         return name;
     }
 
+    public String getType() {
+        return type;
+    }
+
+    public String getImei() {
+        return imei;
+    }
+
     public String getDescription() {
         return description;
     }
@@ -41,4 +56,8 @@ public class CampaignUnit {
     public OffsetDateTime getToTime() {
         return toTime;
     }
+
+    public long getCampaignId() {
+        return campaignId;
+    }
 }

+ 44 - 18
src/main/java/cz/senslog/telemetry/database/repository/MapLogRepository.java

@@ -272,51 +272,77 @@ public class MapLogRepository implements SensLogRepository {
                 .onFailure(logger::catching);
     }
 
-    private static final Function<Row, CampaignUnit> ROW_TO_CAMPAIGN_UNIT = (row) -> CampaignUnit.of(
-            row.getLong("unit_id"),
-            row.getString("name"),
-            row.getString("description"),
-            row.getOffsetDateTime("from_time"),
-            row.getOffsetDateTime("to_time")
-    );
-
     @Override
     public Future<List<CampaignUnit>> findUnitsByCampaignId(long campaignId) {
-        return client.preparedQuery("SELECT u.unit_id, u.name, u.description, utc.from_time, utc.to_time " +
+        return client.preparedQuery("SELECT u.unit_id, utc.camp_id, u.name, u.imei, u.unit_type_id, u.description, utc.from_time, utc.to_time " +
                         "FROM maplog.unit_to_campaign AS utc " +
                         "JOIN maplog.campaign c on c.campaign_id = utc.camp_id " +
                         "JOIN maplog.unit u on u.unit_id = utc.unit_id " +
                         "WHERE utc.camp_id = $1")
                 .execute(Tuple.of(campaignId))
                 .map(rs -> StreamSupport.stream(rs.spliterator(), false)
-                        .map(ROW_TO_CAMPAIGN_UNIT)
+                        .map((row) -> CampaignUnit.of(
+                                row.getLong("unit_id"),
+                                row.getLong("camp_id"),
+                                row.getString("name"),
+                                row.getString("description"),
+                                row.getOffsetDateTime("from_time"),
+                                row.getOffsetDateTime("to_time")
+                        ))
                         .collect(Collectors.toList()));
     }
 
+    private static final Function<Row, CampaignUnit> ROW_TO_CAMPAIGN_UNIT = (row) -> CampaignUnit.of(
+            row.getLong("unit_id"),
+            row.getLong("camp_id"),
+            row.getString("name"),
+            row.getString("unit_type_id"),
+            row.getString("imei"),
+            row.getString("description"),
+            row.getOffsetDateTime("from_time"),
+            row.getOffsetDateTime("to_time")
+    );
+
+    @Override
+    public Future<CampaignUnit> findUnitByIdAndCampaignId(long campaignId, long unitId) {
+        return client.preparedQuery("SELECT u.unit_id, utc.camp_id, u.name, u.imei, u.unit_type_id, u.description, utc.from_time, utc.to_time " +
+                        "FROM maplog.unit AS u " +
+                        "JOIN maplog.unit_to_campaign utc on u.unit_id = utc.unit_id " +
+                        "WHERE utc.camp_id = $1 AND utc.unit_id = $2")
+                .execute(Tuple.of(campaignId, unitId))
+                .map(RowSet::iterator)
+                .map(iterator -> iterator.hasNext() ? ROW_TO_CAMPAIGN_UNIT.apply(iterator.next()) : null);
+    }
+
     @Override
     public Future<Map<Long, Sensor>> findSensorsByUnitIdGroupById(long unitId) {
         return findSensorsByUnitId(unitId).map(sensors -> sensors.stream()
                 .collect(Collectors.toMap(Sensor::getSensorId, Function.identity())));
     }
 
-    private static final Function<Row, Campaign> ROW_TO_CAMPAIGN = (row) -> Campaign.of(
-            row.getLong("campaign_id"),
-            row.getString("description"),
-            row.getOffsetDateTime("from_time"),
-            row.getOffsetDateTime("to_time")
-        );
-
     @Override
     public Future<List<Campaign>> allCampaigns() {
         return client.query("SELECT campaign_id, description, from_time, to_time FROM maplog.campaign ORDER BY campaign_id")
                 .execute()
                 .map(rs -> StreamSupport.stream(rs.spliterator(), false)
-                        .map(ROW_TO_CAMPAIGN)
+                        .map((row) -> Campaign.of(
+                                row.getLong("campaign_id"),
+                                row.getString("description"),
+                                row.getOffsetDateTime("from_time"),
+                                row.getOffsetDateTime("to_time")
+                        ))
                         .collect(Collectors.toList())
                 )
                 .onFailure(logger::catching);
     }
 
+    private static final Function<Row, Campaign> ROW_TO_CAMPAIGN = (row) -> Campaign.of(
+            row.getLong("campaign_id"),
+            row.getString("description"),
+            row.getOffsetDateTime("from_time"),
+            row.getOffsetDateTime("to_time")
+    );
+
     @Override
     public Future<Campaign> findCampaignById(long campaignId) {
         return client.preparedQuery("SELECT campaign_id, description, from_time, to_time FROM maplog.campaign WHERE campaign_id = $1")

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

@@ -151,6 +151,11 @@ public class MockMapLogRepository implements SensLogRepository {
     }
 
     @Override
+    public Future<CampaignUnit> findUnitByIdAndCampaignId(long campaignId, long unitId) {
+        return Future.succeededFuture(CampaignUnit.of(unitId, campaignId, "mock(name)", "mock(description)", OffsetDateTime.now(), OffsetDateTime.now().plusDays(1)));
+    }
+
+    @Override
     public Future<List<UnitLocation>> findUnitsLocationsByCampaignId(long campaignId, int limitPerUnit, OffsetDateTime from, OffsetDateTime to, ZoneId zone, SortType sort) {
         return Future.succeededFuture(Collections.emptyList());
     }

+ 1 - 0
src/main/java/cz/senslog/telemetry/database/repository/SensLogRepository.java

@@ -24,6 +24,7 @@ public interface SensLogRepository {
     Future<Unit> findUnitByIMEI(String imei);
     Future<List<Unit>> findUnitsBySensorId(long sensorId);
     Future<List<CampaignUnit>> findUnitsByCampaignId(long campaignId);
+    Future<CampaignUnit> findUnitByIdAndCampaignId(long campaignId, long unitId);
     Future<List<Long>> findUnitIdsByCampaignId(long campaignId);
 
     Future<List<Sensor>> allSensors();

+ 1 - 0
src/main/java/cz/senslog/telemetry/server/HttpVertxServer.java

@@ -60,6 +60,7 @@ public final class HttpVertxServer extends AbstractVerticle {
                     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("unitsGET").handler(apiHandler::unitsGET);
                     openAPIRouterBuilder.operation("unitIdGET").handler(apiHandler::unitIdGET);

+ 34 - 2
src/main/java/cz/senslog/telemetry/server/OpenAPIHandler.java

@@ -120,7 +120,8 @@ public class OpenAPIHandler {
         repo.findUnitsByCampaignId(campaignId)
                 .onSuccess(units -> rc.response().end(new JsonArray(
                         units.stream().map(u -> (navigationLinks ? JsonObject.of(
-                                "Unit@NavigationLink", String.format("%s/units/%d",host, u.getUnitId())
+                                "Unit@NavigationLink", String.format("%s/units/%d",host, u.getUnitId()),
+                                "CampaignUnit@NavigationLink", String.format("%s/campaigns/%d/units/%d",host, u.getCampaignId(), u.getUnitId())
                         ) : JsonObject.of()).mergeIn(JsonObject.of(
                                 "unitId", u.getUnitId(),
                                 "name", u.getUnitId(),
@@ -453,7 +454,7 @@ public class OpenAPIHandler {
                                 "Sensors@NavigationLink", String.format("%s/units/%d/sensors", host, u.getUnitId()),
                                 "Campaigns@NavigationLink", String.format("%s/units/%d/campaigns", host, u.getUnitId())
                         ) : JsonObject.of()).mergeIn(JsonObject.of(
-                                "id", u.getUnitId(),
+                                "unitId", u.getUnitId(),
                                 "name", u.getName(),
                                 "imei", u.getImei(),
                                 "description", u.getDescription()
@@ -644,4 +645,35 @@ public class OpenAPIHandler {
                 ).encode()))
                 .onFailure(th -> rc.fail(400, th));
     }
+
+    public void campaignIdUnitIdGET(RoutingContext rc) {
+        String host =  hostURLFull(rc.request());
+
+        long campaignId = Long.parseLong(rc.pathParam("campaignId"));
+        long unitId = Long.parseLong(rc.pathParam("unitId"));
+
+        List<String> paramZone = rc.queryParam("zone");
+        ZoneId zone = paramZone.isEmpty() ? DEFAULT_ZONE_ID : ZoneId.of(paramZone.get(0));
+
+        List<String> paramNavigationLinks = rc.queryParam("navigationLinks");
+        boolean navigationLinks = paramNavigationLinks.isEmpty() ? DEFAULT_NAVIGATION_LINKS : parseBoolean(paramNavigationLinks.get(0));
+
+        repo.findUnitByIdAndCampaignId(campaignId, unitId)
+                .onSuccess(u -> rc.response().end((navigationLinks ? JsonObject.of(
+                                "self@NavigationLink", String.format("%s/campaigns/%d/units/%d", host, u.getCampaignId(), u.getUnitId()),
+                                "Unit@NavigationLink", String.format("%s/units/%d", host, u.getUnitId()),
+                                "Campaign@NavigationLink", String.format("%s/campaigns/%d", host, u.getCampaignId()),
+                                "Sensors@NavigationLink", String.format("%s/campaigns/%d/units/%d/sensors", host, u.getCampaignId(), u.getUnitId()),
+                                "Observations@NavigationLink", String.format("%s/campaigns/%d/units/%d/observations", host, u.getCampaignId(), u.getUnitId()),
+                                "Locations@NavigationLink", String.format("%s/campaigns/%d/units/%d/observations/locations", host, u.getCampaignId(), u.getUnitId())
+                        ) : JsonObject.of()).mergeIn(JsonObject.of(
+                                "unitId", u.getUnitId(),
+                                "name", u.getName(),
+                                "imei", u.getImei(),
+                                "description", u.getDescription(),
+                                "fromTime", DATE_TIME_FORMATTER.apply(u.getFromTime(), zone),
+                                "toTime", DATE_TIME_FORMATTER.apply(u.getToTime(), zone)
+                        )).encode())
+                        .onFailure(th -> rc.fail(400, th)));
+    }
 }

+ 45 - 22
src/main/resources/openAPISpec.yaml

@@ -300,6 +300,23 @@ paths:
             type: integer
           required: true
           description: Numeric ID of the unit to get
+        - in: query
+          name: zone
+          schema:
+            type: string
+            default: UTC
+          required: false
+          examples:
+            UTC:
+              value: UTC
+            GMT:
+              value: GMT
+        - in: query
+          name: navigationLinks
+          schema:
+            type: boolean
+            default: true
+          description: Option to disable @NavigationLinks in a response
       responses:
         200:
           description: JSON Object containing info about the unit within its campaign' scope
@@ -382,7 +399,7 @@ paths:
               schema:
                 $ref: '#/components/schemas/Error'
 
-  /campaigns/{campaignId}/units/{unitId}/observations/location:
+  /campaigns/{campaignId}/units/{unitId}/observations/locations:
     get: # done
       operationId: campaignIdUnitIdLocationsGET
       summary: Publish locations of the unit within the campaign
@@ -746,7 +763,7 @@ paths:
                 $ref: '#/components/schemas/Error'
 
   /sensors/{sensorId}/units:
-    get:
+    get: # done
       operationId: sensorIdUnitsGET
       summary: Publish info about units to whom the sensor is assigned
       parameters:
@@ -963,6 +980,9 @@ components:
         Unit@NavigationLink:
           type: string
           format: uri
+        CampaignUnit@NavigationLink:
+          type: string
+          format: uri
         unitId:
           description: Identifier of the unit
           type: integer
@@ -981,6 +1001,7 @@ components:
           format: date-time
       example:
         Unit@NavigationLink: "<domain>/units/25"
+        CampaignUnit@NavigationLink: "<domain>/campaigns/1/units/25"
         unitId: 25
         name: "Unit name"
         description: "Purpose of the Unit 25"
@@ -990,35 +1011,35 @@ components:
     CampaignUnitDetailInfo:
       type: object
       required:
-        - self@NavigationLink
-        - Campaign@NavigationLink
-        - Unit@NavigationLink
-        - Sensors@NavigationLink
-        - Observations@NavigationLink
-        - id
+        - unitId
+        - imei
         - fromTime
         - toTime
       properties:
         self@NavigationLink:
           type: string
           format: uri
-        Campaign@NavigationLink:
+        Unit@NavigationLink:
           type: string
           format: uri
-        Unit@NavigationLink:
+        Campaign@NavigationLink:
           type: string
           format: uri
         Sensors@NavigationLink:
           type: string
           format: uri
         Observations@NavigationLink:
-          description: Navigation link to all campaign's data across all units
           type: string
           format: uri
-        id:
+        Locations@NavigationLink:
+          type: string
+          format: uri
+        unitId:
           description: Identifier of the unit
           type: integer
           format: int64
+        imei:
+          type: string
         description:
           type: string
         fromTime:
@@ -1031,11 +1052,13 @@ components:
           format: date-time
       example:
         self@NavigationLink: "<domain>/campaigns/1/units/25"
+        Unit@NavigationLink: "<domain>/units/25"
         Campaign@NavigationLink: "<domain>/campaigns/1"
-        Observations@NavigationLink: "<domain>/campaigns/1/units/25/observations"
         Sensors@NavigationLink: "<domain>/campaigns/1/units/25/sensors"
-        Unit@NavigationLink: "<domain>/units/25"
-        id: 25
+        Observations@NavigationLink: "<domain>/campaigns/1/units/25/observations"
+        Locations@NavigationLink: "<domain>/campaigns/1/units/25/observations/locations"
+        unitId: 25
+        imei: "3434533453"
         description: "Purpose of the Unit 25"
         fromTime: "2023-01-25 15:35:32Z"
         toTime: "2023-03-20 10:35:32Z"
@@ -1306,7 +1329,7 @@ components:
       example:
         Campaign@NavigationLink: "<domain>/campaigns/1"
         Unit@NavigationLink: "<domain>/campaigns/1/units/25"
-        next@NavigationLink: "<domain>/campaigns/1/units/25/observations/location?offset=500"
+        next@NavigationLink: "<domain>/campaigns/1/units/25/observations/locations?offset=500"
         params:
           offset: 0
           limit: 500
@@ -1384,13 +1407,13 @@ components:
     UnitBasicInfo:
       type: object
       required:
-        - id
+        - unitId
         - name
       properties:
         Unit@NavigationLink:
           type: string
           format: uri
-        id:
+        unitId:
           description: Identifier of the unit
           type: integer
           format: int64
@@ -1400,14 +1423,14 @@ components:
           type: string
       example:
         Unit@NavigationLink: "<domain>/units/25"
-        id: 25
+        unitId: 25
         name: "Mobile Unit"
         description: "Mobile Unit 25"
 
     UnitDetailInfo:
       type: object
       required:
-        - id
+        - unitId
         - name
         - imei
       properties:
@@ -1420,7 +1443,7 @@ components:
         Campaigns@NavigationLink:
           type: string
           format: uri
-        id:
+        unitId:
           description: Identifier of the unit
           type: integer
           format: int64
@@ -1434,7 +1457,7 @@ components:
         self@NavigationLink: "<domain>/units/25"
         Sensors@NavigationLink: "<domain>/units/25/sensors"
         Campaigns@NavigationLink: "<domain>/units/25/campaigns"
-        id: 25
+        unitId: 25
         name: "Mobile Unit"
         description: "Mobile Unit 25"
         imei: "3434535323345"