feat(cw2): /api/v1/dronesWithCooling impl

This commit is contained in:
js0ny 2025-11-18 17:55:50 +00:00
parent 2b9b668a2f
commit 3c96f9d5af
10 changed files with 206 additions and 27 deletions

View file

@ -0,0 +1,31 @@
meta {
name: dronesWithCooling -> false
type: http
seq: 1
}
get {
url: {{API_BASE}}/dronesWithCooling/false
body: none
auth: inherit
}
tests {
test("Status code is 200", function() {
expect(res.status).to.equal(200);
});
test("Response body is a JSON array", function() {
expect(res.getBody()).to.be.an('array');
});
test("Array is not empty and contains numbers", function() {
const data = res.getBody();
expect(data[0]).to.be.a('number'); // data should be in number
});
}
settings {
encodeUrl: true
timeout: 0
}

View file

@ -0,0 +1,24 @@
meta {
name: dronesWithCooling -> illegal
type: http
seq: 1
}
get {
url: {{API_BASE}}/dronesWithCooling/illegal
body: none
auth: inherit
}
tests {
test("Status code is 400", function() {
expect(res.status).to.equal(400);
});
}
settings {
encodeUrl: true
timeout: 0
}

View file

@ -0,0 +1,31 @@
meta {
name: dronesWithCooling -> true
type: http
seq: 1
}
get {
url: {{API_BASE}}/dronesWithCooling/true
body: none
auth: inherit
}
tests {
test("Status code is 200", function() {
expect(res.status).to.equal(200);
});
test("Response body is a JSON array", function() {
expect(res.getBody()).to.be.an('array');
});
test("Array is not empty and contains numbers", function() {
const data = res.getBody();
expect(data[0]).to.be.a('number'); // data should be in number
});
}
settings {
encodeUrl: true
timeout: 0
}

View file

@ -0,0 +1,8 @@
meta {
name: [GET] dronesWithCooling
seq: 1
}
auth {
mode: inherit
}

9
ilp-cw-api/bruno.json Normal file
View file

@ -0,0 +1,9 @@
{
"version": "1",
"name": "ILP CW API Collection",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}

View file

@ -0,0 +1,4 @@
vars:pre-request {
BASE_URL: http://localhost:8080
API_BASE: {{BASE_URL}}/api/v1
}

View file

@ -16,7 +16,7 @@ import io.github.js0ny.ilp_coursework.service.GpsCalculationService;
/**
* Main REST Controller for the ILP Coursework 1 application.
* <p>
* This class handles all incoming HTTP requests for the API under {@code /api/v1} path.
* This class handles incoming HTTP requests for the API under {@code /api/v1} path (defined in CW1)
* This is responsible for mapping requests to the appropriate service method and returning the results as responses.
* The business logic is delegated to {@link GpsCalculationService}
*/

View file

@ -8,43 +8,50 @@ import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
/**
* Main Rest Controller for the ILP Coursework 2 application.
* <p>
* This class handles incoming HTTP requests for the API under {@code /api/v1} path (defined in CW2)
* The business logic is delegated to {@link DroneInfoService}
*/
@RestController
@RequestMapping("/api/v1")
public class DroneController {
private final DroneInfoService droneService;
private final String baseUrl;
private final RestTemplate restTemplate = new RestTemplate();
/**
* Constructor of the {@code DroneController} with the business logic dependency {@code DroneInfoService}
* <p>
* We handle the {@code baseUrl} here. Use a predefined URL if the environment variable {@code ILP_ENDPOINT}
* is not given.
*
* @param droneService The service component that contains all business logic
*/
public DroneController(DroneInfoService droneService) {
this.droneService = droneService;
String baseUrl = System.getenv("ILP_ENDPOINT");
if (baseUrl == null || baseUrl.isBlank()) {
this.baseUrl = "https://ilp-rest-2025-bvh6e9hschfagrgy.ukwest-01.azurewebsites.net/";
} else {
if (!baseUrl.endsWith("/")) {
baseUrl += "/";
}
this.baseUrl = baseUrl;
}
}
/**
* Handles GET requests to retrieve an array of drones (identified by id) that has the capability of cooling
*
* @param state The path variable that indicates the return should have or not have the capability
* @return An array of drone id with cooling capability.
*/
@GetMapping("/dronesWithCooling/{state}")
public int[] getDronesWithCoolingAbility(@PathVariable boolean state) {
String droneUrl = baseUrl + "drones";
DroneDto[] drones = restTemplate.getForObject(droneUrl, DroneDto[].class);
if (drones == null) {
return new int[]{};
public int[] getDronesWithCoolingCapability(@PathVariable boolean state) {
return droneService.dronesWithCooling(state);
}
return Arrays.stream(drones).
filter(drone -> drone.capability().cooling() == state).
mapToInt(drone -> Integer.parseInt(String.valueOf(drone.id()))).
toArray();
}
// @GetMapping("/droneDetails/{id}")
// public DroneDto getDroneDetail(@PathVariable int id) {
// String droneUrl = baseUrl + "drones";
//
// DroneDto[] drones = restTemplate.getForObject(droneUrl, DroneDto[].class);
//
// return new DroneDto();
//
// }
@PostMapping("queryAvailableDrones")
public int queryAvailableDrones() {

View file

@ -5,6 +5,6 @@ package io.github.js0ny.ilp_coursework.data;
*/
public record DroneDto(
String name,
int id,
String id,
DroneCapabilityDto capability) {
}

View file

@ -1,8 +1,73 @@
package io.github.js0ny.ilp_coursework.service;
import io.github.js0ny.ilp_coursework.data.DroneDto;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriBuilder;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.Arrays;
import java.util.stream.Stream;
@Service
public class DroneInfoService {
private final String baseUrl;
private final String dronesEndpoint = "drones";
private final RestTemplate restTemplate = new RestTemplate();
/**
* Constructor, handles the base url here.
*/
public DroneInfoService() {
String baseUrl = System.getenv("ILP_ENDPOINT");
if (baseUrl == null || baseUrl.isBlank()) {
this.baseUrl = "https://ilp-rest-2025-bvh6e9hschfagrgy.ukwest-01.azurewebsites.net/";
} else {
// Defensive: Add '/' to the end of the URL
if (!baseUrl.endsWith("/")) {
baseUrl += "/";
}
this.baseUrl = baseUrl;
}
}
/**
* Return an array of ids of drones with/without cooling capability
*
* @param state determines the capability filtering
* @return if {@code state} is true, return ids of drones with cooling capability, else without cooling
*/
public int[] dronesWithCooling(boolean state) {
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
DroneDto[] drones = restTemplate.getForObject(droneUrl, DroneDto[].class);
if (drones == null) {
return new int[]{};
}
return Arrays.stream(drones).
filter(drone -> drone.capability().cooling() == state).
mapToInt(drone -> Integer.parseInt(drone.id())).
toArray();
}
// TODO: This is function is WIP
public Stream<DroneDto> droneDetail(int id) {
String droneUrl = baseUrl + dronesEndpoint;
DroneDto[] drones = restTemplate.getForObject(droneUrl, DroneDto[].class);
if (drones == null) {
throw new IllegalArgumentException("drone with that ID cannot be found");
}
return Arrays.stream(drones).
filter(drone -> Integer.parseInt(String.valueOf(drone.id())) == id);
}
}