Form Runner offline embedding API
Availability
[SINCE Orbeon Forms 2021.1]
As of Orbeon Forms 2023.1 this feature is considered in "beta" status.
Introduction
See the Offline test feature for a rationale and for the quickest way to preview this feature.
Uses
As of Orbeon Forms 2023.1:
The Form Runner offline embedding API is mainly relevant for users who want to embed Orbeon Forms in another application, typically a web view within a mobile app.
Orbeon Forms does not yet provide an offline mode for end users of the regular Form Runner web application. For more about this, see this issue.
APIs
Server-side form compilation API
An Orbeon form first needs to be compiled into a serialized representation. This is done on the server-side, and the result is a zip file containing the compiled form definition, as well as any resources referenced by the form.
In order to obtain a compiled form definition, you call the following service endpoint with an HTTP GET
request:
The resulting binary data can later be passed to the client-side renderForm()
API.
Client-side embedding API
The API entry point is from the global JavaScript object:
You must provide a specific configuration before running renderForm()
and other methods:
The first argument is a SubmissionProvider
instance, which is described below. This tells Form Runner how to read and write form data, as well as how to call services.
The second, optional parameter, is the size of the compiled form cache, in number of compiled forms. This is used to limit the number of compiled forms kept in memory.
The effect of calling configure()
is global. It applies to all subsequent calls to renderForm()
.
You can render the form in a given HTML element with id orbeon-wrapper
as follows:
This returns a Promise
which resolves when the form initialization is complete.
The compiledFormDefinition
parameter is the binary data obtained from the server-side compilation API, as a Uint8Array
.
The third argument is a JavaScript object with the following properties:
appName
string
formName
string
formVersion
number
positive integer
mode
string
new
|edit
|view
documentId
string?
for edit
|view
modes only
queryString
string?
optional
headers
Headers?
optional
formData
string?
for POST
ed form data
If formData
is defined, it must be a string containing form data in XML format. This is the equivalent of performing an HTTP POST
when online.
Note that regular reading/writing data is done through the SubmissionProvider
interface, which is described below.
If you are only loading one form in the page, you can chain the calls as follows:
Client-side submission provider API
When running in offline mode, Form Runner needs to be able to:
read form data
write form data
call services
This is done through the SubmissionProvider
interface, which is implemented by the embedder:
SubmissionProvider
SubmissionProvider
This is the main entry point. You implement your own SubmissionProvider
class and pass it to the ORBEON.fr.FormRunnerOffline.configure()
function.
This class has two methods:
submit()
synchronous call
only method supported until Orbeon Forms 2023.1
deprecated as of Orbeon Forms 2023.1
submitAsync()
asynchronous call
used for saving data
used for asynchronous service calls
SubmissionRequest
SubmissionRequest
This is the request object passed to the submit()
and submitAsync()
methods.
It has the following properties:
method
possible HTTP method
GET
,POST
,PUT
,DELETE
,HEAD
url
JavaScript
URL
objectsee the URL documentation
this is the URL of the resource to be accessed
headers
JavaScript
Headers
objectsee the Headers documentation
HTTP headers associated with the request, including:
Content-Type
: for methods that have a body:POST
,PUT
Orbeon-Workflow-Stage
: workflow stage informationOrbeon-Form-Definition-Version
: form definition version information
body
optional for methods that don't have a body:
GET
,HEAD
,DELETE
, in which case it can be leftundefined
required for methods that have a body:
POST
,PUT
Uint8Array
orReadableStream<Uint8Array>
Uint8Array
is used for synchronous callsReadableStream<Uint8Array>
is used for asynchronous callssee also the JavaScript ReadableStream documentation
Using ReadableStream
is the most difficult part of this API. This standard JavaScript API exposes a way to get data in a streamable way, in chunks, asynchronously. This means that you don't need to have all your data in memory at once, and you can start processing data as soon as it is available. You can also move data across network or application boundaries asynchronously, for example between a Web View and a native app.
This is an example of a demo SubmissionProvider implementation which uses ReadableStream
. The example is written in Scala, but the same exact logic applies to a JavaScript or TypeScript implementation.
SubmissionResponse
SubmissionResponse
This is the response object returned by the submit()
and submitAsync()
methods.
It has the following properties:
statusCode
HTTP status code
200
,201
,204
,400
,401
,403
,404
,500
, etc.
headers
JavaScript
Headers
objectsee the Headers documentation
HTTP headers associated with the response, including:
Content-Type
: for methods that return a body:POST
,GET
Orbeon-Workflow-Stage
: workflow stage information when reading form data
body
for methods that return a body:
POST
,GET
, leftundefined
for methods that don't return a bodyUint8Array
orReadableStream<Uint8Array>
Uint8Array
is used for synchronous calls or asynchronous calls that return a body synchronouslyReadableStream<Uint8Array>
is used for asynchronous calls that return a body asynchronouslysee the ReadableStream documentation
Form Runner calls
Introduction
Form Runner calls are made through the SubmissionProvider
interface.
Form Runner persistence calls (saving and reading form data and attachments) use submitAsync()
instead of submit()
in call cases.
Saving form data
Form Runner starts by saving attachments, if needed (see below). Only if saving all attachments succeeds does Form Runner save the form data itself.
Then Form Runner saves the form data itself.
submitAsync()
is calledthe
method
isPUT
the
url
is the URL of the form data resourcefor
save-final
:/fr/service/persistence/crud/$app/$form/data/$document/data.xml
for
save-progress
:/fr/service/persistence/crud/$app/$form/draft/$document/data.xml
this is only called if autosave is enabled
the
headers
include:Content-Type
:application/xml
Orbeon-Workflow-Stage
: workflow stage informationOrbeon-Form-Definition-Version
: form definition version information
The XML body is passed as a ReadableStream<Uint8Array>
. You can convert that to a Promise<Uint8Array>
if needed.
Reading form data
When Form Runner needs to read form data, it calls:
submitAsync()
the
method
isGET
the
url
is the URL of the form data resourcefor reading final data:
/fr/service/persistence/crud/$app/$form/data/$document/data.xml
for reading autosaved data:
/fr/service/persistence/crud/$app/$form/draft/$document/data.xml
the
headers
must include:Content-Type
:application/xml
Orbeon-Workflow-Stage
: workflow stage information, if applicable (in particular if this was specified when the data was saved)Orbeon-Form-Definition-Version
: form definition version
Here, the XML body can be returned as a ReadableStream<Uint8Array>
or directly as a Uint8Array
.
Saving attachments
This works like saving data, except that:
the
url
is the URL of the attachment resourcefor
save-final
:/fr/service/persistence/crud/$app/$form/data/$document/$attachment.bin
for
save-progress
:/fr/service/persistence/crud/$app/$form/draft/$document/$attachment.bin
this is only called if autosave is enabled
the
headers
include:Content-Type
:application/octet-stream
Orbeon-Form-Definition-Version
: form definition version
Currently, Form Runner always uses a .bin
extension for attachments, even if the original file had a different extension. Similarly, Form Runner always uses application/octet-stream
as the Content-Type
for attachments, even if the original file had a different Content-Type
.
Form Runner will issue one call to submitAsync()
per attachment.
Again, an attachment body is passed as a ReadableStream<Uint8Array>
. The main difference, compared with form data, is that attachments can be typically much larger: from a few megabytes to gigabytes, when images and videos are attached, for example. This makes it all the more important to leverage ReadableStream
to avoid having to load the entire attachment in memory at once.
Reading attachments
TODO
Service calls
Form Runner will issue service calls through the SubmissionProvider
API as well. This includes form author-defined service calls in Form Builder, as well as services calls specified in the Dynamic Dropdown, in particular.
For example a Dynamic Dropdown might call a service at the following URL with the GET
method
Make sure you return a Content-Type
header with a value of application/xml
or application/json
, depending on the format you return.
The service can then retrieve the list of countries from a database, and return the list as XML or JSON in a response body. Here again, you can return either a ReadableStream<Uint8Array>
or a Uint8Array
.
See also
Last updated