Skip to content

Commit 9988ccf

Browse files
committed
feat: vehicles router added
1 parent a542874 commit 9988ccf

File tree

7 files changed

+789
-4
lines changed

7 files changed

+789
-4
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.ctrlhub.core.assets.vehicles
2+
3+
import com.ctrlhub.core.Api
4+
import com.ctrlhub.core.Config
5+
import com.ctrlhub.core.api.ApiClientException
6+
import com.ctrlhub.core.api.ApiException
7+
import com.ctrlhub.core.assets.vehicles.response.Vehicle
8+
import com.ctrlhub.core.http.KtorClientFactory
9+
import com.ctrlhub.core.router.Router
10+
import com.github.jasminb.jsonapi.ResourceConverter
11+
import com.github.jasminb.jsonapi.SerializationFeature
12+
import io.ktor.client.HttpClient
13+
import io.ktor.client.call.body
14+
import io.ktor.client.plugins.ClientRequestException
15+
import io.ktor.client.request.get
16+
import io.ktor.client.request.header
17+
import io.ktor.http.headers
18+
19+
enum class VehicleIncludes(val value: String) {
20+
Status("status"),
21+
Assignee("assignee"),
22+
Equipment("equipment"),
23+
EquipmentManufacturer("equipment.manufacturer"),
24+
EquipmentModel("equipment.model"),
25+
EquipmentModelCategories("equipment.model.categories"),
26+
Specification("specification"),
27+
SpecificationModel("specification.model"),
28+
SpecificationModelManufacturer("specification.model.manufacturer")
29+
}
30+
31+
class VehiclesRouter(httpClient: HttpClient) : Router(httpClient) {
32+
suspend fun all(sessionToken: String, organisationId: String, vararg includes: VehicleIncludes): List<Vehicle> {
33+
val includesQuery = if (includes.isNotEmpty()) {
34+
"?include=${includes.joinToString(",") { it.value }}"
35+
} else {
36+
""
37+
}
38+
39+
return fetchAllVehicles(sessionToken, organisationId, includesQuery)
40+
}
41+
42+
private suspend fun fetchAllVehicles(sessionToken: String, organisationId: String, includesQuery: String): List<Vehicle> {
43+
return try {
44+
val rawResponse = httpClient.get("${Config.apiBaseUrl}/v3/orgs/$organisationId/assets/vehicles$includesQuery") {
45+
headers {
46+
header("X-Session-Token", sessionToken)
47+
}
48+
}
49+
50+
val resourceConverter = ResourceConverter(Vehicle::class.java).apply {
51+
enableSerializationOption(SerializationFeature.INCLUDE_RELATIONSHIP_ATTRIBUTES)
52+
}
53+
54+
val jsonApiResponse = resourceConverter.readDocumentCollection<Vehicle>(
55+
rawResponse.body<ByteArray>(),
56+
Vehicle::class.java
57+
)
58+
59+
jsonApiResponse.get()!!
60+
} catch (e: ClientRequestException) {
61+
throw ApiClientException("All vehicles request failed", e.response, e)
62+
} catch (e: Exception) {
63+
throw ApiException("All vehicles request failed", e)
64+
}
65+
}
66+
}
67+
68+
val Api.vehicles: VehiclesRouter
69+
get() = VehiclesRouter(KtorClientFactory.create())
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.ctrlhub.core.assets.vehicles.response
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
4+
import com.fasterxml.jackson.annotation.JsonProperty
5+
import com.github.jasminb.jsonapi.StringIdHandler
6+
import com.github.jasminb.jsonapi.annotations.Id
7+
import com.github.jasminb.jsonapi.annotations.Relationship
8+
import com.github.jasminb.jsonapi.annotations.Type
9+
10+
@Type("vehicles")
11+
class Vehicle {
12+
@Id(StringIdHandler::class)
13+
var id: String = ""
14+
var colour: String = ""
15+
var description: String? = null
16+
var registration: String = ""
17+
var status: String = ""
18+
var vin: String? = null
19+
20+
@Relationship("specification")
21+
var specification: VehicleSpecification? = null
22+
}
23+
24+
@Type("vehicle-specifications")
25+
@JsonIgnoreProperties(ignoreUnknown = true)
26+
class VehicleSpecification {
27+
@Id(StringIdHandler::class)
28+
var id: String = ""
29+
var emissions: Double = 0.0
30+
var transmission: String = ""
31+
var year: Int = 0
32+
@JsonProperty("fuel_type") var fuelType: String = ""
33+
@JsonProperty("engine_capacity") var engineCapacity: String = ""
34+
var documentation: List<VehicleDocumentation> = emptyList()
35+
36+
@Relationship("model")
37+
var model: VehicleModel? = null
38+
}
39+
40+
@Type("vehicle-manufacturers")
41+
class VehicleManufacturer {
42+
@Id(StringIdHandler::class)
43+
var id: String = ""
44+
var name: String = ""
45+
}
46+
47+
@Type("vehicle-models")
48+
class VehicleModel {
49+
@Id(StringIdHandler::class)
50+
var id: String = ""
51+
var name: String = ""
52+
53+
@Relationship("manufacturer")
54+
var manufacturer: VehicleManufacturer? = null
55+
}
56+
57+
class VehicleDocumentation {
58+
var name: String = ""
59+
var description: String = ""
60+
var link: String = ""
61+
}

src/main/kotlin/com/ctrlhub/core/governance/OrganisationRouter.kt renamed to src/main/kotlin/com/ctrlhub/core/governance/OrganisationsRouter.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import io.ktor.client.request.get
1515
import io.ktor.client.request.header
1616
import io.ktor.http.headers
1717

18-
class OrganisationRouter(httpClient: HttpClient) : Router(httpClient) {
18+
class OrganisationsRouter(httpClient: HttpClient) : Router(httpClient) {
1919
suspend fun all(sessionToken: String): List<Organisation> {
2020
return try {
2121
val rawResponse = httpClient.get("${Config.apiBaseUrl}/v3/orgs") {
@@ -39,5 +39,5 @@ class OrganisationRouter(httpClient: HttpClient) : Router(httpClient) {
3939
}
4040
}
4141

42-
val Api.organisation: OrganisationRouter
43-
get() = OrganisationRouter(KtorClientFactory.create())
42+
val Api.organisations: OrganisationsRouter
43+
get() = OrganisationsRouter(KtorClientFactory.create())
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.ctrlhub.core.assets.vehicles
2+
3+
import com.ctrlhub.core.assets.vehicles.response.Vehicle
4+
import com.ctrlhub.core.configureForTest
5+
import io.ktor.client.HttpClient
6+
import io.ktor.client.engine.mock.MockEngine
7+
import io.ktor.client.engine.mock.respond
8+
import io.ktor.http.HttpHeaders
9+
import io.ktor.http.HttpStatusCode
10+
import io.ktor.http.headersOf
11+
import kotlinx.coroutines.runBlocking
12+
import org.junit.jupiter.api.Test
13+
import java.nio.file.Files
14+
import java.nio.file.Paths
15+
import kotlin.test.assertIs
16+
import kotlin.test.assertNotNull
17+
18+
class VehiclesRouterTest {
19+
@Test
20+
fun `test retrieve all vehicles`() {
21+
val jsonFilePath = Paths.get("src/test/resources/assets/vehicles/all-vehicles-response.json")
22+
val jsonContent = Files.readString(jsonFilePath)
23+
24+
val mockEngine = MockEngine { request ->
25+
respond(
26+
content = jsonContent,
27+
status = HttpStatusCode.OK,
28+
headers = headersOf(HttpHeaders.ContentType, "application/json")
29+
)
30+
}
31+
32+
val vehiclesRouter = VehiclesRouter(httpClient = HttpClient(mockEngine).configureForTest())
33+
34+
runBlocking {
35+
val response = vehiclesRouter.all(sessionToken = "test-token", organisationId = "123")
36+
assertIs<List<Vehicle>>(response)
37+
assertNotNull(response[0].id)
38+
}
39+
}
40+
41+
@Test
42+
fun `test can retrieve all vehicles with includes`() {
43+
val jsonFilePath = Paths.get("src/test/resources/assets/vehicles/all-vehicles-response-with-includes.json")
44+
val jsonContent = Files.readString(jsonFilePath)
45+
46+
val mockEngine = MockEngine { request ->
47+
respond(
48+
content = jsonContent,
49+
status = HttpStatusCode.OK,
50+
headers = headersOf(HttpHeaders.ContentType, "application/json")
51+
)
52+
}
53+
54+
val vehiclesRouter = VehiclesRouter(httpClient = HttpClient(mockEngine).configureForTest())
55+
56+
runBlocking {
57+
val response = vehiclesRouter.all(sessionToken = "test-token", organisationId = "123", VehicleIncludes.SpecificationModel)
58+
assertIs<List<Vehicle>>(response)
59+
val first = response[0]
60+
assertNotNull(first.specification?.model)
61+
}
62+
}
63+
}

src/test/kotlin/com/ctrlhub/core/governance/OrganisationRouterTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class OrganisationRouterTest {
2929
)
3030
}
3131

32-
val organisationRouter = OrganisationRouter(httpClient = HttpClient(mockEngine).configureForTest())
32+
val organisationRouter = OrganisationsRouter(httpClient = HttpClient(mockEngine).configureForTest())
3333

3434
runBlocking {
3535
val response = organisationRouter.all("test-session")

0 commit comments

Comments
 (0)