diff --git a/docs/Getting-Started/Configuration.md b/docs/Getting-Started/Configuration.md index a5849b3..79d9447 100644 --- a/docs/Getting-Started/Configuration.md +++ b/docs/Getting-Started/Configuration.md @@ -83,4 +83,79 @@ You will need to read these settings into the coldfusion server upon server star {% hint style="warning" %} For security reasons, make sure to add `.env` to your `.gitignore` file to avoid committing environment secrets to github/your git server. -{% endhint %} \ No newline at end of file +{% endhint %} + +## Request Parameter Overrides + +In addition to global configuration settings, you can override request-specific parameters on a per-call basis. This is useful when you need different timeouts or settings for specific operations. + +### Builder-Level Overrides + +Builder objects (like `IndexBuilder`, `SearchBuilder`, `Document`, etc.) extend `BaseModel` and support fluent configuration of request parameters using the `.with*()` method: + +```cfc +// Create an index with a custom 10-second timeout +get( "IndexBuilder@cbelasticsearch" ) + .new( name = "books" ) + .withTimeout( 10 ) + .save(); +``` + +Here's an example of setting a custom header on a search builder: + +```cfc +// Search with custom headers +get( "SearchBuilder@cbelasticsearch" ) + .new() + .withHeader( "X-Custom-Header", "MyValue" ) + .setQuery( ... ) + .execute(); +``` + +### Direct Client Method Overrides + +All public methods in `HyperClient` accept a `requestOverrides` struct as the final parameter. This allows you to pass request-specific configuration directly to the client: + +```cfc +// Check if index exists with a 45-second timeout +get( "HyperClient@cbelasticsearch" ) + .indexExists( "foo", { "timeout" : 45 } ); + +// Get index settings with custom timeout +get( "HyperClient@cbelasticsearch" ) + .getSettings( "myIndex", { "timeout" : 30 } ); + +// Search with request overrides +get( "HyperClient@cbelasticsearch" ) + .executeSearch( + searchBuilder, + { "timeout" : 60, "readTimeout" : 5000 } + ); +``` + +### Merging Overrides + +When both builder-level and client-level overrides are provided, they are merged together with client-level overrides taking precedence: + +```cfc +var builder = get( "IndexBuilder@cbelasticsearch" ) + .new( name = "books" ) + .withTimeout( 10 ); // Builder-level: 10 seconds + +// Client call with override - the 30-second timeout wins +get( "HyperClient@cbelasticsearch" ) + .applyIndex( builder, { "timeout" : 30 } ); +``` + +### Available Override Parameters + +Any HyperRequest parameters can be overridden on a per-request basis. These include: + +- `timeout` - Connection timeout in seconds +- `username` - Username for authentication +- `password` - Password for authentication +- `maximumRedirects` - Maximum number of redirects to follow +- `retries` - Number of times to retry a request in case of failure +- `proxyUser` - Username for proxy authentication +- `proxyPassword` - Password for proxy authentication +- `headers` - Struct of custom headers to include in the request - use `.withHeader( "X-Custom-Header", "MyValue" )` to fluently set headers on builders \ No newline at end of file diff --git a/models/AliasBuilder.cfc b/models/AliasBuilder.cfc index 1790386..509ef5f 100644 --- a/models/AliasBuilder.cfc +++ b/models/AliasBuilder.cfc @@ -1,9 +1,14 @@ -component accessors="true" { +component accessors="true" extends="BaseModel" { property name="action"; property name="indexName"; property name="aliasName"; + function init(){ + super.init(); + return this; + } + function add( required string indexName, required string aliasName ){ arguments.action = "add"; return new ( argumentCollection = arguments ); diff --git a/models/BaseModel.cfc b/models/BaseModel.cfc new file mode 100644 index 0000000..e5c1138 --- /dev/null +++ b/models/BaseModel.cfc @@ -0,0 +1,57 @@ +/** + * + * Base Model for Fluent Request Overrides + * + * Provides a foundation for builder and model objects to support per-request + * configuration overrides via fluent method chaining. All builder classes + * (IndexBuilder, SearchBuilder, AliasBuilder, Document, Pipeline, etc.) extend + * this component to inherit request override capabilities. + * + * @package cbElasticsearch.models + * @author Jon Clausen + * @license Apache v2.0 + * + */ +component accessors="true" { + + property name="requestOverrides" type="struct"; + + public BaseModel function init(){ + variables.requestOverrides = {}; + return this; + } + + public BaseModel function withRequestOverrides( struct overrides ){ + setRequestOverrides( arguments.overrides ); + return this; + } + + /** + * Handles any missing methods that start with "with" and adds the value to the requestOverrides struct for request configuration + * Example: withTimeout( 1000 ) would set a default timeout of 1000ms on all requests created by this model + * + * @param methodName the name of the method being called + * @param arguments the arguments passed to the method, where arguments[1] is expected to be the value to set for the default + * @return returns the model instance for chaining + */ + public BaseModel function onMissingMethod( string missingMethodName, struct missingMethodArguments ){ + if ( left( missingMethodName, 4 ) == "with" ) { + var args = []; + if ( !isNull( missingMethodArguments ) ) { + args = missingMethodArguments.reduce( function( acc, key, val ){ + acc.append( val ); + return acc; + }, [] ); + } + variables.requestOverrides[ lCase( replace( missingMethodName, "with", "" ) ) ] = args; + return this; + } + // For all other missing methods, raise a proper missing-method exception + throw( + type = "MissingMethodException", + message = "No such method found for #missingMethodName# on #getMetadata( this ).name#.", + detail = "Use with#missingMethodName# to set default values for request overrides on this model." + ); + } + +} diff --git a/models/Document.cfc b/models/Document.cfc index 5ae858b..34dfcb2 100644 --- a/models/Document.cfc +++ b/models/Document.cfc @@ -7,7 +7,7 @@ * @license Apache v2.0 * */ -component accessors="true" { +component accessors="true" extends="BaseModel" { property name="config" inject="Config@cbelasticsearch"; @@ -51,6 +51,11 @@ component accessors="true" { */ property name="fields" type="struct"; + function init(){ + super.init(); + return this; + } + function onDIComplete(){ reset(); } diff --git a/models/ILMPolicyBuilder.cfc b/models/ILMPolicyBuilder.cfc index d49b9c4..bbdfe71 100644 --- a/models/ILMPolicyBuilder.cfc +++ b/models/ILMPolicyBuilder.cfc @@ -1,4 +1,4 @@ -component accessors="true" { +component accessors="true" extends="BaseModel" { property name="policyName"; @@ -20,6 +20,11 @@ component accessors="true" { * @phases a struct of phases ( optional ) * @meta optional struct of meta */ + ILMPolicyBuilder function init(){ + super.init(); + return this; + } + ILMPolicyBuilder function new( required string policyName, struct phases, diff --git a/models/IndexBuilder.cfc b/models/IndexBuilder.cfc index 86ecbfc..b9cc2fa 100644 --- a/models/IndexBuilder.cfc +++ b/models/IndexBuilder.cfc @@ -7,7 +7,7 @@ * @license Apache v2.0 * */ -component accessors="true" { +component accessors="true" extends="BaseModel" { // The name of our index property name="indexName"; @@ -25,6 +25,11 @@ component accessors="true" { property name="aliases"; + function init(){ + super.init(); + return this; + } + function onDIComplete(){ reset(); } diff --git a/models/Pipeline.cfc b/models/Pipeline.cfc index 6d77eb6..ec84bf5 100644 --- a/models/Pipeline.cfc +++ b/models/Pipeline.cfc @@ -1,4 +1,4 @@ -component accessors="true" threadSafe { +component accessors="true" extends="BaseModel" threadSafe { property name="Util" inject="Util@cbelasticsearch"; @@ -30,6 +30,7 @@ component accessors="true" threadSafe { cbElasticsearch.models.Pipeline function init( struct definition ){ + super.init(); variables.description = ""; variables.processors = []; diff --git a/models/SearchBuilder.cfc b/models/SearchBuilder.cfc index 7dbdf71..cb6f015 100644 --- a/models/SearchBuilder.cfc +++ b/models/SearchBuilder.cfc @@ -7,7 +7,7 @@ * @license Apache v2.0 * */ -component accessors="true" { +component accessors="true" extends="BaseModel" { property name="configObject" inject="Config@cbelasticsearch"; @@ -15,6 +15,7 @@ component accessors="true" { * Property containing the index name of the active builder search **/ property name="index"; + /** * Property containing the object type within the index **/ @@ -104,6 +105,11 @@ component accessors="true" { property name="size"; property name="from"; + function init(){ + super.init(); + return this; + } + function onDIComplete(){ reset(); diff --git a/models/io/HyperClient.cfc b/models/io/HyperClient.cfc index a801038..3616fac 100644 --- a/models/io/HyperClient.cfc +++ b/models/io/HyperClient.cfc @@ -169,15 +169,22 @@ component accessors="true" threadSafe singleton { /** * Execute a client search request * @searchBuilder SearchBuilder An instance of the SearchBuilder object + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return SearchResult The search result object * @interfaced **/ cbElasticsearch.models.SearchResult function executeSearch( - required cbElasticsearch.models.searchBuilder searchBuilder + required cbElasticsearch.models.searchBuilder searchBuilder, + struct requestOverrides = {} ){ - var requestBuilder = variables.nodePool - .newRequest( SearchBuilder.getIndex() & "/_search", "POST" ) + var mergedOverrides = arguments.searchBuilder.getRequestOverrides().append( arguments.requestOverrides ); + var requestBuilder = variables.nodePool + .newRequest( + searchBuilder.getIndex() & "/_search", + "POST", + mergedOverrides + ) .setBody( arguments.searchBuilder.getJSON() ) .asJSON(); @@ -199,13 +206,22 @@ component accessors="true" threadSafe singleton { /** * Retreives a count of documents matching the given query * @searchBuilder [SearchBuilder] An instance of the SearchBuilder object + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return numeric The returned count matching the search parameters * @interfaced */ - numeric function count( required cbElasticsearch.models.searchBuilder searchBuilder ){ - var requestBuilder = variables.nodePool - .newRequest( SearchBuilder.getIndex() & "/_count", "POST" ) + numeric function count( + required cbElasticsearch.models.searchBuilder searchBuilder, + struct requestOverrides = {} + ){ + var mergedOverrides = arguments.searchBuilder.getRequestOverrides().append( arguments.requestOverrides ); + var requestBuilder = variables.nodePool + .newRequest( + searchBuilder.getIndex() & "/_count", + "POST", + mergedOverrides + ) .setBody( getUtil().toJSON( { "query" : arguments.searchBuilder.getQuery() } ) ) .asJSON(); @@ -222,12 +238,17 @@ component accessors="true" threadSafe singleton { * Verifies whether an index exists * * @indexName string the name of the index + * @requestOverrides struct A struct of request overrides to pass to the node pool * @interfaced **/ - boolean function indexExists( required string indexName ){ + boolean function indexExists( required string indexName, struct requestOverrides = {} ){ return ( variables.nodePool - .newRequest( arguments.indexName, "HEAD" ) + .newRequest( + arguments.indexName, + "HEAD", + arguments.requestOverrides + ) .send() .getStatusCode() < 400 ); @@ -237,11 +258,15 @@ component accessors="true" threadSafe singleton { * Verifies whether an index mapping exists * * @indexName string the name of the index + * @requestOverrides struct A struct of request overrides to pass to the node pool * @interfaced **/ - boolean function indexMappingExists( required string indexName ){ + boolean function indexMappingExists( required string indexName, struct requestOverrides = {} ){ try { - var mappings = getMappings( arguments.indexName ); + var mappings = getMappings( + indexName = arguments.indexName, + requestOverrides = arguments.requestOverrides + ); } catch ( any e ) { return false; } @@ -253,10 +278,15 @@ component accessors="true" threadSafe singleton { * Returns the settings for an index * * @indexName string the name of the index ( optional ) if null returns all settings for the server + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - struct function getSettings( string indexName ){ + struct function getSettings( string indexName, struct requestOverrides = {} ){ var response = variables.nodePool - .newRequest( !isNull( arguments.indexName ) ? arguments.indexName & "/_settings" : "_settings", "GET" ) + .newRequest( + !isNull( arguments.indexName ) ? arguments.indexName & "/_settings" : "_settings", + "GET", + arguments.requestOverrides + ) .send(); if ( response.getStatusCode() != 200 ) { @@ -273,8 +303,13 @@ component accessors="true" threadSafe singleton { * * @indexName string the name of the index * @field an optional field name or field pattern + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - struct function getMappings( required string indexName, string field ){ + struct function getMappings( + required string indexName, + string field, + struct requestOverrides = {} + ){ var path = arguments.indexName & "/_mapping"; if ( !isNull( arguments.field ) ) { path &= "/field/" & arguments.field; @@ -283,7 +318,7 @@ component accessors="true" threadSafe singleton { arguments.field = "*"; path &= "/field/" & arguments.field; } - var response = variables.nodePool.newRequest( path, "GET" ).send(); + var response = variables.nodePool.newRequest( path, "GET", arguments.requestOverrides ).send(); if ( response.getStatusCode() != 200 ) { onResponseFailure( response ); @@ -311,11 +346,15 @@ component accessors="true" threadSafe singleton { /** * Applies an index item ( create/update ) * @indexBuilder IndexBuilder An instance of the IndexBuilder object + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return boolean True if the index was successfully applied * @interfaced **/ - boolean function applyIndex( required cbElasticsearch.models.IndexBuilder indexBuilder ){ + boolean function applyIndex( + required cbElasticsearch.models.IndexBuilder indexBuilder, + struct requestOverrides = {} + ){ var indexResult = {}; if ( isNull( arguments.indexBuilder.getIndexName() ) ) { @@ -358,9 +397,11 @@ component accessors="true" threadSafe singleton { structDelete( indexDSL.mappings, "_all" ); } - if ( !indexExists( indexName ) ) { + var mergedOverrides = arguments.indexBuilder.getRequestOverrides().append( arguments.requestOverrides ); + + if ( !indexExists( indexName, mergedOverrides ) ) { var requestBuilder = variables.nodePool - .newRequest( indexName, "PUT" ) + .newRequest( indexName, "PUT", mergedOverrides ) .setBody( getUtil().toJSON( indexDSL ) ) .asJSON(); @@ -378,20 +419,28 @@ component accessors="true" threadSafe singleton { } else { if ( structKeyExists( indexDSL, "mappings" ) ) { if ( !isMajorVersion( 6 ) ) { - indexResult[ "mappings" ] = applyMapping( indexName, "_doc", indexDSL.mappings ); + indexResult[ "mappings" ] = applyMapping( + indexName, + "_doc", + indexDSL.mappings, + mergedOverrides + ); } else { - indexResult[ "mappings" ] = applyMappings( indexName, indexDSL.mappings ); + indexResult[ "mappings" ] = applyMappings( indexName, indexDSL.mappings, mergedOverrides ); } } if ( structKeyExists( indexDSL, "settings" ) && !structIsEmpty( indexDSL.settings ) ) { var requestBuilder = variables.nodePool - .newRequest( indexName & "/_settings", "PUT" ) + .newRequest( + indexName & "/_settings", + "PUT", + mergedOverrides + ) .setBody( getUtil().toJSON( indexDSL.settings ) ) .asJSON(); var response = requestBuilder.send(); - if ( structKeyExists( response.json(), "error" ) ) { onResponseFailure( response ); } @@ -406,11 +455,16 @@ component accessors="true" threadSafe singleton { * Deletes an index * * @indexName string the name of the index to be deleted + * @requestOverrides struct A struct of request overrides to pass to the node pool * **/ - struct function deleteIndex( required string indexName ){ + struct function deleteIndex( required string indexName, struct requestOverrides = {} ){ return variables.nodePool - .newRequest( arguments.indexName, "DELETE" ) + .newRequest( + arguments.indexName, + "DELETE", + arguments.requestOverrides + ) .send() .json(); } @@ -427,6 +481,7 @@ component accessors="true" threadSafe singleton { * @script any A script to run while reindexing. * @throwOnError boolean Whether to throw an exception if the reindexing fails. This flag is * only used if `waitForCompletion` is `true`. + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return any Struct result of the reindex action if waiting for completion or a Task object if dispatched asnyc **/ @@ -436,10 +491,11 @@ component accessors="true" threadSafe singleton { boolean waitForCompletion = true, any params, any script, - boolean throwOnError = true + boolean throwOnError = true, + struct requestOverrides = {} ){ var requestBuilder = variables.nodePool - .newRequest( "_reindex", "POST" ) + .newRequest( "_reindex", "POST", arguments.requestOverrides ) .setQueryParam( "wait_for_completion", arguments.waitForCompletion ); var body = { @@ -515,12 +571,21 @@ component accessors="true" threadSafe singleton { * * @indexName string|array Index name or alias. Can accept an array of index/alias names. * @params struct Struct of query parameters to influence the request. For example: `{ "ignore_unavailable" : true }` + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - struct function refreshIndex( required any indexName, struct params = {} ){ + struct function refreshIndex( + required any indexName, + struct params = {}, + struct requestOverrides = {} + ){ if ( isArray( arguments.indexName ) ) { arguments.indexName = arrayToList( arguments.indexName ); } - var refreshRequest = variables.nodePool.newRequest( "/#arguments.indexName#/_refresh", "post" ); + var refreshRequest = variables.nodePool.newRequest( + "/#arguments.indexName#/_refresh", + "post", + arguments.requestOverrides + ); return refreshRequest .withQueryParams( arguments.params ) @@ -534,11 +599,13 @@ component accessors="true" threadSafe singleton { * @indexName string|array Index name or alias. Can accept an array of index/alias names. * @metrics array Array of index metrics to retrieve. I.e. `[ "completion","refresh", "request_cache" ]`. * @params struct Struct of query parameters to influence the request. For example: `{ "expand_wildcards" : "none", "level" : "shards" } + * @requestOverrides struct A struct of request overrides to pass to the node pool */ struct function getIndexStats( any indexName, - array metrics = [], - struct params = {} + array metrics = [], + struct params = {}, + struct requestOverrides = {} ){ if ( isArray( arguments.indexName ) ) { arguments.indexName = arrayToList( arguments.indexName ); @@ -549,7 +616,11 @@ component accessors="true" threadSafe singleton { endpoint.prepend( arguments.indexName ); } endpoint.append( arrayToList( metrics ) ); - var statsRequest = variables.nodePool.newRequest( arrayToList( endpoint, "/" ), "get" ); + var statsRequest = variables.nodePool.newRequest( + arrayToList( endpoint, "/" ), + "get", + arguments.requestOverrides + ); return statsRequest .withQueryParams( arguments.params ) @@ -564,10 +635,19 @@ component accessors="true" threadSafe singleton { * * @indexName string|array Index name or alias. Can accept an array of index/alias names, as well as wildcard expressions or `_all`. * @params struct Struct of query parameters to influence the request. For example: `{ "expand_wildcards" : "none" } + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - function openIndex( required any indexName, any params ){ + function openIndex( + required any indexName, + any params, + struct requestOverrides = {} + ){ var requestBuilder = getNodePool() - .newRequest( "#arrayToList( arguments.indexName )#/_open", "POST" ) + .newRequest( + "#arrayToList( arguments.indexName )#/_open", + "POST", + arguments.requestOverrides + ) .asJSON(); if ( structKeyExists( arguments, "params" ) ) { @@ -592,10 +672,19 @@ component accessors="true" threadSafe singleton { * * @indexName string|array Index name or alias. Can accept an array of index/alias names, as well as wildcard expressions or `_all`. * @params struct Struct of query parameters to influence the request. For example: `{ "expand_wildcards" : "none" } + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - function closeIndex( required any indexName, any params ){ + function closeIndex( + required any indexName, + any params, + struct requestOverrides = {} + ){ var requestBuilder = getNodePool() - .newRequest( "#arrayToList( arguments.indexName )#/_close", "POST" ) + .newRequest( + "#arrayToList( arguments.indexName )#/_close", + "POST", + arguments.requestOverrides + ) .asJSON(); if ( structKeyExists( arguments, "params" ) ) { @@ -620,12 +709,14 @@ component accessors="true" threadSafe singleton { * @id string Document ID to query term vectors on. * @params struct Struct of query parameters to influence the request. For example: `"offsets": false }` * @options struct Body payload to send. For example: `{ "filter": { "max_num_terms": 3 } }` + * @requestOverrides struct A struct of request overrides to pass to the node pool */ struct function getTermVectors( required string indexName, - string id = "", - any fields = [], - struct options = {} + string id = "", + any fields = [], + struct options = {}, + struct requestOverrides = {} ){ arguments.options[ "fields" ] = arguments.fields; if ( !isArray( arguments.options[ "fields" ] ) ) { @@ -636,7 +727,11 @@ component accessors="true" threadSafe singleton { if ( arguments.id != "" ) { endpoint.append( arguments.id ); } - var vectorRequest = variables.nodePool.newRequest( arrayToList( endpoint, "/" ), "POST" ); + var vectorRequest = variables.nodePool.newRequest( + arrayToList( endpoint, "/" ), + "POST", + arguments.requestOverrides + ); return vectorRequest .setBody( getUtil().toJSON( arguments.options ) ) @@ -648,9 +743,10 @@ component accessors="true" threadSafe singleton { * Returns a struct containing all indices in the system, with statistics * * @verbose boolean whether to return the full stats output for the index + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - struct function getIndices( verbose = false ){ - var statsRequest = variables.nodePool.newRequest( "_stats", "get" ); + struct function getIndices( verbose = false, struct requestOverrides = {} ){ + var statsRequest = variables.nodePool.newRequest( "_stats", "get", arguments.requestOverrides ); var statsResult = statsRequest.send().json(); @@ -677,10 +773,11 @@ component accessors="true" threadSafe singleton { * Returns a struct containing the mappings of all aliases in the cluster * * @aliases + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - struct function getAliases(){ + struct function getAliases( struct requestOverrides = {} ){ var aliasesResult = variables.nodePool - .newRequest( "_alias" ) + .newRequest( "_alias", "GET", arguments.requestOverrides ) .setThrowOnError( true ) .send() .json(); @@ -722,10 +819,11 @@ component accessors="true" threadSafe singleton { * Applies an alias (or array of aliases) * * @aliases AliasBuilder An AliasBuilder instance (or array of instances) + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return boolean Boolean result as to whether the operations were successful **/ - boolean function applyAliases( required any aliases ){ + boolean function applyAliases( required any aliases, struct requestOverrides = {} ){ arguments.aliases = isArray( arguments.aliases ) ? arguments.aliases : [ arguments.aliases ]; var requestBody = { "actions" : [] }; for ( var alias in arguments.aliases ) { @@ -737,8 +835,10 @@ component accessors="true" threadSafe singleton { } ); } + var mergedOverrides = arguments.aliases[ 1 ].getRequestOverrides().append( arguments.requestOverrides ); + return variables.nodePool - .newRequest( "_aliases", "POST" ) + .newRequest( "_aliases", "POST", mergedOverrides ) .setBody( getUtil().toJSON( requestBody ) ) .asJSON() .setThrowOnError( true ) @@ -753,12 +853,14 @@ component accessors="true" threadSafe singleton { * @indexName string the name of the index * @mappingName string the name of the mapping * @mappingConfig struct the mapping configuration struct + * @requestOverrides struct A struct of request overrides to pass to the node pool * @interfaced **/ struct function applyMapping( required string indexName, string mappingName, - required struct mappingConfig + required struct mappingConfig, + struct requestOverrides = {} ){ if ( !isMajorVersion( 6 ) ) { // remove v7/v8 unsupported keys @@ -773,7 +875,11 @@ component accessors="true" threadSafe singleton { } var mappingResult = variables.nodePool - .newRequest( "#arguments.indexName#/_mapping", "PUT" ) + .newRequest( + "#arguments.indexName#/_mapping", + "PUT", + arguments.requestOverrides + ) .setBody( JSONMapping ) .asJSON() .send(); @@ -792,9 +898,14 @@ component accessors="true" threadSafe singleton { * Applies multiple mappings to an index * @indexName string The name of the index * @mappings struct a struct containing the mapping configuration + * @requestOverrides struct A struct of request overrides to pass to the node pool * @interfaced **/ - struct function applyMappings( required string indexName, required struct mappings ){ + struct function applyMappings( + required string indexName, + required struct mappings, + struct requestOverrides = {} + ){ var mappingResults = {}; if ( arguments.mappings.keyExists( "properties" ) ) { @@ -805,7 +916,8 @@ component accessors="true" threadSafe singleton { mappingResults[ mapKey ] = applyMapping( arguments.indexName, mapKey, - arguments.mappings[ mapKey ] + arguments.mappings[ mapKey ], + arguments.requestOverrides ); } @@ -819,6 +931,7 @@ component accessors="true" threadSafe singleton { * @index string The name of the index * @type type The name of the type * @params struct A struct of params to pass to the request + * @requestOverrides struct A struct of request overrides to pass to the node pool * @interfaced * * @return any Returns a Document object if found, otherwise returns null @@ -827,7 +940,8 @@ component accessors="true" threadSafe singleton { required any id, string index, string type, - struct params = {} + struct params = {}, + struct requestOverrides = {} ){ if ( isNull( arguments.index ) ) { arguments.index = variables.instanceConfig.get( "defaultIndex" ); @@ -838,7 +952,11 @@ component accessors="true" threadSafe singleton { } var getRequest = variables.nodePool - .newRequest( "#arguments.index#/#arguments.type#/#urlEncodedFormat( arguments.id )#" ) + .newRequest( + "#arguments.index#/#arguments.type#/#urlEncodedFormat( arguments.id )#", + "GET", + arguments.requestOverrides + ) .setThrowOnError( false ); arguments.params @@ -871,6 +989,7 @@ component accessors="true" threadSafe singleton { * @keys array An array of keys to retrieve * @index string The name of the index * @type type The name of the type + * @requestOverrides struct A struct of request overrides to pass to the node pool * @interfaced * * @return array An array of Document objects @@ -879,7 +998,8 @@ component accessors="true" threadSafe singleton { required array keys, string index, string type, - struct params = {} + struct params = {}, + struct requestOverrides = {} ){ if ( isNull( arguments.index ) ) { arguments.index = variables.instanceConfig.get( "defaultIndex" ); @@ -899,7 +1019,7 @@ component accessors="true" threadSafe singleton { } ); var multiRequest = variables.nodePool - .newRequest( "_mget", "POST" ) + .newRequest( "_mget", "POST", arguments.requestOverrides ) .setBody( getUtil().toJSON( requestBody ) ) .asJSON(); @@ -935,11 +1055,22 @@ component accessors="true" threadSafe singleton { * * @taskId string The identifier of the task to retreive * @taskObj Task The task object used for population - defaults to a new task + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @interfaced */ - any function getTask( required string taskId, cbElasticsearch.models.Task taskObj = newTask() ){ - var taskResult = variables.nodePool.newRequest( "_tasks/#arguments.taskId#" ).send(); + any function getTask( + required string taskId, + cbElasticsearch.models.Task taskObj = newTask(), + struct requestOverrides = {} + ){ + var taskResult = variables.nodePool + .newRequest( + "_tasks/#arguments.taskId#", + "GET", + arguments.requestOverrides + ) + .send(); if ( taskResult.getStatusCode() != 200 ) { onResponseFailure( taskResult ); @@ -951,11 +1082,13 @@ component accessors="true" threadSafe singleton { /** * Retreives all tasks running on the cluster * + * @requestOverrides struct A struct of request overrides to pass to the node pool + * * @interfaced */ - any function getTasks(){ + any function getTasks( struct requestOverrides = {} ){ var tasksResult = variables.nodePool - .newRequest( "_tasks" ) + .newRequest( "_tasks", "GET", arguments.requestOverrides ) .setQueryParam( "detailed", true ) .send() .json(); @@ -978,18 +1111,29 @@ component accessors="true" threadSafe singleton { /** * @document Document@cbElasticSearch An instance of the elasticsearch Document object - * @refresh any if `true`, will return a newly populated instance of the document retreived from the index ( useful for pipelined saves ). if `"wait_for"`, will block until the next index refresh ingests the document update. + * @refresh any if `true`, will return a newly populated instance of the document retreived from the index ( useful for pipelined saves ). if `"wait_for"`, will block until the next index refresh ingests the document update. + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return Document The saved cbElasticsearch Document object * @interfaced **/ - cbElasticsearch.models.Document function save( required cbElasticsearch.models.Document document, any refresh ){ + cbElasticsearch.models.Document function save( + required cbElasticsearch.models.Document document, + any refresh, + struct requestOverrides = {} + ){ + var mergedOverrides = arguments.document.getRequestOverrides().append( arguments.requestOverrides ); if ( isNull( arguments.document.getId() ) ) { - var saveRequest = variables.nodePool.newRequest( "#arguments.document.getIndex()#/_doc", "POST" ); + var saveRequest = variables.nodePool.newRequest( + "#arguments.document.getIndex()#/_doc", + "POST", + mergedOverrides + ); } else { var saveRequest = variables.nodePool.newRequest( "#arguments.document.getIndex()#/_doc/#urlEncodedFormat( arguments.document.getId() )#", - "PUT" + "PUT", + mergedOverrides ); } @@ -1040,6 +1184,7 @@ component accessors="true" threadSafe singleton { * @identifier string The identifier of the elasticsearch document * @contents struct A struct of contents to update. May contain script/doc information, along with upsert parameters * @params struct A struct of params to provide to the deletion request + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return void * @@ -1049,7 +1194,8 @@ component accessors="true" threadSafe singleton { required string index, required string identifier, required struct contents, - struct params = {} + struct params = {}, + struct requestOverrides = {} ){ if ( !arguments.contents.keyExists( "doc" ) && !arguments.contents.keyExists( "script" ) ) { var directive = { "doc" : arguments.contents }; @@ -1058,7 +1204,11 @@ component accessors="true" threadSafe singleton { } var patchRequest = variables.nodePool - .newRequest( "#arguments.index#/_update/#urlEncodedFormat( arguments.identifier )#", "POST" ) + .newRequest( + "#arguments.index#/_update/#urlEncodedFormat( arguments.identifier )#", + "POST", + arguments.requestOverrides + ) .setBody( getUtil().toJSON( directive ) ) .asJSON(); @@ -1074,6 +1224,7 @@ component accessors="true" threadSafe singleton { * @document Document the Document object for the document to be deleted * @throwOnError boolean whether to throw an error if the document cannot be deleted ( default: false ) * @params struct a struct of params to provide to the deletion request + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return boolean (true|false) as to whether the doucument was deleted * @@ -1081,14 +1232,16 @@ component accessors="true" threadSafe singleton { **/ boolean function delete( required cbElasticsearch.models.Document document, - boolean throwOnError = true, - struct params = {} + boolean throwOnError = true, + struct params = {}, + struct requestOverrides = {} ){ return deleteById( document.getIndex(), document.getId(), arguments.throwOnError, - arguments.params + arguments.params, + arguments.requestOverrides ); } @@ -1100,6 +1253,7 @@ component accessors="true" threadSafe singleton { * @identifier string the identifier of the document * @throwOnError boolean whether to throw an error if the document cannot be deleted ( default: false ) * @params struct a struct of params to provide to the deletion request + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return boolean (true|false) as to whether the doucument was deleted * @@ -1108,11 +1262,16 @@ component accessors="true" threadSafe singleton { boolean function deleteById( required string index, required string identifier, - boolean throwOnError = true, - params = {} + boolean throwOnError = true, + params = {}, + struct requestOverrides = {} ){ var deleteRequest = variables.nodePool - .newRequest( "#arguments.index#/_doc/#urlEncodedFormat( arguments.identifier )#", "DELETE" ) + .newRequest( + "#arguments.index#/_doc/#urlEncodedFormat( arguments.identifier )#", + "DELETE", + arguments.requestOverrides + ) .asJSON(); parseParams( arguments.params ).each( function( param ){ @@ -1133,10 +1292,12 @@ component accessors="true" threadSafe singleton { * Deletes items in the index by query * @searchBuilder SearchBuilder The search builder object to use for the query * @waitForCompletion boolean Whether to block the request until completion or return a task which can be checked + * @requestOverrides struct A struct of request overrides to pass to the node pool **/ any function deleteByQuery( required cbElasticsearch.models.SearchBuilder searchBuilder, - boolean waitForCompletion = true + boolean waitForCompletion = true, + struct requestOverrides = {} ){ if ( isNull( arguments.searchBuilder.getIndex() ) ) { throw( @@ -1145,9 +1306,11 @@ component accessors="true" threadSafe singleton { ); } - var deleteRequest = variables.nodePool.newRequest( + var mergedOverrides = arguments.searchBuilder.getRequestOverrides().append( arguments.requestOverrides ); + var deleteRequest = variables.nodePool.newRequest( "#arguments.searchBuilder.getIndex()#/_delete_by_query", - "POST" + "POST", + mergedOverrides ); @@ -1178,11 +1341,13 @@ component accessors="true" threadSafe singleton { * @searchBuilder SearchBuilder The search builder object to use for the query * @script struct script to process on the query * @waitForCompletion boolean Whether to block the request until completion or return a task which can be checked + * @requestOverrides struct A struct of request overrides to pass to the node pool **/ any function updateByQuery( required cbElasticsearch.models.SearchBuilder searchBuilder, required struct script, - boolean waitForCompletion = true + boolean waitForCompletion = true, + struct requestOverrides = {} ){ if ( isNull( arguments.searchBuilder.getIndex() ) ) { throw( @@ -1191,8 +1356,13 @@ component accessors="true" threadSafe singleton { ); } - var updateRequest = variables.nodePool - .newRequest( "#arguments.searchBuilder.getIndex()#/_update_by_query", "POST" ) + var mergedOverrides = arguments.searchBuilder.getRequestOverrides().append( arguments.requestOverrides ); + var updateRequest = variables.nodePool + .newRequest( + "#arguments.searchBuilder.getIndex()#/_update_by_query", + "POST", + mergedOverrides + ) .setBody( reReplace( getUtil().toJSON( { @@ -1230,19 +1400,24 @@ component accessors="true" threadSafe singleton { * Persists multiple items to the index * @documents array An array of elasticsearch Document objects to persist * @throwOnError boolean Whether to throw an exception on error on individual documents which were not persisted + * @params struct A struct of params to pass to the request + * @mode string The mode to use for the bulk operation. Defaults to "update". + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @return array An array of results for the saved items * @interfaced **/ array function saveAll( required array documents, - boolean throwOnError = false, - struct params = {}, - string mode = "update" + boolean throwOnError = false, + struct params = {}, + string mode = "update", + struct requestOverrides = {} ){ var requests = []; - var saveRequest = variables.nodePool.newRequest( "_bulk", "POST" ); + var mergedOverrides = arguments.documents[ 1 ].getRequestOverrides().append( arguments.requestOverrides ); + var saveRequest = variables.nodePool.newRequest( "_bulk", "POST", mergedOverrides ); arguments.params .keyArray() @@ -1365,16 +1540,18 @@ component accessors="true" threadSafe singleton { * @operations array An array of operations to perform * @params struct Parameters to apply on the request * @throwOnError boolean Whether to throw an error if the result was unsuccessful + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html */ any function processBulkOperation( required array operations, - struct params = {}, - boolean throwOnError = true + struct params = {}, + boolean throwOnError = true, + struct requestOverrides = {} ){ var bulkRequest = variables.nodePool - .newRequest( "_bulk", "POST" ) + .newRequest( "_bulk", "POST", arguments.requestOverrides ) .setBody( arguments.operations.reduce( function( acc, action ){ if ( action.keyExists( "operation" ) ) { @@ -1410,10 +1587,19 @@ component accessors="true" threadSafe singleton { * Create or update pipeline * * @pipeline The Pipeline object + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - boolean function applyPipeline( required cbElasticsearch.models.Pipeline pipeline ){ - var response = variables.nodePool - .newRequest( "_ingest/pipeline/#urlEncodedFormat( arguments.pipeline.getId() )#", "PUT" ) + boolean function applyPipeline( + required cbElasticsearch.models.Pipeline pipeline, + struct requestOverrides = {} + ){ + var mergedOverrides = arguments.pipeline.getRequestOverrides().append( arguments.requestOverrides ); + var response = variables.nodePool + .newRequest( + "_ingest/pipeline/#urlEncodedFormat( arguments.pipeline.getId() )#", + "PUT", + mergedOverrides + ) .setBody( arguments.pipeline.getJSON() ) .send(); @@ -1433,10 +1619,15 @@ component accessors="true" threadSafe singleton { * Retreives the definition of a pipeline * * @id The identifier of the pipeline to retreive + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function getPipeline( required string id ){ + any function getPipeline( required string id, struct requestOverrides = {} ){ var definition = variables.nodePool - .newRequest( "_ingest/pipeline/#urlEncodedFormat( arguments.id )#" ) + .newRequest( + "_ingest/pipeline/#urlEncodedFormat( arguments.id )#", + "GET", + arguments.requestOverrides + ) .send() .json(); return definition.keyExists( arguments.id ) ? definition[ arguments.id ] : javacast( "null", 0 ); @@ -1444,10 +1635,15 @@ component accessors="true" threadSafe singleton { /** * Retreives all pipeline definitions + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function getPipelines(){ + any function getPipelines( struct requestOverrides = {} ){ return variables.nodePool - .newRequest( "_ingest/pipeline" ) + .newRequest( + "_ingest/pipeline", + "GET", + arguments.requestOverrides + ) .send() .json(); } @@ -1456,10 +1652,15 @@ component accessors="true" threadSafe singleton { * Deletes a pipeline * * @id The identifier of the pipeline to delete + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - boolean function deletePipeline( required string id ){ + boolean function deletePipeline( required string id, struct requestOverrides = {} ){ var response = variables.nodePool - .newRequest( "_ingest/pipeline/#urlEncodedFormat( arguments.id )#", "DELETE" ) + .newRequest( + "_ingest/pipeline/#urlEncodedFormat( arguments.id )#", + "DELETE", + arguments.requestOverrides + ) .send(); var responseData = response.json(); @@ -1480,18 +1681,28 @@ component accessors="true" threadSafe singleton { * Determines whether a snapshot repository exists * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - function snapshotRepositoryExists( required string name ){ + function snapshotRepositoryExists( required string name, struct requestOverrides = {} ){ return variables.nodePool - .newRequest( "_snapshot/#arguments.name#" ) + .newRequest( + "_snapshot/#arguments.name#", + "GET", + arguments.requestOverrides + ) .send() .getStatusCode() == "200"; } /** * Creates or Updates a Snapshot Repository + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - function applySnapshotRepository( required name, required definition ){ + function applySnapshotRepository( + required name, + required definition, + struct requestOverrides = {} + ){ if ( isSimpleValue( arguments.definition ) ) { arguments.definition = { "type" : "fs", @@ -1500,7 +1711,11 @@ component accessors="true" threadSafe singleton { } var response = variables.nodePool - .newRequest( "_snapshot/#arguments.name#", "PUT" ) + .newRequest( + "_snapshot/#arguments.name#", + "PUT", + arguments.requestOverrides + ) .setBody( getUtil().toJSON( arguments.definition ) ) .asJSON() .send(); @@ -1512,9 +1727,16 @@ component accessors="true" threadSafe singleton { /** * Deletes a Snapshot Repository + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - function deleteSnapshotRepository( required name ){ - var response = variables.nodePool.newRequest( "_snapshot/#arguments.name#", "DELETE" ).send(); + function deleteSnapshotRepository( required name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_snapshot/#arguments.name#", + "DELETE", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1529,10 +1751,15 @@ component accessors="true" threadSafe singleton { * Determines whether an index template exists * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - boolean function indexTemplateExists( required string name ){ + boolean function indexTemplateExists( required string name, struct requestOverrides = {} ){ return variables.nodePool - .newRequest( "_index_template/#arguments.name#" ) + .newRequest( + "_index_template/#arguments.name#", + "GET", + arguments.requestOverrides + ) .send() .getStatusCode() == "200"; } @@ -1542,10 +1769,19 @@ component accessors="true" threadSafe singleton { * * @name string * @definition struct + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function applyIndexTemplate( required string name, required struct definition ){ + any function applyIndexTemplate( + required string name, + required struct definition, + struct requestOverrides = {} + ){ var response = variables.nodePool - .newRequest( "_index_template/#arguments.name#", "PUT" ) + .newRequest( + "_index_template/#arguments.name#", + "PUT", + arguments.requestOverrides + ) .setBody( getUtil().toJSON( arguments.definition ) ) .asJSON() .send(); @@ -1558,9 +1794,16 @@ component accessors="true" threadSafe singleton { /** * Deletes an index template * @name string + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function deleteIndexTemplate( required string name ){ - var response = variables.nodePool.newRequest( "_index_template/#arguments.name#", "DELETE" ).send(); + any function deleteIndexTemplate( required string name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_index_template/#arguments.name#", + "DELETE", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1575,10 +1818,15 @@ component accessors="true" threadSafe singleton { * Determines whether an component template exists * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - boolean function componentTemplateExists( required string name ){ + boolean function componentTemplateExists( required string name, struct requestOverrides = {} ){ return variables.nodePool - .newRequest( "_component_template/#arguments.name#" ) + .newRequest( + "_component_template/#arguments.name#", + "GET", + arguments.requestOverrides + ) .send() .getStatusCode() == "200"; } @@ -1588,10 +1836,19 @@ component accessors="true" threadSafe singleton { * * @name string * @definition struct + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function applyComponentTemplate( required string name, required struct definition ){ + any function applyComponentTemplate( + required string name, + required struct definition, + struct requestOverrides = {} + ){ var response = variables.nodePool - .newRequest( "_component_template/#arguments.name#", "PUT" ) + .newRequest( + "_component_template/#arguments.name#", + "PUT", + arguments.requestOverrides + ) .setBody( getUtil().toJSON( !definition.keyExists( "template" ) @@ -1610,9 +1867,16 @@ component accessors="true" threadSafe singleton { /** * Deletes a component template * @name string + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function deleteComponentTemplate( required string name ){ - var response = variables.nodePool.newRequest( "_component_template/#arguments.name#", "DELETE" ).send(); + any function deleteComponentTemplate( required string name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_component_template/#arguments.name#", + "DELETE", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1627,10 +1891,15 @@ component accessors="true" threadSafe singleton { * Checks whether a named ILM policy exists * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - boolean function ILMPolicyExists( required string name ){ + boolean function ILMPolicyExists( required string name, struct requestOverrides = {} ){ return variables.nodePool - .newRequest( "_ilm/policy/#arguments.name#" ) + .newRequest( + "_ilm/policy/#arguments.name#", + "GET", + arguments.requestOverrides + ) .send() .getStatusCode() == 200; } @@ -1639,9 +1908,16 @@ component accessors="true" threadSafe singleton { * Get an ILM policy by name * * @name string + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function getILMPolicy( required string name ){ - var response = variables.nodePool.newRequest( "_ilm/policy/#arguments.name#" ).send(); + any function getILMPolicy( required string name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_ilm/policy/#arguments.name#", + "GET", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1653,10 +1929,19 @@ component accessors="true" threadSafe singleton { * * @name string * @policy object Either a struct defining the policy or a policy object + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function applyILMPolicy( required string name, required any policy ){ + any function applyILMPolicy( + required string name, + required any policy, + struct requestOverrides = {} + ){ var response = variables.nodePool - .newRequest( "_ilm/policy/#arguments.name#", "PUT" ) + .newRequest( + "_ilm/policy/#arguments.name#", + "PUT", + arguments.requestOverrides + ) .setBody( getUtil().toJSON( { "policy" : arguments.policy } ) ) .asJSON() .send(); @@ -1670,9 +1955,16 @@ component accessors="true" threadSafe singleton { * Deletes an ILM policy * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function deleteILMPolicy( required string name ){ - var response = variables.nodePool.newRequest( "_ilm/policy/#arguments.name#", "DELETE" ).send(); + any function deleteILMPolicy( required string name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_ilm/policy/#arguments.name#", + "DELETE", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1688,9 +1980,16 @@ component accessors="true" threadSafe singleton { * Checks to see whether a data stream exists * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - boolean function dataStreamExists( required string name ){ - var check = variables.nodePool.newRequest( "_data_stream/#arguments.name#" ).send(); + boolean function dataStreamExists( required string name, struct requestOverrides = {} ){ + var check = variables.nodePool + .newRequest( + "_data_stream/#arguments.name#", + "GET", + arguments.requestOverrides + ) + .send(); return check.getStatusCode() == "200" && check.json().data_streams.len(); } @@ -1698,9 +1997,16 @@ component accessors="true" threadSafe singleton { * Ensures the existence of a data stream * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function ensureDataStream( required string name ){ - var response = variables.nodePool.newRequest( "_data_stream/#arguments.name#", "PUT" ).send(); + any function ensureDataStream( required string name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_data_stream/#arguments.name#", + "PUT", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1711,10 +2017,15 @@ component accessors="true" threadSafe singleton { * Migrates an existing index in to a data stream * * @indexName + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function migrateToDataStream( required string indexName ){ + any function migrateToDataStream( required string indexName, struct requestOverrides = {} ){ var response = variables.nodePool - .newRequest( "_data_stream/_migrate/#arguments.indexName#", "POST" ) + .newRequest( + "_data_stream/_migrate/#arguments.indexName#", + "POST", + arguments.requestOverrides + ) .send(); return response.getStatusCode() == 200 @@ -1726,9 +2037,16 @@ component accessors="true" threadSafe singleton { * Gets a datastream definition * * @name + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function getDataStream( required string name ){ - var response = variables.nodePool.newRequest( "_data_stream/#arguments.name#" ).send(); + any function getDataStream( required string name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_data_stream/#arguments.name#", + "GET", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1740,9 +2058,16 @@ component accessors="true" threadSafe singleton { * * @name string the name of the stream * @template string the name of the index template to use for this data stream + * @requestOverrides struct A struct of request overrides to pass to the node pool */ - any function deleteDataStream( required string name ){ - var response = variables.nodePool.newRequest( "_data_stream/#arguments.name#", "DELETE" ).send(); + any function deleteDataStream( required string name, struct requestOverrides = {} ){ + var response = variables.nodePool + .newRequest( + "_data_stream/#arguments.name#", + "DELETE", + arguments.requestOverrides + ) + .send(); return response.getStatusCode() == 200 ? response.json() @@ -1803,6 +2128,7 @@ component accessors="true" threadSafe singleton { * * @indexName string|array Index name or array of index names to query on * @field string|struct If string, field name to query. Otherwise, a struct of query options where only "field" is required. + * @requestOverrides struct A struct of request overrides to pass to the node pool * * @see https://www.elastic.co/guide/en/elasticsearch/reference/8.7/search-terms-enum.html */ @@ -1811,12 +2137,17 @@ component accessors="true" threadSafe singleton { required any field, any match, numeric size = 10, - boolean caseInsensitive = true + boolean caseInsensitive = true, + struct requestOverrides = {} ){ if ( isArray( arguments.indexName ) ) { arguments.indexName = arrayToList( arguments.indexName ); } - var termsRequest = variables.nodePool.newRequest( "/#arguments.indexName#/_terms_enum", "post" ); + var termsRequest = variables.nodePool.newRequest( + "/#arguments.indexName#/_terms_enum", + "post", + arguments.requestOverrides + ); var opts = { "size" : arguments.size, diff --git a/models/io/HyperPool.cfc b/models/io/HyperPool.cfc index 8d5e3a6..2de0e7a 100644 --- a/models/io/HyperPool.cfc +++ b/models/io/HyperPool.cfc @@ -79,8 +79,13 @@ component { * * @route the relative URI to the route * @method the HTTP Request method + * @overrides any default values to set on the request ( timeout, referrer, bodyFormat, etc. ) */ - public Hyper.models.HyperRequest function newRequest( required string route, string method = "GET" ){ + public Hyper.models.HyperRequest function newRequest( + required string route, + string method = "GET", + struct overrides = {} + ){ var requestObj = hyper.new(); var nodeUsage = "write"; @@ -100,8 +105,8 @@ component { requestObj .setMethod( arguments.method ) - .setThrowOnError( false ) - .setTimeout( variables.instanceConfig.get( "readTimeout" ) ); + .setTimeout( variables.instanceConfig.get( "readTimeout" ) ) + .setThrowOnError( false ); var uriParts = listToArray( route, "/" ); uriParts.prepend( node.url ); @@ -133,6 +138,20 @@ component { message = "The authentication type #variables.authenticationScheme# is not currently supported" ); } + // allow overriding of any request key via the overrides struct argument + var withMethods = [ "headers" ]; + for ( var key in arguments.overrides ) { + var methodName = withMethods.contains( key ) ? "with#key#" : "set#key#"; + if ( structKeyExists( requestObj, methodName ) ) { + invoke( + requestObj, + methodName, + isArray( arguments.overrides[ key ] ) ? arguments.overrides[ key ] : [ + arguments.overrides[ key ] + ] + ); + } + } return requestObj; } diff --git a/test-harness/tests/specs/unit/HyperClientTest.cfc b/test-harness/tests/specs/unit/HyperClientTest.cfc index 2d86894..f8197ca 100644 --- a/test-harness/tests/specs/unit/HyperClientTest.cfc +++ b/test-harness/tests/specs/unit/HyperClientTest.cfc @@ -12,6 +12,8 @@ component extends="coldbox.system.testing.BaseTestCase" { variables.testIndexName = lCase( "ElasticsearchClientTests" ); variables.model.deleteIndex( variables.testIndexName ); + + addMatchers( "hyper.models.TestBoxMatchers" ); } function afterAll(){ @@ -1043,7 +1045,6 @@ component extends="coldbox.system.testing.BaseTestCase" { testDocument._id, "title" ); -debug( result ); expect( result.keyExists( "error" ) ).toBeFalse(); expect( result.keyExists( "term_vectors" ) ).toBeTrue(); expect( result.term_vectors ).toHaveKey( "title" ); @@ -1764,6 +1765,97 @@ debug( result ); .toHaveKey( "_shards" ); }); }); + + describe( "connection overrides", function() { + beforeEach( function() { + getWirebox().getInstance( "HyperBuilder@hyper" ).fake({ + "*": function( newFakeResponse, req ) { + return newFakeResponse() + .setStatusCode( 200 ) + .setData( serializeJSON( req.getMemento() ) ); + } + }); + if( !variables.keyExists( "hyper" ) ){ + variables.hyper = getWirebox().getInstance( "HyperBuilder@hyper" ); + } + addMatchers( "hyper.models.TestBoxMatchers" ); + } ); + afterEach( function() { + getWirebox().getInstance( "HyperBuilder@hyper" ).clearFakes(); + } ); + it( "indexExists() - can set custom connection timeout", function(){ + + var response = variables.model.indexExists( "bar", { timeout = 300 } ); + + expect( variables.hyper ) + .toHaveSentRequest( ( req ) => { + return req.getMethod() === "HEAD" && + req.getUrl() == "http://127.0.0.1:9200/bar" && + req.getTimeout() === 300; + } ); + } ); + + it( "Can set request overrides from IndexBuilder", function(){ + + var response = getWirebox() + .getInstance( "IndexBuilder@cbelasticsearch" ) + .withTimeout( 45 ) + .new( name = "foo", settings = { "refresh_interval" : "1s" } ) + .save(); + + expect( variables.hyper ) + .toHaveSentRequest( ( req ) => { + return req.getMethod() === "PUT" && + req.getUrl() == "http://127.0.0.1:9200/foo/_settings" && + req.getTimeout() === 45; + } ); + } ); + + it( "BaseModel will throw on missing method", function(){ + expect( () => { + var response = getWirebox() + .getInstance( "IndexBuilder@cbelasticsearch" ) + .Timeout( 45 ) + .new( name = "foo", settings = { "refresh_interval" : "1s" } ) + .save(); + + expect( variables.hyper ) + .toHaveSentRequest( ( req ) => { + return req.getMethod() === "PUT" && + req.getUrl() == "http://127.0.0.1:9200/foo/_settings" && + req.getTimeout() === 45; + } ); + } ).toThrow( "MissingMethodException" ); + } ); + + it( "Can set custom username/password", function(){ + + var response = variables.model.indexExists( "bar", { username: "admin", password: "admin123$" } ); + + expect( variables.hyper ) + .toHaveSentRequest( ( req ) => { + return req.getMethod() === "HEAD" && + req.getUrl() == "http://127.0.0.1:9200/bar" && + req.getUsername() === "admin" && + req.getPassword() === "admin123$"; + } ); + } ); + + it( "Can set custom request headers", function(){ + var response = getWirebox() + .getInstance( "IndexBuilder@cbelasticsearch" ) + .withHeader( "X-Custom-Header", "MyValue" ) + .new( name = "foo", settings = { "refresh_interval" : "1s" } ) + .save(); + + expect( variables.hyper ) + .toHaveSentRequest( ( req ) => { + return req.getMethod() == "PUT" && + req.getUrl() == "http://127.0.0.1:9200/foo/_settings" && + req.getHeader( "X-Custom-Header" ) == "MyValue"; + } ); + } ); + }) } ); }