Add HttpDataApiUrlData
parent
5b410005cd
commit
7d0dc97162
|
@ -139,14 +139,33 @@ module.exports = Class( 'HttpDataApi' )
|
|||
|
||||
this._validateDataType( data );
|
||||
|
||||
this._impl.requestData(
|
||||
this._url,
|
||||
this._method,
|
||||
this.requestData( this._url, this._method, data, callback );
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Request data from underlying HttpImpl
|
||||
*
|
||||
* Subtypes may override this method to alter any aspect of the request
|
||||
* before sending.
|
||||
*
|
||||
* @param {string} url destination URL
|
||||
* @param {string} method RFC-2616-compliant HTTP method
|
||||
* @param {Object|string} data request params
|
||||
* @param {function(?Error, ?string)} callback server response callback
|
||||
*
|
||||
* @return {HttpDataApi} self
|
||||
*/
|
||||
'virtual protected requestData'( url, method, data, callback )
|
||||
{
|
||||
return this._impl.requestData(
|
||||
url,
|
||||
method,
|
||||
this.encodeData( data ),
|
||||
callback
|
||||
);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* Provide data as part of URL
|
||||
*
|
||||
* Copyright (C) 2018 R-T Specialty, LLC.
|
||||
*
|
||||
* This file is part of the Liza Data Collection Framework
|
||||
*
|
||||
* Liza is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { Trait } = require( 'easejs' );
|
||||
const HttpDataApi = require( './HttpDataApi' );
|
||||
|
||||
|
||||
/**
|
||||
* Place fields from given data in the URL
|
||||
*
|
||||
* All remaining fields are passed to the underlying supertype.
|
||||
*/
|
||||
module.exports = Trait( 'HttpDataApiUrlData' )
|
||||
.extend( HttpDataApi,
|
||||
{
|
||||
/**
|
||||
* Fields to take from data and place in URL
|
||||
* @type {string}
|
||||
*/
|
||||
'private _fields': [],
|
||||
|
||||
|
||||
/**
|
||||
* Initialize with URL field list
|
||||
*
|
||||
* @param {Array<string>} fields list of fields to include in URL
|
||||
*/
|
||||
__mixin( fields )
|
||||
{
|
||||
this._fields = fields;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Concatenate chosen fields with URL
|
||||
*
|
||||
* The previously specified fields will have their values delimited by '/'
|
||||
* and will be concatenated with the URL. All used fields in DATA will be
|
||||
* removed before being passed to the supertype. METHOD and CALLBACK are
|
||||
* proxied as-is.
|
||||
*
|
||||
* @param {string} url destination URL
|
||||
* @param {string} method RFC-2616-compliant HTTP method
|
||||
* @param {Object|string} data request params
|
||||
* @param {function(Error, Object)} callback server response callback
|
||||
*
|
||||
* @return {HttpImpl} self
|
||||
*/
|
||||
'override public requestData'( url, method, data, callback )
|
||||
{
|
||||
const [ values, filtered_data ] = this._getFieldValues( data );
|
||||
|
||||
const params = values.map( ( [ , value ] ) => value );
|
||||
const missing = values.filter( ( [ , value ] ) => value === undefined );
|
||||
|
||||
if ( missing.length > 0 )
|
||||
{
|
||||
callback(
|
||||
Error(
|
||||
"Missing URL parameters: " +
|
||||
missing.map( ( [ field ] ) => field ).join( ", " )
|
||||
),
|
||||
null
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
const built_url = ( params.length > 0 )
|
||||
? url + '/' + params.join( '/' )
|
||||
: url;
|
||||
|
||||
return this.__super( built_url, method, filtered_data, callback );
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Associate fields with their respective values from DATA
|
||||
*
|
||||
* The returned values are of the form `[ [ field, value ], ... ]`.
|
||||
* The returned data object is a copy of the original and is stripped
|
||||
* of the respective fields.
|
||||
*
|
||||
* @param {Object} data source data
|
||||
*
|
||||
* @return {Array} values and copy of data stripped of those fields
|
||||
*/
|
||||
'private _getFieldValues'( data )
|
||||
{
|
||||
const fieldset = new Set( this._fields );
|
||||
const values = this._fields.map( field => [ field, data[ field ] ] );
|
||||
|
||||
// copy of data with fields stripped
|
||||
const new_data = Object.keys( data ).reduce( ( dest, key ) =>
|
||||
{
|
||||
if ( fieldset.has( key ) )
|
||||
{
|
||||
return dest;
|
||||
}
|
||||
|
||||
dest[ key ] = data[ key ];
|
||||
return dest;
|
||||
}, {} );
|
||||
|
||||
return [ values, new_data ];
|
||||
},
|
||||
} );
|
|
@ -87,7 +87,7 @@ module.exports = Class( 'NodeHttpImpl' )
|
|||
*
|
||||
* @return {HttpImpl} self
|
||||
*/
|
||||
'public requestData'( url, method, data, callback )
|
||||
'virtual public requestData'( url, method, data, callback )
|
||||
{
|
||||
const options = this._parseUrl( url );
|
||||
const protocol = options.protocol.replace( /:$/, '' );
|
||||
|
|
|
@ -71,7 +71,7 @@ module.exports = Class( 'XhrHttpImpl' )
|
|||
*
|
||||
* @return {HttpImpl} self
|
||||
*/
|
||||
'public requestData': function( url, method, data, callback )
|
||||
'virtual public requestData': function( url, method, data, callback )
|
||||
{
|
||||
if ( typeof data !== 'string' )
|
||||
{
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* Tests HttpDataApiUrlData
|
||||
*
|
||||
* Copyright (C) 2018 R-T Specialty, LLC.
|
||||
*
|
||||
* This file is part of the Liza Data Collection Framework.
|
||||
*
|
||||
* liza is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { Class } = require( 'easejs' );
|
||||
const { expect } = require( 'chai' );
|
||||
|
||||
const {
|
||||
dapi: {
|
||||
http: {
|
||||
HttpDataApi,
|
||||
HttpImpl,
|
||||
HttpDataApiUrlData: Sut,
|
||||
},
|
||||
},
|
||||
} = require( '../../../' );
|
||||
|
||||
|
||||
describe( 'HttpDataApiUrlData', () =>
|
||||
{
|
||||
[
|
||||
{
|
||||
fields: [],
|
||||
data: {
|
||||
foo: "foodata",
|
||||
bar: "bardata",
|
||||
},
|
||||
base_url: "base",
|
||||
after_data: "foo=foodata&bar=bardata",
|
||||
expected: "base",
|
||||
},
|
||||
{
|
||||
fields: [ 'foo', 'bar' ],
|
||||
data: {
|
||||
foo: "foodata",
|
||||
bar: "bardata",
|
||||
baz: "shouldnotuse",
|
||||
},
|
||||
base_url: "base2",
|
||||
after_data: "baz=shouldnotuse",
|
||||
expected: "base2/foodata/bardata",
|
||||
},
|
||||
].forEach( ( { fields, data, base_url, after_data, expected }, i ) =>
|
||||
it( `can include portion of data in url (#${i})`, done =>
|
||||
{
|
||||
const expected_method = 'PUT';
|
||||
|
||||
const impl = Class.implement( HttpImpl ).extend(
|
||||
{
|
||||
'virtual requestData'( url, method, given_data, callback )
|
||||
{
|
||||
expect( url ).to.equal( expected );
|
||||
expect( method ).to.equal( expected_method );
|
||||
expect( given_data ).to.deep.equal( after_data );
|
||||
|
||||
// should not have mutated the original object
|
||||
// (they shouldn't be the same object)
|
||||
expect( data ).to.not.equal( given_data );
|
||||
|
||||
// should be done
|
||||
callback();
|
||||
},
|
||||
|
||||
setOptions: () => {},
|
||||
} )();
|
||||
|
||||
const sut = HttpDataApi.use( Sut( fields ) )(
|
||||
base_url, expected_method, impl
|
||||
);
|
||||
|
||||
const result = sut.request( data, done );
|
||||
} )
|
||||
);
|
||||
|
||||
|
||||
it( "throws error if param is missing from data", done =>
|
||||
{
|
||||
const impl = Class.implement( HttpImpl ).extend(
|
||||
{
|
||||
'virtual requestData': ( url, method, data, callback ) => {},
|
||||
setOptions: () => {},
|
||||
} )();
|
||||
|
||||
const sut = HttpDataApi.use( Sut( [ 'foo' ] ) )(
|
||||
'', 'PUT', impl
|
||||
);
|
||||
|
||||
// missing `foo` in data
|
||||
sut.request( { unused: "missing foo" }, ( err, data ) =>
|
||||
{
|
||||
expect( err ).to.be.instanceof( Error );
|
||||
expect( err.message ).to.match( /\bfoo\b/ );
|
||||
|
||||
expect( data ).to.equal( null );
|
||||
|
||||
done();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue