Reputation: 6956
I'm developing a service invoking script that looks like:
<cfinvoke webservice="#ServiceURL#" method="AddCustomer" returnvariable="ResponseData" argumentcollection="#stAguments#">
<cfinvokeargument name="api_key" value="#ServiceKey#" />
</cfinvoke>
stAguments structure filled before this call, obviously. Imagine you've forgot to add one of arguments into this container or used wrong argument type, say created invalid request. ColdFusion throws exception that can be catched, but can not (not literally) be read:
*Web service operation AddCustomer with parameters {postcode={12345},org_name={Terms test 7.79661762856},fax={},html={1},addr1={address1},firstname={sergey},city={Austin},country={},taxable={},notify={1},lastname={galashyn},addr2={},ssn={},api_key={8FE9AD0BCF2382D92A1080DB3AA62DB9},taxrate={0},terms={Net 15},active={},state={},salutation={Mr.},password={123},account_manager={1}} cannot be found.*
It breaks my head checking all these arguments manually one by one. This is the problem.
Maybe anyone uses some technique to make this easier.
I've even thought about some kind of parser to automate this comparison.
Will appreciate any thought and ideas.
Thank you.
P.S. Sorry for my English -- not my native language. Please ask if I've wrote anything not clear enough.
EDIT:
To clarify. Problem is not in accessing service. I am owner of it and I am definitely know all arguments and their types of each method.
Problem is only in reading error message when creating request -- filling method arguments container.
For example, method got 10 arguments and accidentally I've added 9 -- local instance of CF throws error that method can't be found and shows raw list (em'ed above) of fields I've passed. And I need to compare them one by one with method arguments to find what I've missed.
Really, it's an usability and time saving problem.
Upvotes: 1
Views: 1427
Reputation: 6956
Finally I've finished with writing additional code to help me testing my service. Also, I needed to provide my API users with examples in CFML and combined these tasks.
Would like to share code samples, maybe someone else will find them useful.
I've used validating structures method (but a bit simplified it by removing regex'es because of they are overhead in my case -- service validates incoming arguments and reports errors). Main idea was to pull WSDL and build structure-template to compare with testing data. Also, added simple script with interface to test different methods, it helps me to test changes in service faster.
So, here is UDFs for fetching WSDL and comparing structures (my XPath-fu can be not a perfect :) and here is base testing code.
Hope this way of publishing code is not a rules violation. Thought it's not a good idea to post it all right here.
Special thanks to Tomalak for his ideas.
Upvotes: 0
Reputation: 338108
Really, it's an usability and time saving problem.
Okay, so if I understand you correctly, you need a "debugging" way to compare what you have to what you need.
Let's assume you have a "this is how it should be"-struct:
<cfset WSargs = StructNew()>
<cfset WSargs.AddCustomer = StructNew()>
<cfset WSargs.AddCustomer.postcode = "\d{5}">
<cfset WSargs.AddCustomer.org_name = ".+">
<cfset WSargs.AddCustomer.fax = ".*">
<cfset WSargs.AddCustomer.html = "^[01]$">
<cfset WSargs.AddCustomer.addr1 = ".*">
<cfset WSargs.AddCustomer.firstname = ".*">
<cfset WSargs.AddCustomer.city = ".*">
<cfset WSargs.AddCustomer.country = ".*">
<cfset WSargs.AddCustomer.taxable = ".*">
<cfset WSargs.AddCustomer.notify = "^[01]$">
<cfset WSargs.AddCustomer.lastname = ".*">
<cfset WSargs.AddCustomer.addr2 = ".*">
<cfset WSargs.AddCustomer.ssn = ".*">
<cfset WSargs.AddCustomer.taxrate = "^\d*$">
<cfset WSargs.AddCustomer.terms = ".*">
<cfset WSargs.AddCustomer.active = ".*">
<cfset WSargs.AddCustomer.state = ".*">
<cfset WSargs.AddCustomer.salutation= ".*">
<cfset WSargs.AddCustomer.password = ".+">
<cfset WSargs.AddCustomer.account_manager = "^[01]$">
And you want a function that compares the argumentcollection
to this.
<cffunction name="CompareStructs" returntype="array" output="no">
<cfargument name="template" type="struct" required="yes">
<cfargument name="data" type="struct" required="yes">
<cfset var errors = ArrayNew(1)>
<cfset var key = "">
<cfloop collection="#template#" item="key">
<cfif StructKeyExists(data, key)>
<cfif REFind(template[key], ToString(data[key])) eq 0>
<cfset ArrayAppend(errors, "Field '#key#' has an invalid value.")>
</cfif>
<cfelse>
<cfset ArrayAppend(errors, "Field '#key#' is missing.")>
</cfif>
</cfloop>
<cfloop collection="#data#" item="key">
<cfif not StructKeyExists(template, key)>
<cfset ArrayAppend(errors, "Field '#key#' is not allowed.")>
</cfif>
</cfloop>
<cfreturn errors>
</cffunction>
Called like this:
<cfset errors = CompareStructs(WSargs.AddCustomer, stAguments)>
<cfif ArrayLen(errors) eq 0>
<cfinvoke
webservice="#ServiceURL#"
method="AddCustomer"
returnvariable="ResponseData"
argumentcollection="#stAguments#"
>
<cfinvokeargument name="api_key" value="#ServiceKey#" />
</cfinvoke>
<cfelse>
<cfdump var="#errors#" label="Errors calling AddCustomer()">
<cfabort>
<!--- or whatever --->
</cfif>
Upvotes: 2
Reputation: 9455
your headache is stemming from just accepting the values that are passed into your method without validating that the input. this is easily fixed by adding validation and overloading into your method.
Upvotes: 0