enketo-transformer
Advanced tools
Comparing version 1.0.9 to 1.1.0
63
app.js
@@ -1,9 +0,11 @@ | ||
var express = require( 'express' ), | ||
app = express(), | ||
Q = require( 'q' ), | ||
request = require( 'request' ), | ||
transformer = require( './src/transformer' ), | ||
debug = require( 'debug' )( 'app.js' ), | ||
config = require( './config/config.json' ); | ||
'use strict'; | ||
var express = require( 'express' ); | ||
var app = express(); | ||
var Promise = require( 'q' ).Promise; | ||
var request = require( 'request' ); | ||
var transformer = require( './src/transformer' ); | ||
var debug = require( 'debug' )( 'app.js' ); | ||
var config = require( './config/config.json' ); | ||
for ( var item in config ) { | ||
@@ -49,32 +51,31 @@ app.set( item, config[ item ] ); | ||
function _request( options ) { | ||
var error, | ||
deferred = Q.defer(); | ||
var method; | ||
var error; | ||
// set headers | ||
options.headers = options.headers || {}; | ||
options.headers[ 'X-OpenRosa-Version' ] = '1.0'; | ||
var method = options.method || 'get'; | ||
method = options.method || 'get'; | ||
request[ method ]( options, function( error, response, body ) { | ||
if ( error ) { | ||
debug( 'Error occurred when requesting ' + options.url, error ); | ||
deferred.reject( error ); | ||
} else if ( response.statusCode === 401 ) { | ||
error = new Error( 'Forbidden. Authorization Required.' ); | ||
error.status = response.statusCode; | ||
deferred.reject( error ); | ||
} else if ( response.statusCode < 200 || response.statusCode >= 300 ) { | ||
error = new Error( 'Request to ' + options.url + ' failed.' ); | ||
error.status = response.statusCode; | ||
deferred.reject( error ); | ||
} else { | ||
debug( 'response of request to ' + options.url + ' has status code: ', response.statusCode ); | ||
deferred.resolve( { | ||
xform: body | ||
} ); | ||
} | ||
return new Promise( function( resolve, reject ) { | ||
request[ method ]( options, function( error, response, body ) { | ||
if ( error ) { | ||
debug( 'Error occurred when requesting ' + options.url, error ); | ||
reject( error ); | ||
} else if ( response.statusCode === 401 ) { | ||
error = new Error( 'Forbidden. Authorization Required.' ); | ||
error.status = response.statusCode; | ||
reject( error ); | ||
} else if ( response.statusCode < 200 || response.statusCode >= 300 ) { | ||
error = new Error( 'Request to ' + options.url + ' failed.' ); | ||
error.status = response.statusCode; | ||
reject( error ); | ||
} else { | ||
debug( 'response of request to ' + options.url + ' has status code: ', response.statusCode ); | ||
resolve( { | ||
xform: body | ||
} ); | ||
} | ||
} ); | ||
} ); | ||
return deferred.promise; | ||
} |
{ | ||
"name": "enketo-transformer", | ||
"version": "1.0.9", | ||
"version": "1.1.0", | ||
"description": "Library that transforms ODK-compliant XForms in a format that enketo-core consumes", | ||
@@ -27,11 +27,10 @@ "main": "src/transformer.js", | ||
"author": "Martijn van de Rijdt", | ||
"license": "Apache2", | ||
"license": "Apache-2.0", | ||
"dependencies": { | ||
"debug": "2.1.x", | ||
"enketo-xslt": "1.0.8", | ||
"debug": "2.2.x", | ||
"enketo-xslt": "1.1.0", | ||
"express": "4.12.x", | ||
"libxmljs": "0.14.x", | ||
"node_xslt": "0.1.x", | ||
"libxslt": "0.4.x", | ||
"q": "2.0.x", | ||
"request": "2.55.x" | ||
"request": "2.58.x" | ||
}, | ||
@@ -38,0 +37,0 @@ "devDependencies": { |
@@ -8,4 +8,3 @@ Enketo Transformer [![Build Status](https://travis-ci.org/enketo/enketo-transformer.svg?branch=master)](https://travis-ci.org/enketo/enketo-transformer) [![Dependency Status](https://david-dm.org/enketo/enketo-transformer.svg)](https://david-dm.org/enketo/enketo-transformer) | ||
1. Node v0.10.x is required. There is an [outstanding issue with Node v0.12.x](https://github.com/bsuh/node_xslt/issues/24). | ||
2. Install libxslt and libxml2 with `(sudo) apt-get install libxml2-dev libxslt1-dev` | ||
1. Node v0.10.x is required for now. There is an [outstanding issue with Node v0.12.x](https://github.com/albanm/node-libxslt/issues/15). | ||
@@ -53,1 +52,5 @@ ## Install as module | ||
* run tests with `npm test` | ||
## Develop | ||
A vagrant configuration file and provisioning script is included. |
@@ -1,17 +0,20 @@ | ||
/* global setTimeout */ | ||
"use strict"; | ||
var version, | ||
Q = require( 'q' ), | ||
fs = require( 'fs' ), | ||
crypto = require( 'crypto' ), | ||
transformer = require( 'node_xslt' ), | ||
libxmljs = require( "libxmljs" ), | ||
sheets = require( 'enketo-xslt' ), | ||
debug = require( 'debug' )( 'transformer' ); | ||
var Promise = require( 'q' ).Promise; | ||
var fs = require( 'fs' ); | ||
var crypto = require( 'crypto' ); | ||
var libxslt = require( 'libxslt' ); | ||
var libxmljs = libxslt.libxmljs; | ||
var sheets = require( 'enketo-xslt' ); | ||
var debug = require( 'debug' )( 'transformer' ); | ||
var xslFormDoc = libxmljs.parseXml( sheets.xslForm, { | ||
nocdata: true | ||
} ); | ||
var xslModelDoc = libxmljs.parseXml( sheets.xslModel, { | ||
nocdata: true | ||
} ); | ||
var version = _getVersion(); | ||
_setVersion(); | ||
/** | ||
* Performs XSLT transformation on XForm asynchronously. | ||
* Performs XSLT transformation on XForm and process the result. | ||
* | ||
@@ -22,54 +25,89 @@ * @param {{xform: string, theme: string}} survey Survey object with at least an xform property | ||
function transform( survey ) { | ||
var error, errorMsg, doc, formStylesheet, instanceStylesheet, xsltEndTime, | ||
deferred = Q.defer(), | ||
startTime = new Date().getTime(); | ||
var xsltEndTime; | ||
var xformDoc; | ||
var startTime = new Date().getTime(); | ||
// make this asynchronous, sort of | ||
setTimeout( function() { | ||
try { | ||
doc = transformer.readXmlString( survey.xform ); | ||
return _parseXml( survey.xform ) | ||
.then( function( doc ) { | ||
xformDoc = doc; | ||
return _transform( xslFormDoc, xformDoc ); | ||
} ) | ||
.then( function( htmlDoc ) { | ||
htmlDoc = _replaceTheme( htmlDoc, survey.theme ); | ||
htmlDoc = _replaceMediaSources( htmlDoc, survey.manifest ); | ||
// TODO: does this result in self-closing tags? | ||
survey.form = htmlDoc.root().get( '*' ).toString( false ); | ||
formStylesheet = transformer.readXsltString( sheets.xslForm ); | ||
survey.form = _stripRoot( transformer.transform( formStylesheet, doc, [ 'wtf', 'why' ] ) ); | ||
return _transform( xslModelDoc, xformDoc ); | ||
} ) | ||
.then( function( xmlDoc ) { | ||
xmlDoc = _replaceMediaSources( xmlDoc, survey.manifest ); | ||
instanceStylesheet = transformer.readXsltString( sheets.xslModel ); | ||
survey.model = _stripRoot( transformer.transform( instanceStylesheet, doc, [ 'wtf', 'why' ] ) ); | ||
survey.model = xmlDoc.root().get( '*' ).toString( false ); | ||
xsltEndTime = new Date().getTime(); | ||
debug( 'form and instance XSLT transformation took ' + ( xsltEndTime - startTime ) / 1000 + ' seconds' ); | ||
delete survey.xform; | ||
return survey; | ||
} ); | ||
} | ||
survey.form = _replaceTheme( survey.form, survey.theme ); | ||
/** | ||
* Performs a generic XSLT transformation | ||
* | ||
* @param {[type]} xslDoc libxmljs object of XSL stylesheet | ||
* @param {[type]} xmlDoc libxmljs object of XML document | ||
* @return {Promise} libxmljs result document object | ||
*/ | ||
function _transform( xslDoc, xmlDoc ) { | ||
return new Promise( function( resolve, reject ) { | ||
libxslt.parse( xslDoc, function( error, stylesheet ) { | ||
if ( error ) { | ||
reject( error ); | ||
} else { | ||
stylesheet.apply( xmlDoc, function( error, result ) { | ||
if ( error ) { | ||
reject( error ); | ||
} else { | ||
resolve( result ); | ||
} | ||
} ); | ||
} | ||
} ); | ||
} ); | ||
} | ||
survey.form = _replaceMediaSources( survey.form, survey.manifest ); | ||
survey.model = _replaceMediaSources( survey.model, survey.manifest ); | ||
debug( 'post-processing transformation result took ' + ( new Date().getTime() - xsltEndTime ) / 1000 + ' seconds' ); | ||
/** | ||
* Parses and XML string into a libxmljs object | ||
* | ||
* @param {string} xmlStr XML string | ||
* @return {Promise} libxmljs result document object | ||
*/ | ||
function _parseXml( xmlStr ) { | ||
var doc; | ||
delete survey.xform; | ||
deferred.resolve( survey ); | ||
return new Promise( function( resolve, reject ) { | ||
try { | ||
doc = libxmljs.parseXml( xmlStr ); | ||
resolve( doc ); | ||
} catch ( e ) { | ||
error = ( e ) ? new Error( e ) : new Error( 'unknown transformation error' ); | ||
debug( 'error during xslt transformation', error ); | ||
deferred.reject( error ); | ||
reject( e ); | ||
} | ||
}, 0 ); | ||
return deferred.promise; | ||
} ); | ||
} | ||
function _stripRoot( xml ) { | ||
var xmlDoc = libxmljs.parseXml( xml ); | ||
return xmlDoc.root().get( '*' ).toString( false ); | ||
} | ||
function _replaceTheme( xml, theme ) { | ||
var doc, formClassAttr, formClassValue, | ||
/** | ||
* Replaces the form-defined theme | ||
* | ||
* @param {[type]} doc libxmljs object | ||
* @param {string} theme theme | ||
* @return {[type]} libxmljs object | ||
*/ | ||
function _replaceTheme( doc, theme ) { | ||
var formClassAttr, formClassValue, | ||
HAS_THEME = /(theme-)[^"'\s]+/; | ||
if ( !theme ) { | ||
return xml; | ||
return doc; | ||
} | ||
doc = libxmljs.parseXml( xml ); | ||
formClassAttr = doc.root().get( '/form' ).attr( 'class' ); | ||
formClassAttr = doc.root().get( '/root/form' ).attr( 'class' ); | ||
formClassValue = formClassAttr.value(); | ||
@@ -83,17 +121,20 @@ | ||
// TODO: probably result in selfclosing tags for empty elements where not allowed in HTML. Check this. | ||
return doc.toString(); | ||
return doc; | ||
} | ||
function _replaceMediaSources( xmlStr, manifest ) { | ||
var doc; | ||
/** | ||
* Replaces xformManifest urls with URLs according to an internal Enketo Express url format | ||
* | ||
* @param {[type]} xmlDoc libxmljs object | ||
* @param {*} manifest json representation of XForm manifest | ||
* @return {Promise} libxmljs object | ||
*/ | ||
function _replaceMediaSources( xmlDoc, manifest ) { | ||
if ( !manifest ) { | ||
return xmlStr; | ||
return xmlDoc; | ||
} | ||
doc = libxmljs.parseXml( xmlStr ); | ||
// iterate through each element with a src attribute | ||
doc.find( '//*[@src]' ).forEach( function( mediaEl ) { | ||
xmlDoc.find( '//*[@src]' ).forEach( function( mediaEl ) { | ||
manifest.some( function( file ) { | ||
@@ -110,3 +151,3 @@ if ( new RegExp( 'jr://(images|video|audio|file|file-csv)/' + file.filename ).test( mediaEl.attr( 'src' ).value() ) ) { | ||
manifest.some( function( file ) { | ||
var formLogoEl = doc.get( '//*[@class="form-logo"]' ); | ||
var formLogoEl = xmlDoc.get( '//*[@class="form-logo"]' ); | ||
if ( file.filename === 'form_logo.png' && formLogoEl ) { | ||
@@ -121,4 +162,3 @@ formLogoEl | ||
// TODO: probably result in selfclosing tags for empty elements where not allowed in HTML. Check this. | ||
return doc.toString(); | ||
return xmlDoc; | ||
} | ||
@@ -143,7 +183,4 @@ | ||
*/ | ||
function _setVersion() { | ||
// only perform this expensive check once after (re)starting application | ||
if ( !version ) { | ||
version = _md5( sheets.xslForm + sheets.xslModel ); | ||
} | ||
function _getVersion() { | ||
return _md5( sheets.xslForm + sheets.xslModel ); | ||
} | ||
@@ -150,0 +187,0 @@ |
@@ -43,3 +43,3 @@ /* global describe, require, it*/ | ||
} ); | ||
return expect( result ).to.eventually.be.rejectedWith( Error, /parse/ ); | ||
return expect( result ).to.eventually.be.rejectedWith( Error ); | ||
} ); | ||
@@ -46,0 +46,0 @@ } ); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
226106
6
71
974
55
1
7
2
+ Addedlibxslt@0.4.x
+ Addedasync@2.6.4(transitive)
+ Addedbindings@1.1.1(transitive)
+ Addedcaseless@0.10.0(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addedenketo-xslt@1.1.0(transitive)
+ Addedextend@2.0.2(transitive)
+ Addedform-data@1.0.1(transitive)
+ Addedhttp-signature@0.11.0(transitive)
+ Addedlibxmljs@0.11.1(transitive)
+ Addedlibxslt@0.4.1(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addednan@1.1.21.2.0(transitive)
+ Addedoauth-sign@0.8.2(transitive)
+ Addedqs@3.1.0(transitive)
+ Addedrequest@2.58.0(transitive)
- Removedlibxmljs@0.14.x
- Removednode_xslt@0.1.x
- Removedasync@0.9.2(transitive)
- Removedcaseless@0.9.0(transitive)
- Removedcombined-stream@0.0.7(transitive)
- Removeddebug@2.1.3(transitive)
- Removeddelayed-stream@0.0.5(transitive)
- Removedenketo-xslt@1.0.8(transitive)
- Removedform-data@0.2.0(transitive)
- Removedhttp-signature@0.10.1(transitive)
- Removedlibxmljs@0.14.3(transitive)
- Removedms@0.7.0(transitive)
- Removednan@2.0.7(transitive)
- Removednode_xslt@0.1.9(transitive)
- Removedoauth-sign@0.6.0(transitive)
- Removedrequest@2.55.0(transitive)
Updateddebug@2.2.x
Updatedenketo-xslt@1.1.0
Updatedrequest@2.58.x