-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Engine/client API: submission #188
Comments
@sadiqkhoja does this sound like it'll work from your end? It should be pretty much what we discussed previously, but want to confirm it feels right before I start implementation (which I'd like to do). @lognaturel since you mentioned taking a look at this too, please also feel free to let me know if you have any concerns about this direction. |
This is excellent 🎉. I specially like the option of having submissions to be chunked or not. I have few thoughts/questions though:
PS: "Pit of Success" was originally coined by Rico Mariani 🙃 |
🎉
I'm less familiar with these REST APIs (I wish someone had mentioned this sooner!), but at a glance it looks like you could do this yes. If you think it makes sense to have another "pit of success" structure tailored to these APIs, I'm happy to do another pass. I don't know that it'd look very different, other than probably splitting the known
I do think it'll tend to be almost instantaneous. Even so, this question is a great prompt to consider changing its signature to return a On the question of method name: my reasoning for the
This design intentionally avoids throwing, and this has been a longstanding goal for any fallible aspect of the engine/client API. This is what I've meant when I mention a " The problems with exceptions are numerous (and there's tons of literature on the topic). But most pragmatically for our purposes:
In the longer term, it's quite likely more of the engine/client interface will have a similar shape. If anything, I think it's worth considering making that intent more formally clear here, by giving it an explicit
If my reading of the OpenRosa Protocol is right, I believe we can do this iff:
I think it would be a bit odd to support this in the proposed design. Why would the engine handle this specific subset of the OpenRosa Protocol? If it does, why wouldn't it also handle the portion of IO to actually send the submission and attachments? I'm open to considering this, but I want to better understand the intent.
Thanks for the correction! (And I was kinda surprised I didn't easily find an earlier reference.) |
|
I assumed you brought it up because you anticipate using it in the client<->host integration. If that's not the case, maybe you can elaborate on what motivates the question in the first place? In any case, I expect that you are probably going to be either the initial primary user of these interfaces, or taking point to facilitate their downstream usage. So my instinct is that most of these are your calls to make. Maybe it would be better to take a step back. How do you anticipate using submission data produced by the engine? Either in the client, or in a handoff to hosts. If anything about this design isn't serving that, now is the best time to find that out.
It is optional. If the engine doesn't know about it, it will produce a single, unchunked ( |
I have been imagining that for Central, submission would be done in a layer in Central frontend with the OpenRosa API to match Collect but I admit I haven't thought about it deeply. I think the main tradeoff between that and the Central REST API would be around how submission attachments are submitted. Either way we'll need to do some design work around what happens if connection cuts out part way through a submission and I think we can end up with the same user-facing result. @sadiqkhoja do you see a strong reason to use one vs the other?
👍 Yes, I'm interested in hearing thoughts on this too. There will certainly be use cases outside of ODK Central for APIs of various shapes.
This seems ok, I think. @eyelidlessness you mention
This doesn't feel necessary to me! I agree with what you've both said about getting the chunk size being best handled in an OpenRosa-specific layer. |
My instinct was that—if there is a compelling reason to produce different outputs for the different APIs—it would be additive. I think being well prepared to support the OpenRosa Protocol, while maybe not a hard requirement, seems like the most obvious default. I definitely wouldn't want to make it more difficult, without good reason, in pursuit of other conveniences. I imagine it'd probably accessed by an enum option—e.g. (totally off the cuff) something like: const submission = await root.prepareSubmission({ format: 'openrosa' | 'odk-central' }); I'm not sure how I feel about the I also think chunking (and the I suppose another reasonable option is to not produce an API-specific We could instead address OpenRosa support separately from this engine API. That could be handled by a separate export, package, recommended code snippet, etc. So instead of being additive, it would be composable—i.e. engine produces more general structure, hypothetical OpenRosa thing is something like That's slightly more onerous than the original proposal, but doesn't feel prohibitively so. Writing this response, I keep going back and forth. But the more I think about it, the more I think |
I suppose the other obvious, totally general structure would just be |
My inclination is to hand over submission data produced by the engine to the host application, which will enable it to enhance/compose the data further. A feature that I think a lot of is to render two separate Forms together, on submit combined the data of both Form into one, and then send it to the server. I agree that good support for OpenRosa Protocol should be our primary goal; this will be helpful for non-Central servers to adopt Web Forms. At the same time, I would like to see support for REST APIs because we are building such a powerful tool that people outside of the ODK ecosystem can benefit from this too - definitely this doesn't need to happen now but it would be great if we have a point of extension/addition for that. Hence I like the idea of |
We discussed this briefly and will stick with |
When we last discussed this, we also talked about the possibility of allowing clients to specify I still think this is a good idea! That said, I took a little time this morning to update the affected validation interfaces, and it seemed complicated enough that we might want to give it more thought when we actually introduce functionality producing submission attachments. These are the main complications I encountered:
I think these are great areas to explore! But my inclination now is to defer them for the first pass on submission API implementation, so we can give them more dedicated thought, likely in the context where we introduce real submission attachment functionality we can test against. |
I think I'd also like to remove On that note, here I'll also capture some clarification from a quick chat with @lognaturel:
Footnotes
|
This is essentially the design originally proposed in #188, with the following modifications: - Add a `SubmissionResult.status` and corresponding `SubmissionResult` variant, to convey submission data with attachments exceeding a client-specified `maxSize` - Add corresponding `MaxSizeViolation` interface for that violation scenario… - … including a todo discussing potential benefit of moving `maxSize` configuration to form init; allowing potential to surface node-level max size violations during form filling, rather than only at submission time - Account for controlled/uncontrolled repeat range variants, which was merged while the design has been in flight - As discussed in the design thread, revise `RootNode.prepareSubmission` to return a `Promise` - Add clarifying JSDoc detail to `RootNode.prepareSubmission`, largely expanding on reasoning behind `SubmissionResult` statuses (and production of submission result in “error” states), and reasoning for revision to return a `Promise`
This is essentially the design originally proposed in #188, with the following modifications: - Add a `SubmissionResult.status` and corresponding `SubmissionResult` variant, to convey submission data with attachments exceeding a client-specified `maxSize` - Add corresponding `MaxSizeViolation` interface for that violation scenario… - … including a todo discussing potential benefit of moving `maxSize` configuration to form init; allowing potential to surface node-level max size violations during form filling, rather than only at submission time - Account for controlled/uncontrolled repeat range variants, which was merged while the design has been in flight - As discussed in the design thread, revise `RootNode.prepareSubmission` to return a `Promise` - Add clarifying JSDoc detail to `RootNode.prepareSubmission`, largely expanding on reasoning behind `SubmissionResult` statuses (and production of submission result in “error” states), and reasoning for revision to return a `Promise`
This is essentially the design originally proposed in #188, with the following modifications: - Add a `SubmissionResult.status` and corresponding `SubmissionResult` variant, to convey submission data with attachments exceeding a client-specified `maxSize` - Add corresponding `MaxSizeViolation` interface for that violation scenario… - … including a todo discussing potential benefit of moving `maxSize` configuration to form init; allowing potential to surface node-level max size violations during form filling, rather than only at submission time - Account for controlled/uncontrolled repeat range variants, which was merged while the design has been in flight - As discussed in the design thread, revise `RootNode.prepareSubmission` to return a `Promise` - Add clarifying JSDoc detail to `RootNode.prepareSubmission`, largely expanding on reasoning behind `SubmissionResult` statuses (and production of submission result in “error” states), and reasoning for revision to return a `Promise`
This is essentially the design originally proposed in #188, with the following modifications: - Add a `SubmissionResult.status` and corresponding `SubmissionResult` variant, to convey submission data with attachments exceeding a client-specified `maxSize` - Add corresponding `MaxSizeViolation` interface for that violation scenario… - … including a todo discussing potential benefit of moving `maxSize` configuration to form init; allowing potential to surface node-level max size violations during form filling, rather than only at submission time - Account for controlled/uncontrolled repeat range variants, which was merged while the design has been in flight - As discussed in the design thread, revise `RootNode.prepareSubmission` to return a `Promise` - Add clarifying JSDoc detail to `RootNode.prepareSubmission`, largely expanding on reasoning behind `SubmissionResult` statuses (and production of submission result in “error” states), and reasoning for revision to return a `Promise`
More or less done in #226! Anything else was punted for later, related to pending functionality (e.g. submission attachments). |
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 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 anyconstraint
orrequired
violations (or violates any other future validity conditions, such as any implied by data types) its status will bepending
. Otherwise it will beready
.violations
: ifstatus
ispending
, this will be a reflection of the same violations produced byRootNode.validationState.violations
. Ifstatus
isready
, 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 theFormData
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'
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 subsequentFormData
objects for chunked submission. (Otherwise the engine will provide a single ['monolithic'
if explicitly specified inSubmissionOptions
]SubmissionData
object.)Notes on
SubmissionData
andFormData
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:
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 ↩
The text was updated successfully, but these errors were encountered: