Skip to content

Commit 324a5be

Browse files
committed
feat: Add organisation router with call to get all
1 parent f1ddd3f commit 324a5be

File tree

7 files changed

+301
-0
lines changed

7 files changed

+301
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.ctrlhub.core
2+
3+
abstract class AbstractNomenclatureEntry {
4+
lateinit var language: String
5+
lateinit var singular: String
6+
lateinit var plural: String
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.ctrlhub.core.api.response
2+
3+
class Links {
4+
var current: String? = null
5+
var next: String? = null
6+
var previous: String? = null
7+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.ctrlhub.core.governance
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.governance.response.Organisation
8+
import com.ctrlhub.core.http.KtorClientFactory
9+
import com.ctrlhub.core.router.Router
10+
import com.github.jasminb.jsonapi.ResourceConverter
11+
import io.ktor.client.HttpClient
12+
import io.ktor.client.call.body
13+
import io.ktor.client.plugins.ClientRequestException
14+
import io.ktor.client.request.get
15+
import io.ktor.client.request.header
16+
import io.ktor.http.headers
17+
18+
class OrganisationRouter(httpClient: HttpClient) : Router(httpClient) {
19+
suspend fun all(sessionToken: String): List<Organisation> {
20+
return try {
21+
val rawResponse = httpClient.get("${Config.apiBaseUrl}/v3/orgs") {
22+
headers {
23+
header("X-Session-Token", sessionToken)
24+
}
25+
}
26+
27+
val resourceConverter = ResourceConverter(Organisation::class.java)
28+
val jsonApiResponse = resourceConverter.readDocumentCollection<Organisation>(
29+
(rawResponse.body<ByteArray>()),
30+
Organisation::class.java
31+
)
32+
33+
jsonApiResponse.get()!!
34+
} catch (e: ClientRequestException) {
35+
throw ApiClientException("all organisations GET request failed", e.response, e)
36+
} catch (e: Exception) {
37+
throw ApiException("all organisations GET request failed", e)
38+
}
39+
}
40+
}
41+
42+
val Api.organisation: OrganisationRouter
43+
get() = OrganisationRouter(KtorClientFactory.create())
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.ctrlhub.core.governance.response
2+
3+
import com.ctrlhub.core.AbstractNomenclatureEntry
4+
import com.ctrlhub.core.serializer.JacksonLocalDateTimeDeserializer
5+
import com.fasterxml.jackson.annotation.JsonProperty
6+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
7+
import com.github.jasminb.jsonapi.StringIdHandler
8+
import com.github.jasminb.jsonapi.annotations.Id
9+
import com.github.jasminb.jsonapi.annotations.Meta
10+
import com.github.jasminb.jsonapi.annotations.Type
11+
import java.time.LocalDateTime
12+
13+
class OrganisationCollection {
14+
lateinit var meta: OrganisationResponseMeta
15+
lateinit var items: List<Organisation>
16+
}
17+
18+
@Type("organisations")
19+
class Organisation {
20+
@Id(StringIdHandler::class)
21+
lateinit var id: String
22+
lateinit var name: String
23+
var description: String? = null
24+
lateinit var slug: String
25+
lateinit var settings: Settings
26+
27+
@Meta
28+
lateinit var meta: OrganisationMeta
29+
}
30+
31+
class Settings {
32+
lateinit var nomenclature: Nomenclature
33+
}
34+
35+
/**
36+
* Not to be confused with OrganisationResponseMeta
37+
*/
38+
class OrganisationMeta {
39+
@JsonProperty("created_at")
40+
@JsonDeserialize(using = JacksonLocalDateTimeDeserializer::class)
41+
var createdAt: LocalDateTime? = null
42+
lateinit var status: String
43+
}
44+
45+
class OrganisationResponseMeta {
46+
lateinit var counts: OrganisationResponseMetaCounts
47+
@JsonProperty("created_at")
48+
@JsonDeserialize(using = JacksonLocalDateTimeDeserializer::class)
49+
var createdAt: LocalDateTime? = null
50+
lateinit var status: String
51+
}
52+
53+
class OrganisationResponseMetaCounts {
54+
var limit: Int = 0
55+
}
56+
57+
class OrganisationResponseMetaPage {
58+
var limit: Int = 0
59+
var offset: Int = 0
60+
}
61+
62+
class Nomenclature {
63+
lateinit var governance: GovernanceNomenclature
64+
}
65+
66+
class SchemesNomenclature : AbstractNomenclatureEntry()
67+
class WorkOrdersNomenclature : AbstractNomenclatureEntry()
68+
class OperationsNomenclature : AbstractNomenclatureEntry()
69+
70+
class GovernanceNomenclature {
71+
var schemes: List<SchemesNomenclature>? = null
72+
@JsonProperty("work_orders") var workOrders: List<WorkOrdersNomenclature>? = null
73+
var operations: List<OperationsNomenclature>? = null
74+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.ctrlhub.core.serializer
2+
3+
import com.fasterxml.jackson.core.JsonGenerator
4+
import com.fasterxml.jackson.core.JsonParser
5+
import com.fasterxml.jackson.databind.DeserializationContext
6+
import com.fasterxml.jackson.databind.JsonNode
7+
import com.fasterxml.jackson.databind.SerializerProvider
8+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
9+
import com.fasterxml.jackson.databind.ser.std.StdSerializer
10+
import java.time.LocalDateTime
11+
import java.time.ZoneOffset
12+
import java.time.format.DateTimeFormatter
13+
14+
private val formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME
15+
16+
class JacksonLocalDateTimeSerializer @JvmOverloads constructor(t: Class<LocalDateTime>? = null) : StdSerializer<LocalDateTime>(t) {
17+
override fun serialize(
18+
value: LocalDateTime?,
19+
gen: JsonGenerator?,
20+
provider: SerializerProvider?
21+
) {
22+
value?.let { gen?.writeString(value.atOffset(ZoneOffset.UTC).format(formatter)) }
23+
}
24+
}
25+
26+
class JacksonLocalDateTimeDeserializer @JvmOverloads constructor(t: Class<LocalDateTime>? = null) : StdDeserializer<LocalDateTime>(t) {
27+
override fun deserialize(
28+
p: JsonParser,
29+
ctxt: DeserializationContext?
30+
): LocalDateTime? {
31+
val node: JsonNode = p.codec.readTree(p)
32+
val dateString = node.asText()
33+
34+
return LocalDateTime.parse(dateString, formatter)
35+
}
36+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.ctrlhub.core.governance
2+
3+
import com.ctrlhub.core.configureForTest
4+
import io.ktor.client.HttpClient
5+
import io.ktor.client.engine.mock.MockEngine
6+
import io.ktor.client.engine.mock.respond
7+
import io.ktor.http.HttpHeaders
8+
import io.ktor.http.HttpStatusCode
9+
import io.ktor.http.headersOf
10+
import io.ktor.utils.io.ByteReadChannel
11+
import kotlinx.coroutines.runBlocking
12+
import java.nio.file.Files
13+
import java.nio.file.Paths
14+
import kotlin.test.Test
15+
import kotlin.test.assertEquals
16+
17+
class OrganisationRouterTest {
18+
19+
@Test
20+
fun `test can get all organisations successfully`() {
21+
val jsonFilePath = Paths.get("src/test/resources/governance/all-organisations-response.json")
22+
val jsonContent = Files.readString(jsonFilePath)
23+
24+
val mockEngine = MockEngine { request ->
25+
respond(
26+
content = ByteReadChannel(jsonContent),
27+
status = HttpStatusCode.OK,
28+
headers = headersOf(HttpHeaders.ContentType, "application/json")
29+
)
30+
}
31+
32+
val organisationRouter = OrganisationRouter(httpClient = HttpClient(mockEngine).configureForTest())
33+
34+
runBlocking {
35+
val response = organisationRouter.all("test-session")
36+
37+
assertEquals(3, response.size)
38+
assertEquals("00000000-0000-0000-0000-000000000001", response[0].id)
39+
assertEquals("00000000-0000-0000-0000-000000000002", response[1].id)
40+
}
41+
}
42+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{
2+
"data": [
3+
{
4+
"id": "00000000-0000-0000-0000-000000000001",
5+
"type": "organisations",
6+
"attributes": {
7+
"description": "",
8+
"name": "Organisation 1",
9+
"settings": {
10+
"nomenclature": {
11+
"governance": {
12+
"schemes": null,
13+
"work_orders": null,
14+
"operations": null
15+
}
16+
}
17+
},
18+
"slug": "organisation-1"
19+
},
20+
"meta": {
21+
"status": "active",
22+
"created_at": "2020-08-05T11:42:59Z"
23+
}
24+
},
25+
{
26+
"id": "00000000-0000-0000-0000-000000000002",
27+
"type": "organisations",
28+
"attributes": {
29+
"description": "",
30+
"name": "Organisation 2",
31+
"settings": {
32+
"nomenclature": {
33+
"governance": {
34+
"schemes": null,
35+
"work_orders": null,
36+
"operations": null
37+
}
38+
}
39+
},
40+
"slug": "organisation-2"
41+
},
42+
"meta": {
43+
"status": "active",
44+
"created_at": "2018-07-18T08:53:25Z"
45+
}
46+
},
47+
{
48+
"id": "00000000-0000-0000-0000-000000000003",
49+
"type": "organisations",
50+
"attributes": {
51+
"description": "",
52+
"name": "Organisation 3",
53+
"settings": {
54+
"nomenclature": {
55+
"governance": {
56+
"schemes": null,
57+
"work_orders": null,
58+
"operations": null
59+
}
60+
}
61+
},
62+
"slug": "organisation-3"
63+
},
64+
"meta": {
65+
"status": "active",
66+
"created_at": "2018-06-13T11:42:24Z"
67+
}
68+
}
69+
],
70+
"meta": {
71+
"features": {
72+
"params": {
73+
"include": {
74+
"options": null
75+
},
76+
"sort": {
77+
"options": null
78+
},
79+
"filter": {
80+
"options": null
81+
}
82+
}
83+
}
84+
},
85+
"jsonapi": {
86+
"version": "1.0",
87+
"meta": {}
88+
},
89+
"links": {
90+
"self": "https://api.ctrl-hub.com/v3/orgs"
91+
}
92+
}

0 commit comments

Comments
 (0)