Skip to content

Commit

Permalink
Implement #2781 "Form Metadata API: option to return all non-deleted …
Browse files Browse the repository at this point in the history
…form versions"

- enabled with param `all-versions=true`
- also modify persistence proxy to forward URL parameters in this case
    - it did this for `proxyRequest()` but not `proxyPublishedFormsMetadata()`
  • Loading branch information
ebruchez committed May 16, 2016
1 parent 45331c4 commit d662588
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 48 deletions.
39 changes: 22 additions & 17 deletions src/main/scala/org/orbeon/oxf/fr/FormRunnerPersistenceProxy.scala
Expand Up @@ -81,17 +81,20 @@ class FormRunnerPersistenceProxy extends ProcessorImpl {
path : String
): Unit = {

def buildQueryString =
NetUtils.encodeQueryString(request.getParameterMap)

// Get persistence implementation target URL and configuration headers
val (persistenceBaseURL, headers) = FormRunner.getPersistenceURLHeaders(app, form, formOrData)
assert(persistenceBaseURL ne null, "no base URL specified for persistence provider, check properties")
val serviceURI = NetUtils.appendQueryString(dropTrailingSlash(persistenceBaseURL) + path, buildQueryString)

val serviceURI = NetUtils.appendQueryString(
dropTrailingSlash(persistenceBaseURL) + path,
NetUtils.encodeQueryString(request.getParameterMap)
)

proxyRequest(request, serviceURI, headers, response)
}

private def proxyRequest(request: Request, serviceURI: String, headers: Map[String, String], response: Response): Unit = {

val cxr = proxyEstablishConnection(request, serviceURI, headers)

useAndClose(cxr) { cxr
Expand Down Expand Up @@ -176,7 +179,6 @@ class FormRunnerPersistenceProxy extends ProcessorImpl {
form : Option[String],
path : String
): Unit = {
val propertySet = Properties.instance.getPropertySet

val providers = {
(app, form) match {
Expand All @@ -190,21 +192,24 @@ class FormRunnerPersistenceProxy extends ProcessorImpl {
}
}

val parameters = NetUtils.encodeQueryString(request.getParameterMap)

val allFormElements =
providers map
FormRunner.getPersistenceURLHeadersFromProvider flatMap {
case (baseURI, headers)
// Read all the forms for the current service
val serviceURI = baseURI + "/form" + Option(path).getOrElse("")
val cxr = proxyEstablishConnection(request, serviceURI, headers)

ConnectionResult.withSuccessConnection(cxr, closeOnSuccess = true) { is
val forms = TransformerUtils.readTinyTree(XPath.GlobalConfiguration, is, serviceURI, false, false)
forms \\ "forms" \\ "form"
}
for {
provider providers
(baseURI, headers) = FormRunner.getPersistenceURLHeadersFromProvider(provider)
} yield {
// Read all the forms for the current service
val serviceURI = NetUtils.appendQueryString(baseURI + "/form" + Option(path).getOrElse(""), parameters)
val cxr = proxyEstablishConnection(request, serviceURI, headers)

ConnectionResult.withSuccessConnection(cxr, closeOnSuccess = true) { is
val forms = TransformerUtils.readTinyTree(XPath.GlobalConfiguration, is, serviceURI, false, false)
forms \\ "forms" \\ "form"
}
}

val filteredFormElements = FormRunner.filterFormsAndAnnotateWithOperations(allFormElements)
val filteredFormElements = FormRunner.filterFormsAndAnnotateWithOperations(allFormElements.flatten)

// Aggregate and serialize
val documentElement = elementInfo("forms")
Expand Down
Expand Up @@ -40,6 +40,16 @@ object RequestReader {
def unapply(atts: Atts) = atts.atts collectFirst { case (IdQName, value) value }
}

// Remove `<description>` and `<migration>`, which we don't need, and can easily get us above the 4000 characters limit
// https://github.com/orbeon/orbeon-forms/issues/2385
// 2016-05-13: Also remove `<application-name>` and `<form-name>` since they are in their own columns.
private val MetadataElementsToFilter = Set(
"application-name",
"form-name",
"description",
"migration"
)

// NOTE: Tested that the pattern match works optimally: with form-with-metadata.xhtml, JQName.unapply is
// called 17 times and IdAtt.unapply 2 times until the match is found, which is what is expected.
def isMetadataElement(stack: List[StartElement]): Boolean =
Expand Down Expand Up @@ -88,11 +98,9 @@ object RequestReader {

val metadataWriter = new StringBuilderWriter()

// Remove <description> and <migration>, which we don't need, and can easily get us above the 4000 characters limit
// https://github.com/orbeon/orbeon-forms/issues/2385
val descriptionMigrationFilter = new ElementFilterXMLReceiver(newIdentityReceiver(metadataWriter)) {
override protected def isFilterElement(uri: String, localname: String, qName: String, attributes: Attributes): Boolean =
uri == "" && (localname == "description" || localname == "migration")
protected def isFilterElement(uri: String, localname: String, qName: String, attributes: Attributes) =
uri == "" && MetadataElementsToFilter(localname)
}

// MAYBE: strip enclosing namespaces; truncate long titles
Expand Down
67 changes: 40 additions & 27 deletions src/resources/apps/fr/persistence/relational/form.xpl
Expand Up @@ -11,52 +11,49 @@
The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
-->
<p:config xmlns:p="http://www.orbeon.com/oxf/pipeline"
xmlns:sql="http://orbeon.org/oxf/xml/sql"
xmlns:odt="http://orbeon.org/oxf/xml/datatypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:oxf="http://www.orbeon.com/oxf/processors"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:f="http//www.orbeon.com/function"
xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
xmlns:xh="http://www.w3.org/1999/xhtml"
xmlns:xpl="java:org.orbeon.oxf.pipeline.api.FunctionLibrary">
<p:config
xmlns:p="http://www.orbeon.com/oxf/pipeline"
xmlns:sql="http://orbeon.org/oxf/xml/sql"
xmlns:odt="http://orbeon.org/oxf/xml/datatypes"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:oxf="http://www.orbeon.com/oxf/processors">

<p:param name="instance" type="input"/>
<p:param name="data" type="output"/>
<p:param name="data" type="output"/>

<p:processor name="oxf:request">
<p:input name="config">
<config>
<include>/request/request-path</include>
<include>/request/headers/header[name = 'orbeon-datasource']</include>
<include>/request/parameters/parameter[name = 'all-versions']</include>
</config>
</p:input>
<p:output name="data" id="request"/>
</p:processor>

<p:processor name="oxf:regexp">
<p:input name="config"><config>/fr/service/(oracle|mysql|postgresql|db2|sqlserver)/form(/([^/]+)(/([^/]+))?)?</config></p:input>
<p:input name="data" href="#request#xpointer(/request/request-path)"/>
<p:output name="data" id="matcher-groups"/>
</p:processor>

<p:processor name="oxf:xslt">
<p:processor name="oxf:unsafe-xslt">
<p:input name="matcher-groups" href="#matcher-groups"/>
<p:input name="instance" href="#instance"/>
<p:input name="request" href="#request"/>
<p:input name="data"><dummy/></p:input>
<p:input name="config">
<request xsl:version="2.0">
<xsl:variable name="request" as="element(request)" select="doc('input:request')/request"/>
<xsl:variable name="matcher-groups" as="element(group)+" select="doc('input:matcher-groups')/result/group"/>
<sql:datasource><xsl:value-of select="$request/headers/header[name = 'orbeon-datasource']/value/string() treat as xs:string"/></sql:datasource>
<xsl:variable name="request" as="element(request)" select="doc('input:request')/request"/>
<xsl:variable name="matcher-groups" as="element(group)+" select="doc('input:matcher-groups')/result/group"/>

<sql:datasource><xsl:value-of select="$request/headers/header[name = 'orbeon-datasource']/value/string()"/></sql:datasource>
<xsl:copy-of select="doc('input:instance')/request/*"/>
<xsl:variable name="matcher-groups" as="element(group)+" select="doc('input:matcher-groups')/result/group"/>

<provider><xsl:value-of select="$matcher-groups[1]"/></provider>
<all-versions><xsl:value-of select="$request/parameters/parameter[name = 'all-versions']/value = 'true'"/></all-versions>
</request>
</p:input>
<p:output name="data" id="request-description"/>
Expand Down Expand Up @@ -125,19 +122,35 @@
</p:processor>

<p:processor name="oxf:xslt">
<p:input name="data" href="#sql-out"/>
<p:input name="data" href="#sql-out"/>
<p:input name="request-description" href="#request-description"/>
<p:input name="config">
<xsl:stylesheet version="2.0" exclude-result-prefixes="#all">

<xsl:template match="/">
<forms>
<xsl:for-each select="/sql-out/row">
<form>
<xsl:copy-of select="metadata/*" copy-namespaces="no"/>
<xsl:copy-of select="* except metadata" copy-namespaces="no"/>
</form>
</xsl:for-each>
<xsl:choose>
<xsl:when test="doc('input:request-description')/*/all-versions = 'false'">
<!-- NOTE: We should probably filter in SQL, but it makes the query very complex. So we do it in XSLT for now. -->
<xsl:for-each-group select="/*/row" group-by="concat(application-name, '|', form-name)">
<xsl:variable name="max-form-version" select="max(current-group()/form-version/xs:integer(.))"/>
<xsl:apply-templates select="current-group()[form-version = $max-form-version]"/>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/*/row"/>
</xsl:otherwise>
</xsl:choose>
</forms>
</xsl:template>

<xsl:template match="row">
<form>
<xsl:copy-of select="* except metadata" copy-namespaces="no"/>
<xsl:copy-of select="metadata/(* except (application-name, form-name))" copy-namespaces="no"/>
</form>
</xsl:template>

</xsl:stylesheet>
</p:input>
<p:output name="data" ref="data"/>
Expand Down

0 comments on commit d662588

Please sign in to comment.