views:

655

answers:

3

Hello,

developing 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.

A: 

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.

rip747
Well, for me problem seems to be on client (service client) side. See, such request wont even be sent to service, it fails on client CF instance while it is trying to build request.
Sergii
+2  A: 

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>
Tomalak
Yes this is the way to make it. Only problem here is creating WSargs pattern, but when you did this -- the rest will be easy. Thank you!
Sergii
You are welcome. ;-) I'm not sure if there is an easy way to auto-generate the WSargs struct. At least not when you want to have regexes for basic validity checking. But you can always resign to evaluating the WSDL - it's just XML in the end. This way you can at least get number + names of the args.
Tomalak
To avoid regexed it'd be cool to have type comparisons in CF. Say, some kind of typeof in JavaScript.
Sergii
Well... you can, in a way. There are IsBoolean() and IsNumeric() etc. But you can use regex to look at the actual value of a variable, constraining it to more than just "numeric". Don't know how far you want to take the whole "validation" business. ;-)
Tomalak
A: 

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.

Sergii