I am creating forms in Orbeon 2021.1.2 PE and I have a problem with handling error calls. I'm creating HTTP service and Action via form builder. I hope to work it out and be able to stay with Builder. I call API witch works similarly to Twitter, so it returns Error HTTP Status Codes also for business errors (see doc https://developer.twitter.com/ja/docs/basics/response-codes and https://developer.twitter.com/en/docs/twitter-ads-api/response-codes). For easiest example: if I try to find something a if its not in database, I get code 404 with a detailed error in response body.
I need to get a status code + full body (headers will be good too) to the form. I don't want to use modal windows (witch is default in Orbeon) to handle this call. It disturb users + calls often changes the entire workflow (what is visible, etc.). Some calls are even asynchronous.
What is the best solution to this problem in Orbeon? I tried a few things and find block in several places.
1] Solution through property oxf.fr.detail.process.action-service-error.*.*
Here is set default error modal window. I deleted it and found that I can get some values and write them in the form, for example:
xf:setvalue(ref="//control-1", value="event('response-status-code')")
Specifically, these are the values:
event('error-type')
event('response-status-code')
event('resource-uri')
event('response-headers') - get specific via event('response-headers')[lower-case(name) = 'content-lenght']/value
event('response-body') - This not working, it is still supported?
Because it is a global configuration, I start to working where to put these values. I tried to create my own instance but ended up with the fr:insert()
function.
I wanted to create a new instance for each submission and then enter separate values (event ('response-status-code')
, ...). However, process with fr:insert()
does not work and the documentation has poor example (https://doc.orbeon.com/form-runner/advanced/buttons-and-processes/actions-xforms#xf-insert)
This do what i want, but in form:
<xf:insert context="xxf:instance('HTTP-ERROR-RESULT')" ref="responses" origin="xxf:instance('HTTP-ERROR-TEMPLATE')"/>
I tried to transform it to process, but with no luck:
xf:insert(into="xxf:instance('HTTP-ERROR-RESULT')/reponses", origin="xxf:instance('HTTP-ERROR-TEMPLATE')")
If this is the right way? How to fix my semantic error in xf:insert()
and how do I get response-body (event ('response-body')
doesn't work...)? And is some way to get name of submission or action in process (I need some id for find in instance).
2] Another way I tried is through xforms-submit-error
in submit but it didn't work too.
Under the structure of what the builder creates:
<xf:action event="xforms-submit-done" ev:observer="echo-submission">
<xf:action class="fr-set-control-value-action">
<xf:var name="control-name" value="'control-2'"/>
<xf:var name="control-value" value="/*"/>
</xf:action>
</xf:action>
I tried to insert the something similar but with xforms-submit-error
:
<xf:action event="xforms-submit-error" ev:observer="echo-submission">
<xf:action class="fr-set-control-value-action">
<xf:var name="control-name" value="'control-2'"/>
<xf:var name="control-value" value="/*"/>
</xf:action>
</xf:action>
I found that when using class="fr-service"
in submission, I cant catch xforms-submit-error
. So is the right solution to write the whole submission yourself outside the builder and work with xforms-submit-error
?
Or is there an another elegant solution in Orbeon? Thanks for any reply!
Writing your own XForms will give you the most flexibility. You can put that XForms either directly in the form definition, using Edit Source in Form Builder, or in a custom model, which is a file on disk on the server, which I would recommend, see doc.
You seem to have found all the pieces (you did some good research there!), but here is a summary. If you have a service my-service
, you can listen to xforms-submit-error
, and store the value of event attributes in your own instance. If using a custom model, that file would have the following content, and you can also put the content of the <xf:model>
below directly inside the <xf:model>
of the form definition through Edit Source in Form Builder. Here the logic just saves the status code in an instance my-error-instance
, and then uses an <xf:message>
to show if for debugging.
<xf:model xmlns:xf="http://www.w3.org/2002/xforms">
<xf:instance id="my-error-instance">
<_>
<status-code/>
</_>
</xf:instance>
<xf:action observer="my-service-submission" event="xforms-submit-error">
<xf:setvalue
ref="instance('my-error-instance')/status-code"
value="event('response-status-code')"/>
<xf:message value="instance('my-error-instance')/status-code"/>
</xf:action>
</xf:model>
You'll also want to disable the default dialog on errors by setting the following property.
<property as="xs:string" name="oxf.fr.detail.process.action-service-error.*.*"/>
Finally, I put below the full source of a form definition that you can use to test this, with a my-service
HTTP service, making a request to http://httpbin.org/status/404, and called for form load, which I used to test the above custom model logic.
<xh:html xmlns:xh="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xxi="http://orbeon.org/oxf/xml/xinclude"
xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:exf="http://www.exforms.org/exf/1-0"
xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
xmlns:saxon="http://saxon.sf.net/"
xmlns:sql="http://orbeon.org/oxf/xml/sql"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:fb="http://orbeon.org/oxf/xml/form-builder">
<xh:head>
<xh:title>Calling service returning a 404 on form load</xh:title>
<xf:model id="fr-form-model" xxf:expose-xpath-types="true" xxf:analysis.calculate="true">
<!-- Main instance -->
<xf:instance id="fr-form-instance" xxf:exclude-result-prefixes="#all" xxf:index="id">
<form>
<section-1>
<grid-1>
<control-1/>
</grid-1>
</section-1>
</form>
</xf:instance>
<!-- Bindings -->
<xf:bind id="fr-form-binds" ref="instance('fr-form-instance')">
<xf:bind id="section-1-bind" name="section-1" ref="section-1">
<xf:bind id="grid-1-bind" ref="grid-1" name="grid-1">
<xf:bind id="control-1-bind" name="control-1" ref="control-1" xxf:whitespace="trim"/>
</xf:bind>
</xf:bind>
</xf:bind>
<!-- Metadata -->
<xf:instance id="fr-form-metadata" xxf:readonly="true" xxf:exclude-result-prefixes="#all">
<metadata>
<application-name>a</application-name>
<form-name>a</form-name>
<title xml:lang="en">Calling service returning a 404 on form load</title>
<description xml:lang="en"/>
<created-with-version>2021.1-SNAPSHOT PE</created-with-version>
<library-versions>
<app>4</app>
</library-versions>
</metadata>
</xf:instance>
<!-- Attachments -->
<xf:instance id="fr-form-attachments" xxf:exclude-result-prefixes="#all">
<attachments/>
</xf:instance>
<!-- All form resources -->
<xf:instance xxf:readonly="true" id="fr-form-resources" xxf:exclude-result-prefixes="#all">
<resources>
<resource xml:lang="en">
<section-1>
<label>Untitled Section</label>
</section-1>
<control-1>
<label/>
<hint/>
<alert/>
</control-1>
</resource>
</resources>
</xf:instance>
<xf:instance id="my-service-instance" class="fr-service" xxf:exclude-result-prefixes="#all">
<body xmlns:xxbl="http://orbeon.org/oxf/xml/xbl"
xmlns:fbf="java:org.orbeon.oxf.fb.FormBuilderXPathApi"><params/></body>
</xf:instance>
<xf:submission id="my-service-submission" class="fr-service"
resource="http://httpbin.org/status/404"
method="get"
serialization="none"
mediatype=""/>
<xf:action id="my-action-binding">
<xf:action event="fr-run-form-load-action-after-controls" ev:observer="fr-form-model"
if="true()">
<xf:send submission="my-service-submission"/>
</xf:action>
<xf:action event="xforms-submit" ev:observer="my-service-submission">
<xf:var name="request-instance-name" value="'my-service-instance'"/>
<xf:action/>
</xf:action>
<xf:action event="xforms-submit-done" ev:observer="my-service-submission"/>
</xf:action>
</xf:model>
</xh:head>
<xh:body>
<fr:view>
<fr:body xmlns:p="http://www.orbeon.com/oxf/pipeline" xmlns:xbl="http://www.w3.org/ns/xbl"
xmlns:oxf="http://www.orbeon.com/oxf/processors">
<fr:section id="section-1-section" bind="section-1-bind">
<xf:label ref="$form-resources/section-1/label"/>
<fr:grid id="grid-1-grid" bind="grid-1-bind">
<fr:c y="1" x="1" w="6">
<xf:input id="control-1-control" bind="control-1-bind">
<xf:label ref="$form-resources/control-1/label"/>
<xf:hint ref="$form-resources/control-1/hint"/>
<xf:alert ref="$fr-resources/detail/labels/alert"/>
</xf:input>
</fr:c>
<fr:c y="1" x="7" w="6"/>
</fr:grid>
</fr:section>
</fr:body>
</fr:view>
</xh:body>
</xh:html>