Hello,

I'm trying to connect to SOAP api of tradera.com using feeds (but I think the problem that I have is with how I conigure web service client for soap authenthication

http://api.tradera.com/v3/PublicService.asmx?WSDL

In feeds I choose the: parameters (GetSellerItems)

However whenever I try to import the data I get an error:

"Error invoking the SOAP service tradera active 2, operation GetSellerItems: Invalid request ---> Value cannot be null. Parameter name: No application Id provided"

So my question is where in the Web Service client can I provide the Application ID so it would be passed to feeds ?

I tried to add it to: Input for global service parameters but maybe I used wrong parametrs it didnt worked..
I also tried putting this data in parametr:

AuthenticationHeader (Machine name: AuthenticationHeader) but when I put it in there, then I cannot access the "GetSellerItems"

If anyone could provide me some suggestions what I'm doing wrong it would be helpful :( I'm stuck with it for 2 weeks now :/

- UPDATE -

I have tried different options (I'm attaching screenshots of my setup) but unfortunatly it still doesnt work as on error in the screenshot below, how else can I add my authenthication details to header? I must be doing something wrong I assume but not sure what

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Narhir’s picture

dman’s picture

I did try to replicate, and did get that error.
I haven't been able to guess what an application Id is in this context though. It's possibly something that was introduced, or supported in SOAP 1.2
Do you have any idea what the expected *value* of an application Id should be here, or what a successful packet would look like?

Hm, Well this is nice - there is actually properly written API docs at http://api.tradera.com/v3/PublicService.asmx?op=GetSellerItems
This is something to go on...

But the namespace here implies that the concept of AuthenticationHeader and AppId are custom-made by tradera. Not an official SOAP thing.
Hm.

  <soap:Header>
    <AuthenticationHeader xmlns="http://api.tradera.com">
      <AppId>int</AppId>
      <AppKey>string</AppKey>
    </AuthenticationHeader>
Narhir’s picture

Hi,

Thank you so much for your reply

The application id is this thing: Application Id: 1502

That is provided when you create new application in their "developer center" it also provides:

Service key: (around 30 characters or so)
Public key: (around 30 characters or so)

On the link that they provide: https://api.tradera.com/v3/PublicService.asmx?op=GetSellerItems (as you provided above) is what I'm trying to do unfortunatly with no luck

And overall call should look like in the example

POST /v3/PublicService.asmx HTTP/1.1
Host: api.tradera.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://api.tradera.com/GetSellerItems"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <AuthenticationHeader xmlns="http://api.tradera.com">
      <AppId>int</AppId>
      <AppKey>string</AppKey>
    </AuthenticationHeader>
    <ConfigurationHeader xmlns="http://api.tradera.com">
      <Sandbox>int</Sandbox>
      <MaxResultAge>int</MaxResultAge>
    </ConfigurationHeader>
  </soap:Header>
  <soap:Body>
    <GetSellerItems xmlns="http://api.tradera.com">
      <userId>int</userId>
      <categoryId>int</categoryId>
      <filterActive>All or Active or Inactive</filterActive>
      <minEndDate>dateTime</minEndDate>
      <maxEndDate>dateTime</maxEndDate>
      <filterItemType>All or Auction or PureBuyItNow or ShopItem</filterItemType>
    </GetSellerItems>
  </soap:Body>
</soap:Envelope>

I was trying to find somewhere in code of a module a place where I could somehow even place my authenthication header layout

Narhir’s picture

I have looked futher into the API documentation and apparently there is a way to authenthicate my self by passing my Appid and Appkey in URL:

Authentication and authorization 

To access any of the methods in the API, you need to identify yourself through an application id and a corresponding application service key. By registering an application in the developer center, you will receive an application id and a service key. The id and key then need to be passed to the server in one of two ways:

As part of the URL: using appId and appKey URL parameters.
As part of the SOAP AuthenticationHeader element: using the AppId and AppKey elements.

http://api.tradera.com/v3/Default.aspx?NodeId=9763

So I tried adding that kind of url:

http://api.tradera.com/v3/publicservice.asmx?appId=1502&appKey=965d0336-3fa3-4c11-ac9b-689f680d6482&WSDL

It imported definitions correctly HOWEVER it still gave same error on importing attempt:

Error invoking the SOAP service tradera active 4, operation GetSellerItems: Invalid request ---> Value cannot be null. Parameter name: No application Id provided

Maybe I'm doing something wrong but if there is no easy way to pass the details in SOAP header authenthicate maybe I should change some "dynamic" url value in the module files to fix it ? tried some options so far.. but not much luck :( would appriciate any suggestions

-- UPDATE -- 26/06/2015

Okay.. I looked much further into it, unfortunatly I still was unable to pastt the AppId and AppKey trough URL so I moved back to the creating header and its authenthication, first I enabled the patch that allowed for UI to have the place to type the Username and Password, and then I change it a bit to get closer output with "Tester"

Here is my current php code:

class WSSESecurityHeader extends SoapHeader {
  // Thanks to http://stackoverflow.com/a/20498574/213577
  /**
   * Create the header block.
   *
   * @param string $username
   *   Username.
   * @param string $password
   *   Password.
   */
  public function __construct($username, $password) {
    $wsse_ns = 'http://www.w3.org/2001/XMLSchema.xsd';
    $security = new SoapVar(
      array(new SoapVar(
        array(
          new SoapVar($username, XSD_STRING, NULL, NULL, 'AppId'),
          new SoapVar($password, XSD_STRING, NULL, NULL, 'AppKey'),
        ),
        SOAP_ENC_OBJECT,
        NULL,
        NULL,
        'AuthenticationHeader'
        )),
      SOAP_ENC_OBJECT

    );
    parent::__construct($wsse_ns, 'Header', $security, FALSE);
  }

}

It currently generate that kind of "REQUEST"

POST /v3/PublicService.asmx HTTP/1.1
Host: api.tradera.com
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.6.8
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://api.tradera.com/GetOfficalTime"
Content-Length: 451
Authorization: Basic MTQ5OTo3ZjI5MTJmOC1lZjM2LTRkZTAtYWRmNS1kYjg4NWMxZTlkZjI=

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://api.tradera.com" xmlns:ns2="http://www.w3.org/2001/XMLSchema.xsd">
  <SOAP-ENV:Header>
    <ns2:Header>
      <AuthenticationHeader>
        <AppId>1502</AppId>
        <AppKey>965d0336-3fa3-4c11-ac9b-689f680d6482</AppKey>
      </AuthenticationHeader>
    </ns2:Header>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <ns1:GetOfficalTime/>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The correct version of how tradera requirs should look like this:

POST /v3/publicservice.asmx HTTP/1.1
Host: api.tradera.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://api.tradera.com/GetOfficalTime"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <AuthenticationHeader xmlns="http://api.tradera.com">
      <AppId>int</AppId>
      <AppKey>string</AppKey>
    </AuthenticationHeader>
    <ConfigurationHeader xmlns="http://api.tradera.com">
      <Sandbox>int</Sandbox>
      <MaxResultAge>int</MaxResultAge>
    </ConfigurationHeader>
  </soap:Header>
  <soap:Body>
    <GetOfficalTime xmlns="http://api.tradera.com" />
  </soap:Body>
</soap:Envelope>

Currently I'm trying to figure out how to change the into and how to change all those SOAP-ENV into just "soap" if any of you have some suggestions I would really appriciate that I tried implementing code below.. but It didnt worked (I must have done something wrong)


class XSoapClient extends WSClientEndpoint {
    public function __doRequest($request, $location, $action, $version, $one_way = null)
    {
        $request = preg_replace('/ns2:Header/','Header',$request, -1);  
        return parent::__doRequest($request, $location, $action, $version, $one_way);
    }
}
Narhir’s picture

Hello,

So.. after quite some time I finally managed to solve the problems thanks to a help from IRC :)

I'm copy pasting working code, maybe someone will need it in future, it is modified version of patched wsclient_soap.module

<?php

/**
 * @file
 * Web service client SOAP support.
 */

/**
 * Implements hook_wsclient_endpoint_types().
 */
function wsclient_soap_wsclient_endpoint_types() {
  return array(
    'soap' => array(
      'label' => t('SOAP'),
      'class' => 'WSClientSOAPEndpoint',
    ),
  );
}





/**
 * A remote endpoint type for invoking SOAP services.
 */



class WSClientSOAPEndpoint extends WSClientEndpoint {

  public function client() {
    if (!isset($this->client)) {
      $options['exceptions'] = TRUE;
      // Handle Basic HTTP authentication.

        $this->service->settings['options']['AppId'] = 'My app id';
        $this->service->settings['options']['AppKey'] = 'My app Key';


        $options += $this->service->settings['options'];

      try {
        $this->client = new SOAPClient($this->url, $options);
      }
      catch (SoapFault $e) {
        throw new WSClientException('Error initializing SOAP client for service %name', array('%name' => $this->service->name));
      }

      // Handle WSS style secured webservice.
      // https://www.drupal.org/node/2420779
      if (!empty($this->service->settings['authentication']['wss'])) {
        $this->client->__setSoapHeaders(new WSSESecurityHeader(
          $this->service->settings['authentication']['wss']['username'],
          $this->service->settings['authentication']['wss']['password']
        ));
      }

    }

    return $this->client;
  }

  /**
   * Retrieve metadata from the WSDL about available data types and operations.
   *
   * @param boolean $reset
   *   If TRUE, existing data types and operations will be overwritten.
   */
  public function initializeMetadata($reset = TRUE) {
    $client = $this->client();

    $data_types = wsclient_soap_parse_types($client->__getTypes());
    $operations = wsclient_soap_parse_operations($client->__getFunctions());
    if ($reset) {
      $this->service->datatypes = $data_types;
      $this->service->operations = $operations;
    }
    else {
      $this->service->datatypes += $data_types;
      $this->service->operations += $operations;
    }
    $this->service->clearCache();
  }





  /**
   * Calls the SOAP service.
   *
   * @param string $operation
   *   The name of the operation to execute.
   * @param array $arguments
   *   Arguments to pass to the service with this operation.
   */
  public function call($operation, $arguments) {
    $client = $this->client();
    



    try {
      $response = $client->__soapCall($operation, $arguments);

      return $response;
    }
    catch (SoapFault $e) {
      throw new WSClientException('Error invoking the SOAP service %name, operation %operation: %error', array('%name' => $this->service->label, '%operation' => $operation, '%error' => $e->getMessage()));
    }
  }




}



/**
 * Class WSSESecurityHeader
 *
 * A 'Security' Soap header block to support
 * Web Services Security UsernameToken Profile
 * http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf
 */
class WSSESecurityHeader extends SoapHeader {
  // Thanks to http://stackoverflow.com/a/20498574/213577
  /**
   * Create the header block.
   *
   * @param string $username
   *   Username.
   * @param string $password
   *   Password.
   */
public function __construct($username, $password) {

  $client = new SoapClient('http://api.tradera.com/v3/PublicService.asmx?WSDL',array());

  $auth = array(
        'AppId'=>'My app id',
        'AppKey'=>'My app key',
        'SystemId'=> array('_'=>'DATA','Param'=>'PARAM'),
        );



  parent::SoapHeader('http://api.tradera.com','AuthenticationHeader',$auth,false);
}


}



/**
 * Implements hook_form_FORM_ID_alter().
 */
function wsclient_soap_form_wsclient_service_form_alter(&$form, &$form_state) {
  $form['#submit'][] = 'wsclient_soap_wsclient_service_form_submit';
  $form['#validate'][] = 'wsclient_soap_wsclient_service_form_validate';
}

/**
 * Validation callback to check if the SOAP service URL points to a valid WSDL
 * file.
 */
function wsclient_soap_wsclient_service_form_validate($form, $form_state) {
  $service = $form_state['wsclient_service'];
  if ($form_state['values']['type'] == 'soap') {
    // The url has to point to a valid WSDL file.
    try {
      // If initializing the SOAPClient succeeds we're good, otherwise we catch
      // the exception below and suppress any further warnings.
      // WARNING: if you have the xdebug PHP module enabled this can cause a
      // fatal error on invalid WSDL files (instead of a catchable SoapFault
      // exception).
      // xdebug_disable();
      @$endpoint = new SOAPClient($form_state['values']['url']);
    }
    catch (SoapFault $e) {
      form_set_error('url', t('Error parsing the WSDL file: %message', array('%message' => $e->getMessage())));
    }
  }
}

/**
 * Submit callback for the web service form to populate operations and data
 * types of the new SOAP service.
 */
function wsclient_soap_wsclient_service_form_submit($form, &$form_state) {
  if ($form_state['values']['type'] == 'soap' && $form_state['op'] == 'add') {
    $service = $form_state['wsclient_service'];
    $endpoint = $service->endpoint();
    $endpoint->initializeMetadata();
    $service->save();
    rules_clear_cache();
    $service->clearCache();
    drupal_set_message(t('Operations and data types of the SOAP service have been imported automatically. If the service expects data types with properties as lists (multiple values for the property), please check the multiple flag on those properties. This cannot be auto-detected at the moment.'));
  }
}

/**
 * Convert metadata about data types provided by a SOAPClient into a wsclient
 * compatible data type array.
 *
 * @param array $types
 *   The array containing the struct strings.
 * @return
 *   A data type array with property information.
 */
function wsclient_soap_parse_types(array $types) {
  $wsclient_types = array();
  foreach ($types as $type_string) {
    if (strpos($type_string, 'struct') === 0) {
      $parts = explode('{', $type_string);
      // Cut off struct and whitespaces from type name.
      $type_name = trim(substr($parts[0], 6));
      $wsclient_types[$type_name] = array('label' => $type_name);
      $property_string = $parts[1];
      // Cut off trailing '}'
      $property_string = substr($property_string, 0, -1);
      $properties = explode(';', $property_string);
      // Remove last empty element
      array_pop($properties);
      // Initialize empty property information.
      $wsclient_types[$type_name]['property info'] = array();
      foreach ($properties as $property_string) {
        // Cut off white spaces.
        $property_string = trim($property_string);
        $parts = explode(' ', $property_string);
        $property_type = $parts[0];
        $property_name = $parts[1];
        $wsclient_types[$type_name]['property info'][$property_name] = array(
          'type' => wsclient_soap_type_mapper($property_type),
        );
      }
    }
  }
  return $wsclient_types;
}

/**
 * Convert metadata about operations provided by a SOAPClient into a wsclient
 * compatible operations array.
 *
 * @param array $operations
 *   The array containing the operation signature strings.
 * @return
 *   An operations array with parameter information.
 */
function wsclient_soap_parse_operations(array $operations) {
  $wsclient_operations = array();
  foreach ($operations as $operation) {
    $parts = explode(' ', $operation);
    $return_type = wsclient_soap_type_mapper($parts[0]);
    $name_parts = explode('(', $parts[1]);
    $op_name = $name_parts[0];
    $wsclient_operations[$op_name] = array(
      'label' => $op_name,
      'result' => array('type' => $return_type, 'label' => $return_type),
    );
    $parts = explode('(', $operation);
    // Cut off trailing ')'.
    $param_string = substr($parts[1], 0, -1);
    if ($param_string) {
      $parameters = explode(',', $param_string);
      foreach ($parameters as $parameter) {
        $parameter = trim($parameter);
        $parts = explode(' ', $parameter);
        $param_type = $parts[0];
        // Remove leading '$' from parameter name.
        $param_name = substr($parts[1], 1);
        $wsclient_operations[$op_name]['parameter'][$param_name] = array(
          'type' => wsclient_soap_type_mapper($param_type),
        );
      }
    }
  }
  return $wsclient_operations;
}

/**
 * Maps data type names from SOAPClient to wsclient/rules internals.
 */
function wsclient_soap_type_mapper($type) {
  $primitive_types = array('string', 'int', 'long', 'float', 'boolean', 'double', 'short', 'decimal');
  if (in_array($type, $primitive_types)) {
    switch ($type) {
      case 'double':
      case 'float':
        return 'decimal';
      case 'int':
      case 'long':
      case 'short':
        return 'integer';
      case 'string':
        return 'text';
    }
  }
  // Check for list types.
  if (strpos($type, 'ArrayOf') === 0) {
    $type = substr($type, 7);
    $primitive = strtolower($type);
    if (in_array($primitive, $primitive_types)) {
      return 'list<' . $primitive . '>';
    }
    return 'list<' . $type . '>';
  }
  // Otherwise return the type as is.
  return $type;
}
Narhir’s picture

Assigned: Unassigned » Narhir
Status: Active » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.