Skip to content

Commit 0b330ab

Browse files
committed
feat: new routers for vehicle based APIs
1 parent 584e5c5 commit 0b330ab

File tree

9 files changed

+428
-2
lines changed

9 files changed

+428
-2
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.ctrlhub.core.assets.vehicles
2+
3+
import com.ctrlhub.core.Config
4+
import com.ctrlhub.core.api.ApiClientException
5+
import com.ctrlhub.core.api.ApiException
6+
import com.ctrlhub.core.assets.vehicles.response.VehicleCategory
7+
import com.ctrlhub.core.router.Router
8+
import com.github.jasminb.jsonapi.ResourceConverter
9+
import com.github.jasminb.jsonapi.SerializationFeature
10+
import io.ktor.client.HttpClient
11+
import io.ktor.client.call.body
12+
import io.ktor.client.plugins.ClientRequestException
13+
14+
class VehicleCategoriesRouter(httpClient: HttpClient) : Router(httpClient, requiresAuthentication = true) {
15+
private val endpoint = "${Config.apiBaseUrl}/v3/assets/vehicles/categories"
16+
17+
suspend fun all(): List<VehicleCategory> {
18+
return try {
19+
val rawResponse = performGet(endpoint)
20+
val resourceConverter = ResourceConverter(VehicleCategory::class.java).apply {
21+
enableSerializationOption(SerializationFeature.INCLUDE_RELATIONSHIP_ATTRIBUTES)
22+
}
23+
24+
val jsonApiResponse = resourceConverter.readDocumentCollection<VehicleCategory>(
25+
rawResponse.body<ByteArray>(),
26+
VehicleCategory::class.java
27+
)
28+
29+
jsonApiResponse.get()!!
30+
} catch (e: ClientRequestException) {
31+
throw ApiClientException("All vehicle categories request failed", e.response, e)
32+
} catch (e: Exception) {
33+
throw ApiException("All vehicle categories request failed", e)
34+
}
35+
}
36+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.ctrlhub.core.assets.vehicles
2+
3+
import com.ctrlhub.core.Config
4+
import com.ctrlhub.core.api.ApiClientException
5+
import com.ctrlhub.core.api.ApiException
6+
import com.ctrlhub.core.assets.vehicles.response.VehicleManufacturer
7+
import com.ctrlhub.core.assets.vehicles.response.VehicleModel
8+
import com.ctrlhub.core.router.Router
9+
import com.ctrlhub.core.router.request.JsonApiIncludes
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+
16+
enum class VehicleManufacturerIncludes(val value: String) : JsonApiIncludes {
17+
Categories("categories");
18+
19+
override fun value(): String {
20+
return value
21+
}
22+
}
23+
24+
/**
25+
* A vehicles manufacturer router that deals with the vehicle manufacturers realm of the Ctrl Hub API
26+
*/
27+
class VehicleManufacturersRouter(httpClient: HttpClient) : Router(httpClient, requiresAuthentication = true) {
28+
private val endpoint: String = "${Config.apiBaseUrl}/v3/assets/vehicles/manufacturers"
29+
30+
suspend fun all(): List<VehicleManufacturer> {
31+
return try {
32+
val rawResponse = performGet(endpoint)
33+
val resourceConverter = ResourceConverter(VehicleManufacturer::class.java).apply {
34+
enableSerializationOption(SerializationFeature.INCLUDE_RELATIONSHIP_ATTRIBUTES)
35+
}
36+
37+
val jsonApiResponse = resourceConverter.readDocumentCollection<VehicleManufacturer>(
38+
rawResponse.body<ByteArray>(),
39+
VehicleManufacturer::class.java
40+
)
41+
42+
jsonApiResponse.get()!!
43+
} catch (e: ClientRequestException) {
44+
throw ApiClientException("All vehicle manufacturers request failed", e.response, e)
45+
} catch (e: Exception) {
46+
throw ApiException("All vehicle manufacturers request failed", e)
47+
}
48+
}
49+
50+
suspend fun models(manufacturerId: String, vararg includes: VehicleManufacturerIncludes = emptyArray()): List<VehicleModel> {
51+
return try {
52+
val includesStr = buildIncludesQueryString(*includes)
53+
54+
val rawResponse = performGet("$endpoint/$manufacturerId/models" + (if (includesStr.isNotEmpty()) "?$includesStr" else ""))
55+
val resourceConverter = ResourceConverter(VehicleModel::class.java).apply {
56+
enableSerializationOption(SerializationFeature.INCLUDE_RELATIONSHIP_ATTRIBUTES)
57+
}
58+
59+
val jsonApiResponse = resourceConverter.readDocumentCollection<VehicleModel>(
60+
rawResponse.body<ByteArray>(),
61+
VehicleModel::class.java
62+
)
63+
64+
jsonApiResponse.get()!!
65+
} catch (e: ClientRequestException) {
66+
throw ApiClientException("All vehicle models request failed", e.response, e)
67+
} catch (e: Exception) {
68+
throw ApiException("All vehicle models request failed", e)
69+
}
70+
}
71+
}

src/main/kotlin/com/ctrlhub/core/assets/vehicles/response/VehiclesResponse.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ class Vehicle {
2121
var specification: VehicleSpecification? = null
2222
}
2323

24+
@Type("vehicle-categories")
25+
class VehicleCategory {
26+
@Id(StringIdHandler::class)
27+
var id: String = ""
28+
var name: String = ""
29+
}
30+
2431
@Type("vehicle-specifications")
2532
@JsonIgnoreProperties(ignoreUnknown = true)
2633
class VehicleSpecification {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.ctrlhub.core.assets.vehicles
2+
3+
import com.ctrlhub.core.assets.vehicles.response.VehicleCategory
4+
import com.ctrlhub.core.configureForTest
5+
import io.ktor.client.*
6+
import io.ktor.client.engine.mock.*
7+
import io.ktor.http.*
8+
import kotlinx.coroutines.runBlocking
9+
import java.nio.file.Files
10+
import java.nio.file.Paths
11+
import kotlin.test.Test
12+
import kotlin.test.assertIs
13+
import kotlin.test.assertNotNull
14+
15+
class VehicleCategoriesRouterTest {
16+
@Test
17+
fun `can retrieve all vehicle categories`() {
18+
val jsonFilePath = Paths.get("src/test/resources/assets/vehicles/all-vehicle-categories-response.json")
19+
val jsonContent = Files.readString(jsonFilePath)
20+
21+
val mockEngine = MockEngine { request ->
22+
respond(
23+
content = jsonContent,
24+
status = HttpStatusCode.OK,
25+
headers = headersOf(HttpHeaders.ContentType, "application/json")
26+
)
27+
}
28+
29+
val vehicleCategoriesRouter = VehicleCategoriesRouter(httpClient = HttpClient(mockEngine).configureForTest())
30+
vehicleCategoriesRouter.sessionToken = "sess-123"
31+
32+
runBlocking {
33+
val response = vehicleCategoriesRouter.all()
34+
assertIs<List<VehicleCategory>>(response)
35+
assertNotNull(response[0].id)
36+
}
37+
}
38+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.ctrlhub.core.assets.vehicles
2+
3+
import com.ctrlhub.core.assets.vehicles.response.Vehicle
4+
import com.ctrlhub.core.assets.vehicles.response.VehicleManufacturer
5+
import com.ctrlhub.core.assets.vehicles.response.VehicleModel
6+
import com.ctrlhub.core.configureForTest
7+
import io.ktor.client.HttpClient
8+
import io.ktor.client.engine.mock.MockEngine
9+
import io.ktor.client.engine.mock.respond
10+
import io.ktor.http.HttpHeaders
11+
import io.ktor.http.HttpStatusCode
12+
import io.ktor.http.headersOf
13+
import kotlinx.coroutines.runBlocking
14+
import org.junit.jupiter.api.Test
15+
import java.nio.file.Files
16+
import java.nio.file.Paths
17+
import kotlin.test.assertIs
18+
import kotlin.test.assertNotNull
19+
20+
class VehiclesManufacturersRouterTest {
21+
@Test
22+
fun `can retrieve all vehicles`() {
23+
val jsonFilePath = Paths.get("src/test/resources/assets/vehicles/all-vehicle-manufacturers-response.json")
24+
val jsonContent = Files.readString(jsonFilePath)
25+
26+
val mockEngine = MockEngine { request ->
27+
respond(
28+
content = jsonContent,
29+
status = HttpStatusCode.OK,
30+
headers = headersOf(HttpHeaders.ContentType, "application/json")
31+
)
32+
}
33+
34+
val vehicleManufacturersRouter = VehicleManufacturersRouter(httpClient = HttpClient(mockEngine).configureForTest())
35+
vehicleManufacturersRouter.sessionToken = "sess-123"
36+
37+
runBlocking {
38+
val response = vehicleManufacturersRouter.all()
39+
assertIs<List<VehicleManufacturer>>(response)
40+
assertNotNull(response[0].id)
41+
}
42+
}
43+
44+
@Test
45+
fun `can retrieve all vehicle models for a manufacturer`() {
46+
val jsonFilePath = Paths.get("src/test/resources/assets/vehicles/all-vehicle-models-response.json")
47+
val jsonContent = Files.readString(jsonFilePath)
48+
49+
val mockEngine = MockEngine { request ->
50+
respond(
51+
content = jsonContent,
52+
status = HttpStatusCode.OK,
53+
headers = headersOf(HttpHeaders.ContentType, "application/json")
54+
)
55+
}
56+
57+
val vehicleManufacturersRouter = VehicleManufacturersRouter(httpClient = HttpClient(mockEngine).configureForTest())
58+
vehicleManufacturersRouter.sessionToken = "sess-123"
59+
60+
runBlocking {
61+
val response = vehicleManufacturersRouter.models(manufacturerId = "123")
62+
assertIs<List<VehicleModel>>(response)
63+
val first = response[0]
64+
assertNotNull(first.id)
65+
}
66+
}
67+
}

src/test/kotlin/com/ctrlhub/core/assets/vehicles/VehiclesRouterTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import kotlin.test.assertNotNull
1717

1818
class VehiclesRouterTest {
1919
@Test
20-
fun `test retrieve all vehicles`() {
20+
fun `can retrieve all vehicles`() {
2121
val jsonFilePath = Paths.get("src/test/resources/assets/vehicles/all-vehicles-response.json")
2222
val jsonContent = Files.readString(jsonFilePath)
2323

@@ -40,7 +40,7 @@ class VehiclesRouterTest {
4040
}
4141

4242
@Test
43-
fun `test can retrieve all vehicles with includes`() {
43+
fun `can retrieve all vehicles with includes`() {
4444
val jsonFilePath = Paths.get("src/test/resources/assets/vehicles/all-vehicles-response-with-includes.json")
4545
val jsonContent = Files.readString(jsonFilePath)
4646

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"data": [
3+
{
4+
"id": "00000000-0000-0000-0000-000000000000",
5+
"type": "vehicle-categories",
6+
"attributes": {
7+
"name": "Car"
8+
}
9+
},
10+
{
11+
"id": "00000000-0000-0000-0000-000000000001",
12+
"type": "vehicle-categories",
13+
"attributes": {
14+
"name": "Bus"
15+
}
16+
},
17+
{
18+
"id": "00000000-0000-0000-0000-000000000002",
19+
"type": "vehicle-categories",
20+
"attributes": {
21+
"name": "HGV"
22+
}
23+
},
24+
{
25+
"id": "00000000-0000-0000-0000-000000000003",
26+
"type": "vehicle-categories",
27+
"attributes": {
28+
"name": "Van"
29+
}
30+
}
31+
],
32+
"meta": {
33+
"features": {
34+
"params": {
35+
"include": {
36+
"options": null
37+
},
38+
"sort": {
39+
"options": [
40+
"name",
41+
"created_at"
42+
]
43+
},
44+
"filter": {
45+
"options": null
46+
}
47+
}
48+
},
49+
"count": 4
50+
},
51+
"jsonapi": {
52+
"version": "1.0",
53+
"meta": {}
54+
},
55+
"links": {
56+
"self": "https://api.ctrl-hub.dev/v3/assets/vehicles/categories"
57+
}
58+
}

0 commit comments

Comments
 (0)