orbeon

Processing of HTTP status codes


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!


Solution

  • 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">&lt;params/&gt;</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>