From 166a61bb23dbb31d5e324509ecdffed0f7ed62fa Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Fri, 29 Jul 2022 22:37:25 +0000 Subject: [PATCH] feat: Allow adding multiple query params with the same key. On the backs of 2cb963e4f761479f1cfad06b8949ce00d2170a7d, this commit documents the methods to use to `append` new query params without overwriting the existing values with the same key. BREAKING CHANGE: The `getQueryParams` and `setQueryParams` methods have changed to represent the new internal structure of query params in Hyper. These now are represented internally as an array of structs to support duplicate keys in query strings. You _probably_ want the `getQueryParamByName` and `getAllQueryParamsByName` instead of `getQueryParams` and `withQueryParams` or `appendQueryParams` methods instead of `setQueryParams`. One more item of note, the `getQueryParam`\ method has been deprecated in favor of `getQueryParamByName`. --- README.md | 71 +++++++++++++++++++++++++++-- models/HyperRequest.cfc | 45 +++++++++++++++--- tests/specs/integration/GetSpec.cfc | 24 ++++++++++ 3 files changed, 130 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 21a6193..9d4b2fe 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,16 @@ Hyper runs on Adobe ColdFusion 11+ and Lucee 5+. ColdBox is not required, but mappings are provided for ColdBox users automatically. +### Upgrade from v3 + +The only changes between v3 and v4 is with the `getQueryParams` and `setQueryParams` methods. +These now are represented internally as an array of structs to support duplicate keys in +query strings. You _probably_ want the `getQueryParamByName` and `getAllQueryParamsByName` +instead of `getQueryParams` and `withQueryParams` or `appendQueryParams` methods +instead of `setQueryParams`. + +One more item of note, the `getQueryParam` method has been deprecated in favor of `getQueryParamByName`. + ### HyperBuilder The component you will most likely inject is the `HyperBuilder`. This is @@ -522,6 +532,9 @@ A convenience method to set the Accept header. Gets the query parameters for the request. +**This method returns an array of param structs that is used under the hood by Hyper.** +**You probably want to use `getQueryParamByName` or `getAllQueryParamsByName` instead.** + | Name | Type | Required | Default | Description | | ------------ | ---- | -------- | ------- | ----------- | | No arguments | | | | | @@ -530,13 +543,56 @@ Gets the query parameters for the request. Sets the query parameters for the request. -| Name | Type | Required | Default | Description | -| ----- | ------ | -------- | ------- | ------------------------------------- | -| value | struct | true | | The query parameters for the request. | +**This method accepts an array of param structs that is used under the hood by Hyper.** +**You probably want to use `withQueryParams` or `appendQueryParams` instead.** + +If needed, param structs have two keys, `name` and `value`. + +| Name | Type | Required | Default | Description | +| ----- | ------ | -------- | ------- | ---------------------------------------------------------- | +| value | array | true | | The query parameters for the as an array of param structs. | + +##### `getQueryParam` + +**DEPRECATED:** Use `getQueryParamByName` +Gets the first value for a certian query parameter. +Returns an empty string if the query parameter does not exist. + +| Name | Type | Required | Default | Description | +| ---- | ------ | -------- | ------- | -------------------------------------------------------------| +| name | string | true | | The name of the query parameter to retrieve the first value. | + +##### `getQueryParamByName` + +Gets the first value for a certian query parameter. +Returns an empty string if the query parameter does not exist. + +| Name | Type | Required | Default | Description | +| ---- | ------ | -------- | ------- | -------------------------------------------------------------| +| name | string | true | | The name of the query parameter to retrieve the first value. | + +##### `getAllQueryParamsByName` + +Get all the values for a certian query parameter. +Returns an empty array if the query parameter does not exist. + +| Name | Type | Required | Default | Description | +| ---- | ------ | -------- | ------- | -------------------------------------------------------------- | +| name | string | true | | The name of the query parameter to retrieve all of its values. | ##### `setQueryParam` Set a query parameter for the request. +Note: This removes all other query params with the same name. + +| Name | Type | Required | Default | Description | +| ----- | ------ | -------- | ------- | --------------------------------- | +| name | string | true | | The name of the query parameter. | +| value | string | true | | The value of the query parameter. | + +##### `appendQueryParam` + +Append a query parameter for the request. | Name | Type | Required | Default | Description | | ----- | ------ | -------- | ------- | --------------------------------- | @@ -546,6 +602,15 @@ Set a query parameter for the request. ##### `withQueryParams` Add additional query parameters to the request. +Note: This will remove any values with duplicate keys prior to adding the new struct of params. + +| Name | Type | Required | Default | Description | +| ----------- | ------ | -------- | ------- | --------------------------------------------------- | +| queryParams | struct | true | | A struct of query parameters to add to the request. | + +##### `appendQueryParams` + +Appends additional query parameters to the request. | Name | Type | Required | Default | Description | | ----------- | ------ | -------- | ------- | --------------------------------------------------- | diff --git a/models/HyperRequest.cfc b/models/HyperRequest.cfc index 112b666..ed9f836 100644 --- a/models/HyperRequest.cfc +++ b/models/HyperRequest.cfc @@ -386,8 +386,9 @@ component accessors="true" { /** * Add additional query parameters to the request. + * Note: This will remove any values with duplicate keys prior to adding the new struct of params. * - * @queryParams A struct of query parameters to add to the request. + * @queryParams A struct of query parameters to set for the request. * * @returns The HyperRequest instance. */ @@ -398,9 +399,23 @@ component accessors="true" { return this; } + /** + * Appends additional query parameters to the request. + * + * @queryParams A struct of query parameters to append to the request. + * + * @returns The HyperRequest instance. + */ + function appendQueryParams( queryParams = {} ) { + for ( var name in arguments.queryParams ) { + appendQueryParam( name, arguments.queryParams[ name ] ); + } + return this; + } + /** * Set a query parameter for the request. - * This will delete any existing query params for the key first. + * Note: This removes all other query params with the same name. * * @name The name of the query parameter. * @value The value of the query parameter. @@ -427,7 +442,10 @@ component accessors="true" { * @returns The HyperRequest instance. */ function appendQueryParam( name, value ) { - variables.queryParams.append( { "name": arguments.name, "value": arguments.value } ); + variables.queryParams.append( { + "name" : arguments.name, + "value" : arguments.value + } ); return this; } @@ -449,14 +467,27 @@ component accessors="true" { } /** - * Get the first value for a certian query parameter. + * Gets the first value for a certian query parameter. + * @deprecated Use `getQueryParamByName` * * @name The name of the query parameter to retrieve its value. * - * @returns The value of the query parameter. + * @returns The value of the query parameter to retrieve the first value. * Returns an empty string if the query parameter does not exist. */ function getQueryParam( name ) { + return getQueryParamByName( arguments.name ); + } + + /** + * Gets the first value for a certian query parameter. + * + * @name The name of the query parameter to retrieve its value. + * + * @returns The value of the query parameter to retrieve the first value. + * Returns an empty string if the query parameter does not exist. + */ + function getQueryParamByName( name ) { for ( var i = 1; i <= variables.queryParams.len(); i++ ) { var param = variables.queryParams[ i ]; if ( param.name == arguments.name ) { @@ -469,12 +500,12 @@ component accessors="true" { /** * Get all the values for a certian query parameter. * - * @name The name of the query parameter to retrieve its value. + * @name The name of the query parameter to retrieve all of its values. * * @returns An array of values for the query parameter. * Returns an empty array if the query parameter does not exist. */ - function getAllQueryParam( name ) { + function getAllQueryParamsByName( name ) { return variables.queryParams.filter( function( param ) { return param.name == name; } ); diff --git a/tests/specs/integration/GetSpec.cfc b/tests/specs/integration/GetSpec.cfc index 837fec6..c371fc6 100644 --- a/tests/specs/integration/GetSpec.cfc +++ b/tests/specs/integration/GetSpec.cfc @@ -78,6 +78,30 @@ component extends="tests.resources.ModuleIntegrationSpec" appMapping="/app" { ); } ); + it( "can add additional query params with the same name", function() { + var res = hyper + .setBaseUrl( "https://jsonplaceholder.typicode.com" ) + .setUrl( "/posts" ) + .appendQueryParam( "foo", "bar" ) + .appendQueryParam( "foo", "baz" ) + .get(); + expect( res.getRequest().getFullUrl( withQueryString = true ) ).toBeWithCase( + "https://jsonplaceholder.typicode.com/posts?foo=bar&foo=baz" + ); + } ); + + it( "can append multiple query params at once", function() { + var res = hyper + .setBaseUrl( "https://jsonplaceholder.typicode.com" ) + .setUrl( "/posts" ) + .appendQueryParams( { "foo" : "bar" } ) + .appendQueryParams( { "foo" : "baz", "one" : "two" } ) + .get(); + expect( res.getRequest().getFullUrl( withQueryString = true ) ).toBeWithCase( + "https://jsonplaceholder.typicode.com/posts?foo=bar&foo=baz&one=two" + ); + } ); + it( "has access to the original HyperRequest in the HyperResponse", function() { var res = hyper.get( "https://jsonplaceholder.typicode.com/posts/1" ); expect( res ).toBeInstanceOf( "HyperResponse", "A HyperResponse object should have been returned." );