Sergey Galashyn
Sergey Galashyn

Reputation: 6956

Get readable WSDL service method arguments error in ColdFusion

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

Answers (3)

Sergey Galashyn
Sergey Galashyn

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

Tomalak
Tomalak

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

rip747
rip747

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

Related Questions