I'm opening this issue to start discussion of a design for the engine/client interface for form submissions, in support of:
In past API design issues, I've generally tried to provide at least a couple of competing options. This has helped to identify some of the pros and cons of a particular design, even where I have a bias toward one of the solutions.
In this case, we've already discussed as a team most of the shape I expect this design to take, and in prior discussions we seemed to reach a general consensus. So this issue mostly serves as a more formal reference for the concepts we've discussed.
The proposed interface is currently detailed in 505a5ce (on the design/submission branch). I'll briefly outline the concepts here as a point of reference for further discussion:
-
RootNode.prepareSubmission(options?: SubmissionOptions): SubmissionResult: called by clients to prepare a submission
-
SubmissionResult: provides all of the information a client needs to proceed with a submission
-
status: 'pending' | 'ready': specifies whether the provided submission data is ready to be submitted by a client. This is effectively a reflection of the form instance's validity state. At call time, if the submission has any constraint or required violations (or violates any other future validity conditions, such as any implied by data types) its status will be pending. Otherwise it will be ready.
-
violations: if status is pending, this will be a reflection of the same violations produced by RootNode.validationState.violations. If status is ready, this will be null.
-
definition: SubmissionDefinition:
-
data: SubmissionData | [SubmissionData, ...SubmissionData[]] (as determined by SubmissionOptions)
-
SubmissionDefinition: provides submission-pertinent information as provided by the form, or as provided by the engine in accordance with pertinent ODK XForms specifications. Most likely to be of interest to a client:
submissionAction—corresponds to <submission action>)
submissionMethod: 'post'—corresponds to <submission method>, always normalized to 'post' where forms provide the deprecated form-data-post. Always defined, unless discussion suggests additional nuance not clear from the spec.
-
SubmissionData: a more specific variation of the FormData web standard, which guarantees presence of the form instance data itself, i.e. the XForm Part as defined by the OpenRosa Protocol's Form Submission API.
-
SubmissionOptions.chunked: 'chunked' and SubmissionOptions.maxSize: number: if specified as options to RootNode.prepareSubmission, the engine will chunk the submission data it provides to the client, producing [SubmissionData, ...FormData[]] where form attachments are chunked into subsequent FormData objects for chunked submission. (Otherwise the engine will provide a single ['monolithic' if explicitly specified in SubmissionOptions] SubmissionData object.)
Notes on SubmissionData and FormData
The intent of this aspect of the design is that the engine will provide submission data in a format suitable for clients to initiate any network requests (or hand that responsibility off to a host application)...
-
As specified by the OpenRosa Protocol's Form Submission API, and
-
With the standard and idiomatic web standard Fetch API.
It's expected that deferring to ODK specifications and web standards like these will strike the best balance for clients between:
-
Flexible usage: the engine does not dictate how or where submission occurs, only facilitating the aspects core to other aspects of the engine's responsibilities
-
The "pit of success"1: the engine's facilitation is consistent with expected (typical, idiomatic) usage whether implemented by a client, a host application, or some unknown third thing
Alternative: engine performs submission
In the spirit of past engine/client API design proposals, I do want to provide an alternative option so we have something to contrast. The most obvious alternative would be to have the engine perform submission.
This would likely involve the engine's submission API accepting a fetch-like option similar to the configuration it currently uses to retrieve XForm XML definitions (and is expected to do for form attachments or any other form-referenced resources).
This is a perfectly reasonable option, and we've discussed it as a team as well. I don't recall what motivating factors influenced it as a stronger contender. Off the top of my head, an obvious benefit would be the engine handling common failure modes (such as poor network conditions).
I'll caution that if we went with this option, most of what's in the primary proposal would either:
- Still be present, but internal
- More likely: be present and exposed to clients, which could then choose between either approach
If we want to go this direction, I would want to strongly consider some layering of responsibilities: the engine providing a single interface (likely consistent with the primary proposal), and then also providing a network/IO facilitation layer which consumes that base API.
I'm opening this issue to start discussion of a design for the engine/client interface for form submissions, in support of:
In past API design issues, I've generally tried to provide at least a couple of competing options. This has helped to identify some of the pros and cons of a particular design, even where I have a bias toward one of the solutions.
In this case, we've already discussed as a team most of the shape I expect this design to take, and in prior discussions we seemed to reach a general consensus. So this issue mostly serves as a more formal reference for the concepts we've discussed.
The proposed interface is currently detailed in 505a5ce (on the
design/submissionbranch). I'll briefly outline the concepts here as a point of reference for further discussion:RootNode.prepareSubmission(options?: SubmissionOptions): SubmissionResult: called by clients to prepare a submissionSubmissionResult: provides all of the information a client needs to proceed with a submissionstatus: 'pending' | 'ready': specifies whether the provided submission data is ready to be submitted by a client. This is effectively a reflection of the form instance's validity state. At call time, if the submission has anyconstraintorrequiredviolations (or violates any other future validity conditions, such as any implied by data types) its status will bepending. Otherwise it will beready.violations: ifstatusispending, this will be a reflection of the same violations produced byRootNode.validationState.violations. Ifstatusisready, this will benull.definition: SubmissionDefinition:data: SubmissionData | [SubmissionData, ...SubmissionData[]](as determined bySubmissionOptions)SubmissionDefinition: provides submission-pertinent information as provided by the form, or as provided by the engine in accordance with pertinent ODK XForms specifications. Most likely to be of interest to a client:submissionAction—corresponds to<submission action>)submissionMethod: 'post'—corresponds to<submission method>, always normalized to'post'where forms provide the deprecatedform-data-post. Always defined, unless discussion suggests additional nuance not clear from the spec.SubmissionData: a more specific variation of theFormDataweb standard, which guarantees presence of the form instance data itself, i.e. the XForm Part as defined by the OpenRosa Protocol's Form Submission API.SubmissionOptions.chunked: 'chunked'andSubmissionOptions.maxSize: number: if specified as options toRootNode.prepareSubmission, the engine will chunk the submission data it provides to the client, producing[SubmissionData, ...FormData[]]where form attachments are chunked into subsequentFormDataobjects for chunked submission. (Otherwise the engine will provide a single ['monolithic'if explicitly specified inSubmissionOptions]SubmissionDataobject.)Notes on
SubmissionDataandFormDataThe intent of this aspect of the design is that the engine will provide submission data in a format suitable for clients to initiate any network requests (or hand that responsibility off to a host application)...
As specified by the OpenRosa Protocol's Form Submission API, and
With the standard and idiomatic web standard Fetch API.
It's expected that deferring to ODK specifications and web standards like these will strike the best balance for clients between:
Flexible usage: the engine does not dictate how or where submission occurs, only facilitating the aspects core to other aspects of the engine's responsibilities
The "pit of success"1: the engine's facilitation is consistent with expected (typical, idiomatic) usage whether implemented by a client, a host application, or some unknown third thing
Alternative: engine performs submission
In the spirit of past engine/client API design proposals, I do want to provide an alternative option so we have something to contrast. The most obvious alternative would be to have the engine perform submission.
This would likely involve the engine's submission API accepting a
fetch-like option similar to the configuration it currently uses to retrieve XForm XML definitions (and is expected to do for form attachments or any other form-referenced resources).This is a perfectly reasonable option, and we've discussed it as a team as well. I don't recall what motivating factors influenced it as a stronger contender. Off the top of my head, an obvious benefit would be the engine handling common failure modes (such as poor network conditions).
I'll caution that if we went with this option, most of what's in the primary proposal would either:
If we want to go this direction, I would want to strongly consider some layering of responsibilities: the engine providing a single interface (likely consistent with the primary proposal), and then also providing a network/IO facilitation layer which consumes that base API.
Footnotes
Apparently coined by Jeff Atwood ↩