Skip to content

Commit 9a6c8a2

Browse files
committed
feat: support different methods of retrieving submissions, and their includes
1 parent 75184da commit 9a6c8a2

File tree

5 files changed

+302
-25
lines changed

5 files changed

+302
-25
lines changed

src/main/kotlin/com/ctrlhub/core/datacapture/FormSubmissionVersionsRouter.kt

Lines changed: 163 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,56 @@ package com.ctrlhub.core.datacapture
33
import com.ctrlhub.core.Api
44
import com.ctrlhub.core.datacapture.resource.FormSubmissionVersion
55
import com.ctrlhub.core.datacapture.response.FormSchema
6+
import com.ctrlhub.core.datacapture.response.Form
7+
import com.ctrlhub.core.iam.response.User
8+
import com.ctrlhub.core.media.response.Image
69
import com.ctrlhub.core.router.Router
710
import io.ktor.client.HttpClient
811
import io.ktor.http.ContentType
912
import com.ctrlhub.core.api.response.PaginatedList
13+
import com.ctrlhub.core.router.request.FilterOption
14+
import com.ctrlhub.core.router.request.JsonApiIncludes
15+
import com.ctrlhub.core.router.request.RequestParametersWithIncludes
16+
17+
@Suppress("unused")
18+
enum class FormSubmissionVersionIncludes(val key: String) : JsonApiIncludes {
19+
Author("author"),
20+
Form("form"),
21+
Organisation("organisation"),
22+
Schema("schema"),
23+
Submission("submission"),
24+
PayloadImages("payload_images"),
25+
PayloadOperations("payload_operations"),
26+
PayloadProperties("payload_properties"),
27+
PayloadUsers("payload_users");
28+
29+
override fun value(): String = key
30+
}
31+
32+
class FormSubmissionVersionRequestParameters(
33+
offset: Int = 0,
34+
limit: Int = 100,
35+
filterOptions: List<FilterOption> = emptyList(),
36+
includes: List<FormSubmissionVersionIncludes> = emptyList()
37+
) : RequestParametersWithIncludes<FormSubmissionVersionIncludes>(
38+
offset = offset,
39+
limit = limit,
40+
filterOptions = filterOptions,
41+
includes = includes,
42+
)
1043

1144
class FormSubmissionVersionsRouter(httpClient: HttpClient) : Router(httpClient) {
1245
/**
13-
* Create a new form submission version
46+
* Create a new form submission version.
47+
*
48+
* Sends a POST request to create a submission (and implicitly its version) for the given form.
49+
*
50+
* @param organisationId the organisation UUID the form belongs to
51+
* @param formId the form UUID to create a submission for
52+
* @param schemaId the schema UUID to associate with the created version
53+
* @param payload arbitrary map representing the submission payload (field id -> value)
54+
* @return the created and hydrated FormSubmissionVersion instance
55+
* @throws Exception on network or parsing errors
1456
*/
1557
suspend fun create(organisationId: String, formId: String, schemaId: String, payload: Map<String, Any>): FormSubmissionVersion {
1658
return postJsonApiResource(
@@ -31,26 +73,136 @@ class FormSubmissionVersionsRouter(httpClient: HttpClient) : Router(httpClient)
3173
}
3274

3375
/**
34-
* Get all submission versions for a given form (paginated)
76+
* Get all submission versions for a given form (paginated).
77+
*
78+
* Returns a paginated list of FormSubmissionVersion resources. Supports JSON:API include options
79+
* to hydrate related resources (author, form, schema, images, etc.) via the requestParameters.
80+
*
81+
* @param organisationId the organisation UUID
82+
* @param formId the form UUID
83+
* @param submissionId the submission UUID to list versions for
84+
* @param requestParameters optional paging, filter and include parameters
85+
* @return PaginatedList containing FormSubmissionVersion items and pagination meta
3586
*/
36-
suspend fun all(organisationId: String, formId: String, submissionId: String): PaginatedList<FormSubmissionVersion> {
87+
suspend fun all(
88+
organisationId: String,
89+
formId: String,
90+
submissionId: String,
91+
requestParameters: FormSubmissionVersionRequestParameters = FormSubmissionVersionRequestParameters()
92+
): PaginatedList<FormSubmissionVersion> {
3793
return fetchPaginatedJsonApiResources(
3894
"/v3/orgs/$organisationId/data-capture/forms/$formId/submissions/$submissionId/versions",
39-
queryParameters = emptyMap(),
40-
FormSubmissionVersion::class.java,
41-
FormSchema::class.java
95+
requestParameters.toMap(),
96+
User::class.java,
97+
Form::class.java,
98+
FormSchema::class.java,
99+
Image::class.java
100+
)
101+
}
102+
103+
/**
104+
* Get all submission versions for a specific submission (paginated).
105+
*
106+
* Overload that lists versions by submission id without a form id in the path.
107+
*
108+
* @param organisationId the organisation UUID
109+
* @param submissionId the submission UUID to list versions for
110+
* @param requestParameters optional paging, filter and include parameters
111+
* @return PaginatedList containing FormSubmissionVersion items and pagination meta
112+
*/
113+
suspend fun all(
114+
organisationId: String,
115+
submissionId: String,
116+
requestParameters: FormSubmissionVersionRequestParameters = FormSubmissionVersionRequestParameters()
117+
): PaginatedList<FormSubmissionVersion> {
118+
return fetchPaginatedJsonApiResources(
119+
"/v3/orgs/$organisationId/data-capture/submissions/$submissionId/versions",
120+
requestParameters.toMap(),
121+
User::class.java,
122+
Form::class.java,
123+
FormSchema::class.java,
124+
Image::class.java
125+
)
126+
}
127+
128+
/**
129+
* Get all form submission versions across an organisation (paginated).
130+
*
131+
* Useful for admin-style listing of all submission versions. Supports includes via requestParameters.
132+
*
133+
* @param organisationId the organisation UUID
134+
* @param requestParameters optional paging, filter and include parameters
135+
* @return PaginatedList containing FormSubmissionVersion items and pagination meta
136+
*/
137+
suspend fun all(
138+
organisationId: String,
139+
requestParameters: FormSubmissionVersionRequestParameters = FormSubmissionVersionRequestParameters()
140+
): PaginatedList<FormSubmissionVersion> {
141+
return fetchPaginatedJsonApiResources(
142+
"/v3/orgs/$organisationId/data-capture/form-submission-versions",
143+
requestParameters.toMap(),
144+
User::class.java,
145+
Form::class.java,
146+
FormSchema::class.java,
147+
Image::class.java
42148
)
43149
}
44150

45151
/**
46-
* Get a single submission version
152+
* Get a single submission version.
153+
*
154+
* Fetches and hydrates a single FormSubmissionVersion resource by id. Supports JSON:API include
155+
* parameters via requestParameters to hydrate related resources.
156+
*
157+
* @param organisationId the organisation UUID
158+
* @param formId the form UUID the submission belongs to
159+
* @param submissionId the submission UUID
160+
* @param versionId the version UUID to fetch
161+
* @param requestParameters optional paging, filter and include parameters
162+
* @return the hydrated FormSubmissionVersion
47163
*/
48-
suspend fun one(organisationId: String, formId: String, submissionId: String, versionId: String): FormSubmissionVersion {
164+
suspend fun one(
165+
organisationId: String,
166+
formId: String,
167+
submissionId: String,
168+
versionId: String,
169+
requestParameters: FormSubmissionVersionRequestParameters = FormSubmissionVersionRequestParameters()
170+
): FormSubmissionVersion {
49171
return fetchJsonApiResource(
50172
"/v3/orgs/$organisationId/data-capture/forms/$formId/submissions/$submissionId/versions/$versionId",
51-
queryParameters = emptyMap(),
52-
FormSubmissionVersion::class.java,
53-
FormSchema::class.java
173+
requestParameters.toMap(),
174+
User::class.java,
175+
Form::class.java,
176+
FormSchema::class.java,
177+
Image::class.java
178+
)
179+
}
180+
181+
/**
182+
* Get a single submission version by submission id (no form in path).
183+
*
184+
* Fetches and hydrates a single FormSubmissionVersion resource by id using the endpoint that does
185+
* not include the form id in the path.
186+
*
187+
* @param organisationId the organisation UUID
188+
* @param submissionId the submission UUID
189+
* @param versionId the version UUID to fetch
190+
* @param requestParameters optional paging, filter and include parameters
191+
* @return the hydrated FormSubmissionVersion
192+
*/
193+
suspend fun one(
194+
organisationId: String,
195+
submissionId: String,
196+
versionId: String,
197+
requestParameters: FormSubmissionVersionRequestParameters = FormSubmissionVersionRequestParameters()
198+
): FormSubmissionVersion {
199+
return fetchJsonApiResource(
200+
"/v3/orgs/$organisationId/data-capture/submissions/$submissionId/versions/$versionId",
201+
requestParameters.toMap(),
202+
User::class.java,
203+
Form::class.java,
204+
FormSchema::class.java,
205+
Image::class.java
54206
)
55207
}
56208
}

src/main/kotlin/com/ctrlhub/core/datacapture/resource/FormSubmissionVersion.kt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package com.ctrlhub.core.datacapture.resource
22

33
import com.ctrlhub.core.datacapture.response.FormSchema
4+
import com.ctrlhub.core.datacapture.response.Form
5+
import com.ctrlhub.core.geo.Property
6+
import com.ctrlhub.core.iam.response.User
7+
import com.ctrlhub.core.media.response.Image
8+
import com.ctrlhub.core.projects.operations.response.Operation
49
import com.fasterxml.jackson.annotation.JsonCreator
510
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
611
import com.fasterxml.jackson.annotation.JsonProperty
@@ -11,7 +16,7 @@ import com.github.jasminb.jsonapi.annotations.Type
1116

1217
@JsonIgnoreProperties(ignoreUnknown = true)
1318
@Type("form-submission-versions")
14-
data class FormSubmissionVersion @JsonCreator constructor(
19+
class FormSubmissionVersion @JsonCreator constructor(
1520
@Id(StringIdHandler::class)
1621
var id: String = "",
1722

@@ -23,4 +28,25 @@ data class FormSubmissionVersion @JsonCreator constructor(
2328

2429
@Relationship("schema")
2530
var schema: FormSchema? = null,
31+
32+
@JsonProperty("meta")
33+
var meta: Map<String, Any>? = null,
34+
35+
@Relationship("author")
36+
var author: User? = null,
37+
38+
@Relationship("form")
39+
var form: Form? = null,
40+
41+
@Relationship("payload_images")
42+
var payloadImages: List<Image>? = null,
43+
44+
@Relationship("payload_operations")
45+
var payloadOperations: List<Operation>? = null,
46+
47+
@Relationship("payload_properties")
48+
var payloadProperties: List<Property>? = null,
49+
50+
@Relationship("payload_users")
51+
var payloadUsers: List<User>? = null,
2652
)

src/main/kotlin/com/ctrlhub/core/http/KtorClientFactory.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.ctrlhub.core.Config
44
import io.ktor.client.HttpClient
55
import io.ktor.client.HttpClientConfig
66
import io.ktor.client.engine.okhttp.OkHttp
7+
import io.ktor.client.plugins.HttpTimeout
78
import io.ktor.client.plugins.UserAgent
89
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
910
import io.ktor.client.plugins.defaultRequest
@@ -41,6 +42,11 @@ object KtorClientFactory {
4142
encodeDefaults = true
4243
})
4344
}
45+
install(HttpTimeout) {
46+
requestTimeoutMillis = 15_000
47+
connectTimeoutMillis = 15_000
48+
socketTimeoutMillis = 15_000
49+
}
4450
install(UserAgent) {
4551
agent = Config.userAgent
4652
}

src/main/kotlin/com/ctrlhub/core/media/response/Image.kt

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,51 @@ import com.fasterxml.jackson.annotation.JsonCreator
44
import com.fasterxml.jackson.annotation.JsonProperty
55
import com.github.jasminb.jsonapi.StringIdHandler
66
import com.github.jasminb.jsonapi.annotations.Id
7+
import com.github.jasminb.jsonapi.annotations.Meta
78
import com.github.jasminb.jsonapi.annotations.Type
89

910
@Type("images")
10-
data class Image @JsonCreator constructor(
11-
@Id(StringIdHandler::class) val id: String = "",
12-
@JsonProperty("mime_type") val mimeType: String,
13-
@JsonProperty("extension") val extension: String,
14-
@JsonProperty("width") val width: Int,
15-
@JsonProperty("height") val height: Int,
16-
@JsonProperty("bytes") val bytes: Long,
17-
@JsonProperty("dimensions") val dimensions: List<ImageDimensions> = emptyList()
18-
)
11+
class Image @JsonCreator constructor(
12+
@Id(StringIdHandler::class) var id: String = "",
13+
@JsonProperty("mime_type") var mimeType: String = "",
14+
@JsonProperty("extension") var extension: String = "",
15+
@JsonProperty("width") var width: Int = 0,
16+
@JsonProperty("height") var height: Int = 0,
17+
@JsonProperty("bytes") var bytes: Long = 0L,
18+
@JsonProperty("dimensions") var dimensions: List<ImageDimensions> = emptyList(),
1919

20-
data class ImageDimensions(
21-
val width: Int,
22-
val height: Int,
23-
)
20+
@Meta var meta: ImageMeta? = null
21+
) {
22+
constructor(): this(
23+
id = "",
24+
mimeType = "",
25+
extension = "",
26+
width = 0,
27+
height = 0,
28+
bytes = 0L,
29+
dimensions = emptyList(),
30+
meta = null
31+
)
32+
}
33+
34+
class ImageDimensions(
35+
var width: Int = 0,
36+
var height: Int = 0,
37+
) {
38+
constructor(): this(width = 0, height = 0)
39+
}
40+
41+
class ImageLink(
42+
var url: String = "",
43+
var width: Int? = null,
44+
var height: Int? = null
45+
) {
46+
constructor(): this(url = "", width = null, height = null)
47+
}
48+
49+
class ImageMeta(
50+
var links: List<ImageLink> = emptyList(),
51+
@JsonProperty("created_at") var createdAt: String? = null
52+
) {
53+
constructor(): this(links = emptyList(), createdAt = null)
54+
}

0 commit comments

Comments
 (0)