Skip to content

Commit

Permalink
feat(HyperRequest): Add asXML support
Browse files Browse the repository at this point in the history
Add support for sending XML body types using the `asXML()` helper method.

BREAKING CHANGE: Remove default `Content-Type` header and `adobe@2016` support
  • Loading branch information
elpete committed May 23, 2023
1 parent 687c342 commit 16dab5e
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
fail-fast: false
matrix:
cfengine: ["lucee@5", "adobe@2018", "adobe@2021"]
coldbox: ["coldbox@6", "coldbox@7", "coldbox@be"]
coldbox: ["coldbox@6", "coldbox@7"]
experimental: [false]
include:
- cfengine: "adobe@2023.0.0-beta.1"
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@
<a href="https://forgebox.io/view/hyper"><img src="https://cfmlbadges.monkehworks.com/images/badges/available-on-forgebox.svg" alt="Available on ForgeBox" /></a>
<img src="https://cfmlbadges.monkehworks.com/images/badges/tested-with-testbox.svg" alt="Tested With TestBox" />
</p>
<p style="text-align: center;">
<img height="30" src="https://cfmlbadges.monkehworks.com/images/badges/compatibility-coldfusion-11.svg" alt="Compatible with ColdFusion 11" />
<img height="30" src="https://cfmlbadges.monkehworks.com/images/badges/compatibility-coldfusion-2016.svg" alt="Compatible with ColdFusion 2016" />
<img height="30" src="https://cfmlbadges.monkehworks.com/images/badges/compatibility-lucee-5.svg" alt="Compatible with Lucee 5" />
</p>
<p style="text-align: center;">
<a href="https://travis-ci.org/coldbox-modules/hyper"><img src="https://img.shields.io/travis/coldbox-modules/hyper/master.svg?style=flat-square&label=master" alt="Master Branch Build Status"></a>
</p>

## A CFML HTTP Builder

Expand All @@ -33,10 +25,19 @@ Hyper exists to provide a fluent builder experience for HTTP requests and respon

### Requirements

Hyper runs on Adobe ColdFusion 11+ and Lucee 5+.
Hyper runs on Adobe ColdFusion 2018+ and Lucee 5+.

ColdBox is not required, but mappings are provided for ColdBox users automatically.

### Upgrade from v5
Two breaking changes:
1. There is no default `Content-Type`. Make sure to either set it or use one of the helper methods, like `asJson()`.
2. `hyper@^6` dropped support for `adobe@2016`.

### Upgrade from v4
This is only a breaking change if you have a custom `HttpClient`.
The `HyperHttpClientInterface` now requires a `debug` method.

### Upgrade from v3

The only changes between v3 and v4 is with the `getQueryParams` and `setQueryParams` methods.
Expand Down
88 changes: 63 additions & 25 deletions models/CfhttpHttpClient.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,44 @@ component implements="HyperHttpClientInterface" {

var cfhttpBody = [];
if ( req.hasBody() ) {
if ( req.getBodyFormat() == "json" ) {
cfhttpBody.append( req.prepareBody() );
} else if ( req.getBodyFormat() == "formFields" ) {
var body = req.getBody();
for ( var fieldName in body ) {
for ( var value in arrayWrap( body[ fieldName ] ) ) {
cfhttpBody.append( {
"type" : "formfield",
"name" : fieldName,
"value" : value
switch ( req.getBodyFormat() ) {
case "json":
// this is here for backwards compatibility
if ( !headers.keyExists( "Content-Type" ) ) {
cfhttpHeaders.append( {
"name" : "Content-Type",
"value" : "application/json"
} );
}
}
} else {
cfhttpBody.append( req.prepareBody() );

cfhttpBody.append( {
"type" : "body",
"value" : req.prepareBody()
} );
break;
case "formFields":
var body = req.getBody();
for ( var fieldName in body ) {
for ( var value in arrayWrap( body[ fieldName ] ) ) {
cfhttpBody.append( {
"type" : "formfield",
"name" : fieldName,
"value" : value
} );
}
}
break;
case "xml":
cfhttpBody.append( {
"type" : "xml",
"value" : req.prepareBody()
} );
break;
default:
cfhttpBody.append( {
"type" : "body",
"value" : req.prepareBody()
} );
}
}

Expand Down Expand Up @@ -242,21 +265,36 @@ component implements="HyperHttpClientInterface" {
}

if ( req.hasBody() ) {
if ( req.getBodyFormat() == "json" ) {
cfhttpparam( type = "body", value = req.prepareBody() );
} else if ( req.getBodyFormat() == "formFields" ) {
var body = req.getBody();
for ( var fieldName in body ) {
for ( var value in arrayWrap( body[ fieldName ] ) ) {
switch ( req.getBodyFormat() ) {
case "json":
// this is here for backwards compatibility
if ( !headers.keyExists( "Content-Type" ) ) {
cfhttpparam(
type = "formfield",
name = fieldName,
value = value
type = "header",
name = "Content-Type",
value = "application/json"
);
}
}
} else {
cfhttpparam( type = "body", value = req.prepareBody() );

cfhttpparam( type = "body", value = req.prepareBody() );
break;
case "formFields":
var body = req.getBody();
for ( var fieldName in body ) {
for ( var value in arrayWrap( body[ fieldName ] ) ) {
cfhttpparam(
type = "formfield",
name = fieldName,
value = value
);
}
}
break;
case "xml":
cfhttpparam( type = "xml", value = req.prepareBody() );
break;
default:
cfhttpparam( type = "body", value = req.prepareBody() );
}
}
}
Expand Down
25 changes: 17 additions & 8 deletions models/HyperRequest.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,10 @@ component accessors="true" {
* @returns The HyperRequest instance.
*/
function init( httpClient = new CfhttpHttpClient() ) {
variables.requestID = createUUID();
variables.httpClient = arguments.httpClient;
variables.queryParams = [];
variables.headers = createObject( "java", "java.util.LinkedHashMap" ).init();
variables.headers.put( "Content-Type", "application/json" );
variables.requestID = createUUID();
variables.httpClient = arguments.httpClient;
variables.queryParams = [];
variables.headers = createObject( "java", "java.util.LinkedHashMap" ).init();
variables.files = [];
variables.requestCallbacks = [];
variables.responseCallbacks = [];
Expand Down Expand Up @@ -737,6 +736,17 @@ component accessors="true" {
return this;
}

/**
* A convenience method to set the body format and Content-Type to xml.
*
* @returns The HyperRequest instance.
*/
function asXML() {
setBodyFormat( "xml" );
setContentType( "application/xml" );
return this;
}

/**
* Attaches a file to the Hyper request.
* Also sets the Content-Type as `multipart/form-data`.
Expand Down Expand Up @@ -899,9 +909,8 @@ component accessors="true" {
setBody( "" );
setBodyFormat( "json" );
setReferrer( javacast( "null", "" ) );
variables.queryParams = [];
variables.headers = createObject( "java", "java.util.LinkedHashMap" ).init();
variables.headers.put( "Content-Type", "application/json" );
variables.queryParams = [];
variables.headers = createObject( "java", "java.util.LinkedHashMap" ).init();
variables.files = [];
variables.requestCallbacks = [];
variables.responseCallbacks = [];
Expand Down
7 changes: 4 additions & 3 deletions tests/specs/integration/DebugSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ component extends="tests.resources.ModuleIntegrationSpec" appMapping="/app" {
.setUrl( "https://example.com" )
.debug();

debug( debugReq );

expect( debugReq ).toBeStruct();

expect( debugReq ).toHaveKey( "attributes" );
Expand All @@ -35,7 +33,10 @@ component extends="tests.resources.ModuleIntegrationSpec" appMapping="/app" {
expect( debugReq.body ).toHaveKey( "body" );
expect( debugReq.body.body ).toBeArray();
expect( debugReq.body.body ).toHaveLength( 1 );
expect( debugReq.body.body[ 1 ] ).toBe( binaryBody );
expect( debugReq.body.body[ 1 ] ).toBe( {
"type" : "body",
"value" : binaryBody
} );
expect( debugReq.body ).toHaveKey( "files" );
expect( debugReq.body.files ).toBeEmpty();

Expand Down
6 changes: 6 additions & 0 deletions tests/specs/unit/HyperRequestSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ component extends="testbox.system.BaseSpec" {
} );

req.withHeaders( { "Accept" : "application/xml" } )
.asJSON()
.patch( "https://jsonplaceholder.typicode.com/posts/1" );
expect( method ).toBe( "PATCH" );
expect( headers ).toBe( {
Expand Down Expand Up @@ -215,11 +216,16 @@ component extends="testbox.system.BaseSpec" {
req.setBody( '{"query":{},"size":0,"from":0}' );
expect( req.prepareBody() ).toBe( '{"query":{},"size":0,"from":0}' );
} );

it( "can handle a JSON body format with a body as an struct", function() {
req.setBodyFormat( "json" );
req.setBody( { "query" : {}, "size" : 0, "from" : 0 } );
expect( req.prepareBody() ).toBe( '{"query":{},"size":0,"from":0}' );
} );

it( "defaults to no Content-Type or Headers", function() {
expect( req.getHeaders() ).toBeEmpty();
} );
} );
}

Expand Down

0 comments on commit 16dab5e

Please sign in to comment.