-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial engine/client interface: submission
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`
- Loading branch information
1 parent
9c6eb5d
commit fe8c6a7
Showing
21 changed files
with
299 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
type ODKXFormsUUID = `uuid:${string}`; | ||
|
||
/** | ||
* @see {@link https://getodk.github.io/xforms-spec/#metadata} | ||
*/ | ||
export type InstanceID = ODKXFormsUUID; | ||
|
||
/** | ||
* @see {@link https://getodk.github.io/xforms-spec/#metadata} | ||
*/ | ||
export type DeprecatedID = ODKXFormsUUID; | ||
|
||
/** | ||
* Represents a session-stable identifier for any particular node i | ||
*/ | ||
export type FormNodeID = `node:${string}`; |
12 changes: 12 additions & 0 deletions
12
packages/xforms-engine/src/client/submission/SubmissionData.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { | ||
SubmissionInstanceFile, | ||
SubmissionInstanceFileName, | ||
} from './SubmissionInstanceFile.ts'; | ||
|
||
export interface SubmissionData extends FormData { | ||
get(name: SubmissionInstanceFileName): SubmissionInstanceFile; | ||
get(name: string): FormDataEntryValue | null; | ||
|
||
has(name: SubmissionInstanceFileName): true; | ||
has(name: string): boolean; | ||
} |
16 changes: 16 additions & 0 deletions
16
packages/xforms-engine/src/client/submission/SubmissionDefinition.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export interface SubmissionDefinition { | ||
/** | ||
* @see {@link https://getodk.github.io/xforms-spec/#submission-attributes | `action` submission attribute} | ||
*/ | ||
readonly submissionAction: URL | null; | ||
|
||
/** | ||
* @see {@link https://getodk.github.io/xforms-spec/#submission-attributes | `method` submission attribute} | ||
*/ | ||
readonly submissionMethod: 'post'; | ||
|
||
/** | ||
* @see {@link https://getodk.github.io/xforms-spec/#submission-attributes | `base64RsaPublicKey` submission attribute} | ||
*/ | ||
readonly encryptionKey: string | null; | ||
} |
9 changes: 9 additions & 0 deletions
9
packages/xforms-engine/src/client/submission/SubmissionInstanceFile.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type { SubmissionInstanceFileName, SubmissionInstanceFileType } from '../constants.ts'; | ||
|
||
// Re-export for convenient `SubmissionInstanceFile` construction/access flows | ||
export type { SubmissionInstanceFileName, SubmissionInstanceFileType }; | ||
|
||
export interface SubmissionInstanceFile extends File { | ||
readonly name: SubmissionInstanceFileName; | ||
readonly type: SubmissionInstanceFileType; | ||
} |
28 changes: 28 additions & 0 deletions
28
packages/xforms-engine/src/client/submission/SubmissionOptions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
export type SubmissionChunkedType = 'chunked' | 'monolithic'; | ||
|
||
interface BaseSubmissionOptions<ChunkedType extends SubmissionChunkedType> { | ||
readonly chunked?: ChunkedType | undefined; | ||
|
||
/** | ||
* As described in the | ||
* {@link https://docs.getodk.org/openrosa-form-submission/#extended-transmission-considerations | OpenRosa Form Submission API}, | ||
* clients may obtain this value from an OpenRosa server's | ||
* `X-OpenRosa-Accept-Content-Length` header. | ||
*/ | ||
readonly maxSize?: number; | ||
} | ||
|
||
interface ChunkedSubmissionOptions extends BaseSubmissionOptions<'chunked'> { | ||
readonly maxSize: number; | ||
} | ||
|
||
interface MonolithicSubmissionOptions extends BaseSubmissionOptions<'monolithic'> { | ||
readonly chunked?: 'monolithic' | undefined; | ||
readonly maxSize?: never; | ||
} | ||
|
||
// prettier-ignore | ||
export type SubmissionOptions<ChunkedType extends SubmissionChunkedType = 'monolithic'> = { | ||
chunked: ChunkedSubmissionOptions; | ||
monolithic: MonolithicSubmissionOptions; | ||
}[ChunkedType]; |
124 changes: 124 additions & 0 deletions
124
packages/xforms-engine/src/client/submission/SubmissionResult.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import type { AnyViolation, DescendantNodeViolationReference } from '../validation.ts'; | ||
import type { SubmissionData } from './SubmissionData.ts'; | ||
import type { SubmissionDefinition } from './SubmissionDefinition.ts'; | ||
import type { SubmissionChunkedType, SubmissionOptions } from './SubmissionOptions.ts'; | ||
|
||
// prettier-ignore | ||
export type SubmissionResultStatus = | ||
// eslint-disable-next-line @typescript-eslint/sort-type-constituents | ||
| 'pending' | ||
| 'max-size-exceeded' | ||
| 'ready'; | ||
|
||
// prettier-ignore | ||
type SubmissionResultData<ChunkedType extends SubmissionChunkedType> = { | ||
chunked: readonly [SubmissionData, ...SubmissionData[]]; | ||
monolithic: SubmissionData; | ||
}[ChunkedType]; | ||
|
||
/** | ||
* Provides detail about an individual submission attachment {@link File}s which | ||
* exceeds the client-specified {@link maxSize} for a | ||
* {@link SubmissionResult<'chunked'> | chunked submission result}. Clients may | ||
* use this value to provide guidance to users. | ||
* | ||
* @todo We may want to consider (a) making {@link maxSize} a configuration the | ||
* client can provide when initializing a form instance, rather than only on | ||
* submission; and then (b) treating a maximum size violation as another kind of | ||
* node-level violation. This would go beyond the kinds of validation specified | ||
* by ODK XForms, but it would make a lot of _conceptual sense_. | ||
* | ||
* It would almost certainly be helpful to alert users to violations as the | ||
* occur, rather than only at submission time (where they have likely already | ||
* moved on). This is something clients can do without engine support, but it | ||
* would likely promote good usability patterns if the engine makes it an | ||
* obvious and uniform option at the main engine/client entrypoint. | ||
* | ||
* @todo If we consider the above, we'd want to reframe _this interface_ to | ||
* match the shape of other {@link AnyViolation | violations} (adding it as a | ||
* member of that union). We'd also likely eliminate | ||
* {@link MaxSizeExceededResult} in the process, since | ||
* {@link PendingSubmissionResult} would then cover the case. | ||
*/ | ||
interface MaxSizeViolation { | ||
/** | ||
* Specifies the index of | ||
* {@link SubmissionResultData<'chunked'> | chunked submission data} where a | ||
* submission attachment {@link File} exceeds the client-specified | ||
* {@link maxSize}. | ||
*/ | ||
readonly dataIndex: number; | ||
|
||
/** | ||
* Specifies the name of the file which exceeds the client-specified | ||
* {@link maxSize}. This name can also be used as a key to access the | ||
* violating {@link File}/submission attachment, in the {@link SubmissionData} | ||
* at the specified {@link dataIndex}. | ||
*/ | ||
readonly fileName: string; | ||
|
||
/** | ||
* Reflects the client-specified maximum size for each chunk of a | ||
* {@link SubmissionResult<'chunked'> | chunked submission result}. | ||
*/ | ||
readonly maxSize: number; | ||
|
||
/** | ||
* Details the actual size of the violating {@link File}/submission | ||
* attachment. Along with {@link maxSize}. Clients may use the delta between | ||
* this value and {@link maxSize} to provide detailed guidance to users. | ||
*/ | ||
readonly actualSize: number; | ||
} | ||
|
||
// prettier-ignore | ||
type SubmissionResultViolation = | ||
| DescendantNodeViolationReference | ||
| MaxSizeViolation; | ||
|
||
interface BaseSubmissionResult<ChunkedType extends SubmissionChunkedType> { | ||
readonly status: SubmissionResultStatus; | ||
readonly definition: SubmissionDefinition; | ||
get violations(): readonly SubmissionResultViolation[] | null; | ||
|
||
/** | ||
* Submission data may be chunked according to the | ||
* {@link SubmissionOptions.maxSize | maxSize submission option} | ||
*/ | ||
readonly data: SubmissionResultData<ChunkedType>; | ||
} | ||
|
||
interface PendingSubmissionResult<ChunkedType extends SubmissionChunkedType> | ||
extends BaseSubmissionResult<ChunkedType> { | ||
readonly status: 'pending'; | ||
get violations(): readonly DescendantNodeViolationReference[]; | ||
} | ||
|
||
interface MaxSizeExceededResult extends BaseSubmissionResult<'chunked'> { | ||
readonly status: 'max-size-exceeded'; | ||
get violations(): readonly MaxSizeViolation[]; | ||
} | ||
|
||
interface ReadySubmissionResult<ChunkedType extends SubmissionChunkedType> | ||
extends BaseSubmissionResult<ChunkedType> { | ||
readonly status: 'ready'; | ||
get violations(): null; | ||
} | ||
|
||
// prettier-ignore | ||
type CommonSubmissionResult<ChunkedType extends SubmissionChunkedType> = | ||
| PendingSubmissionResult<ChunkedType> | ||
| ReadySubmissionResult<ChunkedType>; | ||
|
||
// prettier-ignore | ||
export type ChunkedSubmissionResult = | ||
| CommonSubmissionResult<'chunked'> | ||
| MaxSizeExceededResult; | ||
|
||
export type MonolithicSubmissionResult = CommonSubmissionResult<'monolithic'>; | ||
|
||
// prettier-ignore | ||
export type SubmissionResult<ChunkedType extends SubmissionChunkedType> = { | ||
chunked: ChunkedSubmissionResult; | ||
monolithic: MonolithicSubmissionResult; | ||
}[ChunkedType]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.