Skip to content

Commit f785d82

Browse files
committed
feat: add support for operation includes in request calls
1 parent b349af1 commit f785d82

File tree

4 files changed

+229
-5
lines changed

4 files changed

+229
-5
lines changed

src/main/kotlin/com/ctrlhub/core/projects/operations/OperationsRouter.kt

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,29 @@ import com.ctrlhub.core.api.response.PaginatedList
55
import com.ctrlhub.core.projects.workorders.WorkOrdersRouter
66
import com.ctrlhub.core.projects.operations.response.Operation
77
import com.ctrlhub.core.iam.response.User
8+
import com.ctrlhub.core.projects.operations.templates.response.OperationTemplate
89
import com.ctrlhub.core.router.Router
10+
import com.ctrlhub.core.router.request.FilterOption
11+
import com.ctrlhub.core.router.request.JsonApiIncludes
912
import com.ctrlhub.core.router.request.RequestParameters
13+
import com.ctrlhub.core.router.request.RequestParametersWithIncludes
1014
import io.ktor.client.HttpClient
1115

16+
enum class OperationIncludes(val value: String) : JsonApiIncludes {
17+
Template("template");
18+
19+
override fun value(): String {
20+
return value
21+
}
22+
}
23+
24+
class OperationRequestParameters(
25+
offset: Int = 0,
26+
limit: Int = 100,
27+
filterOptions: List<FilterOption> = emptyList(),
28+
includes: List<OperationIncludes> = emptyList()
29+
) : RequestParametersWithIncludes<OperationIncludes>(offset, limit, filterOptions, includes)
30+
1231
class OperationsRouter(httpClient: HttpClient) : Router(httpClient) {
1332

1433
/**
@@ -18,10 +37,16 @@ class OperationsRouter(httpClient: HttpClient) : Router(httpClient) {
1837
*
1938
* @return A list of all operations
2039
*/
21-
suspend fun all(organisationId: String, requestParameters: RequestParameters = RequestParameters()): PaginatedList<Operation> {
40+
suspend fun all(
41+
organisationId: String,
42+
requestParameters: OperationRequestParameters = OperationRequestParameters()
43+
): PaginatedList<Operation> {
2244
val endpoint = "/v3/orgs/$organisationId/projects/operations"
2345

24-
return fetchPaginatedJsonApiResources(endpoint, requestParameters.toMap(), Operation::class.java, User::class.java)
46+
return fetchPaginatedJsonApiResources(
47+
endpoint, requestParameters.toMap(), Operation::class.java,
48+
OperationTemplate::class.java, User::class.java
49+
)
2550
}
2651

2752
/**
@@ -32,10 +57,20 @@ class OperationsRouter(httpClient: HttpClient) : Router(httpClient) {
3257
*
3358
* @return A list of all operations
3459
*/
35-
suspend fun one(organisationId: String, operationId: String): Operation {
60+
suspend fun one(
61+
organisationId: String,
62+
operationId: String,
63+
requestParameters: OperationRequestParameters = OperationRequestParameters()
64+
): Operation {
3665
val endpoint = "/v3/orgs/$organisationId/projects/operations/$operationId"
3766

38-
return fetchJsonApiResource(endpoint, emptyMap(), Operation::class.java, User::class.java)
67+
return fetchJsonApiResource(
68+
endpoint,
69+
requestParameters.toMap(),
70+
Operation::class.java,
71+
OperationTemplate::class.java,
72+
User::class.java
73+
)
3974
}
4075
}
4176

src/main/kotlin/com/ctrlhub/core/projects/operations/response/Operation.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ctrlhub.core.projects.operations.response
22

33
import com.ctrlhub.core.api.Assignable
4+
import com.ctrlhub.core.projects.operations.templates.response.OperationTemplate
45
import com.ctrlhub.core.projects.response.Label
56
import com.fasterxml.jackson.annotation.JsonCreator
67
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
@@ -24,7 +25,10 @@ class Operation @JsonCreator constructor(
2425
var labels: List<Label> = emptyList(),
2526

2627
@Relationship("assignees")
27-
var assignees: java.util.List<Assignable>? = null
28+
var assignees: java.util.List<Assignable>? = null,
29+
30+
@Relationship("template")
31+
var template: OperationTemplate? = null
2832
) {
2933
constructor(): this(
3034
name = "",

src/test/kotlin/com/ctrlhub/core/projects/operations/OperationsRouterTest.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import io.ktor.client.*
77
import io.ktor.client.engine.mock.*
88
import io.ktor.http.*
99
import kotlinx.coroutines.runBlocking
10+
import org.junit.jupiter.api.Assertions.assertNotNull
1011
import org.junit.jupiter.api.Test
1112
import java.nio.file.Files
1213
import java.nio.file.Paths
14+
import kotlin.test.assertEquals
1315
import kotlin.test.assertIs
1416
import kotlin.test.assertNotNull
1517

@@ -64,4 +66,35 @@ class OperationsRouterTest {
6466
assertNotNull(response.id)
6567
}
6668
}
69+
70+
@Test
71+
fun `can retrieve all operations with included templates`() {
72+
val jsonFilePath = Paths.get("src/test/resources/projects/operations/all-operations-with-included-templates.json")
73+
val jsonContent = Files.readString(jsonFilePath)
74+
75+
val mockEngine = MockEngine { request ->
76+
respond(
77+
content = jsonContent,
78+
status = HttpStatusCode.OK,
79+
headers = headersOf(HttpHeaders.ContentType, "application/json")
80+
)
81+
}
82+
83+
val operationsRouter = OperationsRouter(httpClient = HttpClient(mockEngine).configureForTest())
84+
85+
runBlocking {
86+
val response = operationsRouter.all(
87+
organisationId = "123",
88+
requestParameters = OperationRequestParameters(
89+
includes = listOf(
90+
OperationIncludes.Template
91+
)
92+
)
93+
)
94+
95+
assertNotNull(response.data[0].template)
96+
assertEquals("96bc0c11-7f6a-403a-a499-7f9e1bb6811d", response.data[0].template?.id)
97+
assertEquals("Example Template Name", response.data[0].template?.name)
98+
}
99+
}
67100
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
{
2+
"data": [
3+
{
4+
"id": "f3a1c9e5-9f48-42f5-8a3e-c2bfc26ebf5c",
5+
"type": "operations",
6+
"attributes": {
7+
"code": "",
8+
"dates": {
9+
"scheduled": {}
10+
},
11+
"description": "Lorem ipsum dolor sit amet",
12+
"labels": [
13+
{
14+
"key": "category",
15+
"value": "example"
16+
}
17+
],
18+
"name": "Sample Operation Name"
19+
},
20+
"relationships": {
21+
"assignees": {
22+
"data": [
23+
{
24+
"id": "d9c332e2-80fc-4d1b-9f60-1e945cc6a9f3",
25+
"type": "users"
26+
}
27+
]
28+
},
29+
"organisation": {
30+
"data": {
31+
"id": "7c15ae62-91e3-45d4-babc-2652764d0125",
32+
"type": "organisations"
33+
}
34+
},
35+
"permits": {
36+
"data": []
37+
},
38+
"properties": {
39+
"data": []
40+
},
41+
"scheme": {
42+
"data": {
43+
"id": "eec9252a-0322-44c2-8fd2-b0fc75f885b6",
44+
"type": "schemes"
45+
}
46+
},
47+
"streets": {
48+
"data": []
49+
},
50+
"teams": {
51+
"data": []
52+
},
53+
"template": {
54+
"data": {
55+
"id": "96bc0c11-7f6a-403a-a499-7f9e1bb6811d",
56+
"type": "operation-templates"
57+
}
58+
},
59+
"work_order": {
60+
"data": {
61+
"id": "144b3a60-68a6-4206-81e7-f6fa85ce7c53",
62+
"type": "work-orders"
63+
}
64+
}
65+
},
66+
"meta": {
67+
"created_at": "2025-06-04T12:50:03.013Z",
68+
"updated_at": "2025-06-04T12:50:09.727Z",
69+
"counts": {
70+
"properties": 0,
71+
"streets": 0
72+
}
73+
}
74+
}
75+
],
76+
"meta": {
77+
"pagination": {
78+
"current_page": 1,
79+
"counts": {
80+
"resources": 1,
81+
"pages": 1
82+
},
83+
"requested": {
84+
"offset": 0,
85+
"limit": 100
86+
},
87+
"offsets": {
88+
"previous": null,
89+
"next": null
90+
}
91+
},
92+
"features": {
93+
"params": {
94+
"include": {
95+
"options": [
96+
"template"
97+
]
98+
},
99+
"sort": {
100+
"default": "",
101+
"options": null
102+
}
103+
}
104+
}
105+
},
106+
"jsonapi": {
107+
"version": "1.0"
108+
},
109+
"included": [
110+
{
111+
"id": "96bc0c11-7f6a-403a-a499-7f9e1bb6811d",
112+
"type": "operation-templates",
113+
"attributes": {
114+
"labels": [
115+
{
116+
"key": "type",
117+
"value": "form"
118+
}
119+
],
120+
"name": "Example Template Name",
121+
"requirements": {
122+
"forms": [
123+
{
124+
"id": "bba23233-bb35-4d42-a1d1-5d33fcaa6d6b",
125+
"required": true
126+
}
127+
]
128+
}
129+
},
130+
"relationships": {
131+
"forms": {
132+
"data": [
133+
{
134+
"id": "bba23233-bb35-4d42-a1d1-5d33fcaa6d6b",
135+
"type": "forms"
136+
}
137+
]
138+
},
139+
"organisation": {
140+
"data": {
141+
"id": "7c15ae62-91e3-45d4-babc-2652764d0125",
142+
"type": "organisations"
143+
}
144+
}
145+
},
146+
"meta": {
147+
"created_at": "2025-06-04T12:48:27.452Z",
148+
"updated_at": "2025-06-04T12:48:27.452Z"
149+
}
150+
}
151+
]
152+
}

0 commit comments

Comments
 (0)