Skip to content

Commit e1225e0

Browse files
authored
Merge pull request #9 from ctrl-hub/feat/expand-form-submission-version-support
feat: support different methods of retrieving submissions, and their …
2 parents 75184da + dcbc605 commit e1225e0

File tree

5 files changed

+312
-25
lines changed

5 files changed

+312
-25
lines changed

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

Lines changed: 165 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,58 @@ 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+
PayloadWorkOrders("payload_work_orders"),
29+
PayloadSchemes("payload_schemes");
30+
31+
override fun value(): String = key
32+
}
33+
34+
class FormSubmissionVersionRequestParameters(
35+
offset: Int = 0,
36+
limit: Int = 100,
37+
filterOptions: List<FilterOption> = emptyList(),
38+
includes: List<FormSubmissionVersionIncludes> = emptyList()
39+
) : RequestParametersWithIncludes<FormSubmissionVersionIncludes>(
40+
offset = offset,
41+
limit = limit,
42+
filterOptions = filterOptions,
43+
includes = includes,
44+
)
1045

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

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

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

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
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
9+
import com.ctrlhub.core.projects.schemes.response.Scheme
10+
import com.ctrlhub.core.projects.workorders.response.WorkOrder
411
import com.fasterxml.jackson.annotation.JsonCreator
512
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
613
import com.fasterxml.jackson.annotation.JsonProperty
@@ -11,7 +18,7 @@ import com.github.jasminb.jsonapi.annotations.Type
1118

1219
@JsonIgnoreProperties(ignoreUnknown = true)
1320
@Type("form-submission-versions")
14-
data class FormSubmissionVersion @JsonCreator constructor(
21+
class FormSubmissionVersion @JsonCreator constructor(
1522
@Id(StringIdHandler::class)
1623
var id: String = "",
1724

@@ -23,4 +30,31 @@ data class FormSubmissionVersion @JsonCreator constructor(
2330

2431
@Relationship("schema")
2532
var schema: FormSchema? = null,
33+
34+
@JsonProperty("meta")
35+
var meta: Map<String, Any>? = null,
36+
37+
@Relationship("author")
38+
var author: User? = null,
39+
40+
@Relationship("form")
41+
var form: Form? = null,
42+
43+
@Relationship("payload_images")
44+
var payloadImages: List<Image>? = null,
45+
46+
@Relationship("payload_operations")
47+
var payloadOperations: List<Operation>? = null,
48+
49+
@Relationship("payload_properties")
50+
var payloadProperties: List<Property>? = null,
51+
52+
@Relationship("payload_users")
53+
var payloadUsers: List<User>? = null,
54+
55+
@Relationship("payload_work_orders")
56+
var payloadWorkOrders: List<WorkOrder>? = null,
57+
58+
@Relationship("payload_schemes")
59+
var payloadSchemes: List<Scheme>? = null,
2660
)

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)