diff --git a/README.md b/README.md
index 0f8e9fb..492ddfd 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,9 @@ A Laravel SOAP client that provides a clean interface for handling requests and
* [Call](#call)
* [Parameters](#parameters)
* [Nodes](#nodes)
+- [Tracing](#Tracing)
- [Hooks](#hooks)
-- [Faking](#faking)
+- [Faking](#faking)
- [Configuration](#configuration)
* [Include](#include)
@@ -147,6 +148,24 @@ Now, just by adding or removing a body to the `soap_node()` the outputted array
A node can be made with either the Facade `Soap::node()` or the helper method `soap_node()`.
+## Tracing
+Soap allows you to easily trace your interactions with the SOAP endpoint being accessed.
+
+To trace all requests, set the following in the register method of your `ServiceProvider`:
+
+```php
+Soap::trace()
+```
+Now, all `Response` objects returned will have a `Trace` object attached, accessible via `$response->getTrace()`. This has two properties `xmlRequest` and `xmlResponse` - storing the raw XML values for each.
+
+Tracing can also be declared locally:
+```php
+Soap::to('...')->trace()->call('...')
+```
+Now, just this `Response` will have a valid `Trace`.
+
+Tracing is null safe. If `$response->getTrace()` is called when a `Trace` hasn't been set, a new `Trace` is returned. This `Trace`'s properties will all return `null`.
+
## Hooks
Hooks allow you to perform actions before and after Soap makes a request.
@@ -225,20 +244,20 @@ you can use this method, passing in the desired count as a parameter.
#### `Soap::assertSent(callable $callback)`
-You can dive a little deeper and test that a particular request was
+You can dive a little deeper and test that a particular request was
actually sent, and that it returned the expected response. You should
pass a closure into this method, which receives the `$request` and `$response` as parameters, and return `true` if they match your
expectations.
#### `Soap::assertNotSent(callable $callback)`
-This is the opposite of `Soap::assertSent`. You can make sure that a
+This is the opposite of `Soap::assertSent`. You can make sure that a
particular request wasn't made. Again, returning `true` from the
closure will cause it to pass.
#### `Soap::assertNothingSent()`
-If you just want to make sure that absolutely nothing was sent out, you
+If you just want to make sure that absolutely nothing was sent out, you
can call this. It does what it says on the tin.
## Configuration
@@ -257,7 +276,7 @@ You can even use dot syntax on your array keys to permeate deeper into the reque
```php
Soap::include(['login.credentials' => soap_node(['user' => '...', 'password' => '...'])])->for('...');
-```
+```
### Changelog
diff --git a/src/Request/Request.php b/src/Request/Request.php
index c5a3edc..3ae6569 100644
--- a/src/Request/Request.php
+++ b/src/Request/Request.php
@@ -33,4 +33,6 @@ public function getMethod();
public function getBody();
public function set($key, $value): self;
+
+ public function trace($shouldTrace = true): self;
}
diff --git a/src/Request/SoapClientRequest.php b/src/Request/SoapClientRequest.php
index 45ff136..8ca6605 100644
--- a/src/Request/SoapClientRequest.php
+++ b/src/Request/SoapClientRequest.php
@@ -4,17 +4,18 @@
use RicorocksDigitalAgency\Soap\Parameters\Builder;
use RicorocksDigitalAgency\Soap\Response\Response;
-use SoapClient;
+use RicorocksDigitalAgency\Soap\Support\Tracing\Trace;
class SoapClientRequest implements Request
{
protected string $endpoint;
protected string $method;
protected $body = [];
- protected SoapClient $client;
+ protected $client;
protected Builder $builder;
protected Response $response;
protected $hooks = [];
+ protected $shouldTrace = false;
public function __construct(Builder $builder)
{
@@ -48,8 +49,23 @@ public function call($method, $parameters = [])
protected function getResponse()
{
- return $this->response ??= Response::new($this->makeRequest())
- ->withXml($this->client()->__getLastRequest(), $this->client()->__getLastResponse());
+ return $this->response ??= $this->getRealResponse();
+ }
+
+ protected function getRealResponse()
+ {
+ return tap(
+ Response::new($this->makeRequest()),
+ fn($response) => $this->shouldTrace ? $this->addTrace($response) : $response
+ );
+ }
+
+ protected function addTrace($response)
+ {
+ return $response->setTrace(
+ Trace::thisXmlRequest($this->client()->__getLastRequest())
+ ->thisXmlResponse($this->client()->__getLastResponse())
+ );
}
protected function makeRequest()
@@ -59,7 +75,7 @@ protected function makeRequest()
protected function client()
{
- return $this->client ??= new SoapClient($this->endpoint, ['trace' => true]);
+ return $this->client ??= app(\SoapClient::class, ['wsdl' => $this->endpoint, 'options' => ['trace' => $this->shouldTrace]]);
}
public function getMethod()
@@ -109,4 +125,10 @@ public function set($key, $value): Request
data_set($this->body, $key, $value);
return $this;
}
+
+ public function trace($shouldTrace = true): Request
+ {
+ $this->shouldTrace = $shouldTrace;
+ return $this;
+ }
}
diff --git a/src/Response/Response.php b/src/Response/Response.php
index b857b2b..cbd6abf 100644
--- a/src/Response/Response.php
+++ b/src/Response/Response.php
@@ -2,11 +2,12 @@
namespace RicorocksDigitalAgency\Soap\Response;
+use RicorocksDigitalAgency\Soap\Support\Tracing\Trace;
+
class Response
{
public $response;
- public $xmlRequest = null;
- public $xmlResponse = null;
+ protected Trace $trace;
public static function new($response = []): self
{
@@ -18,13 +19,17 @@ public function __get($name)
return data_get($this->response, $name);
}
- public function withXml($xmlRequest, $xmlResponse)
+ public function setTrace(Trace $trace)
{
- $this->xmlRequest = $xmlRequest;
- $this->xmlResponse = $xmlResponse;
+ $this->trace = $trace;
return $this;
}
+ public function trace()
+ {
+ return $this->trace ??= app(Trace::class);
+ }
+
public function set($key, $value): self
{
data_set($this->response, $key, $value);
diff --git a/src/Soap.php b/src/Soap.php
index c6cf45c..82cc452 100644
--- a/src/Soap.php
+++ b/src/Soap.php
@@ -69,6 +69,12 @@ public function afterRequesting(callable $hook)
return $this;
}
+ public function trace($shouldTrace = true)
+ {
+ $this->beforeRequesting(fn($request) => $request->trace($shouldTrace));
+ return $this;
+ }
+
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
diff --git a/src/Support/Tracing/Trace.php b/src/Support/Tracing/Trace.php
new file mode 100644
index 0000000..11907ba
--- /dev/null
+++ b/src/Support/Tracing/Trace.php
@@ -0,0 +1,19 @@
+ $instance->xmlRequest = $xml);
+ }
+
+ public function thisXmlResponse($xml): self
+ {
+ return tap($this, fn($self) => $self->xmlResponse = $xml);
+ }
+}
diff --git a/tests/Mocks/MockSoapClient.php b/tests/Mocks/MockSoapClient.php
new file mode 100644
index 0000000..6b7c850
--- /dev/null
+++ b/tests/Mocks/MockSoapClient.php
@@ -0,0 +1,94 @@
+shouldTrace = true;
+ }
+ }
+
+ public function __call(string $function_name, array $arguments)
+ {
+ }
+
+ public function __doRequest(string $request, string $location, string $action, int $version, $one_way = 0)
+ {
+ }
+
+ public function __getCookies()
+ {
+ }
+
+ public function __getFunctions()
+ {
+ return [
+ "The mock client does not actually have functions!"
+ ];
+ }
+
+ public function __getLastRequest()
+ {
+ if (!$this->shouldTrace) {
+ return null;
+ }
+
+ return 'World';
+ }
+
+ public function __getLastRequestHeaders()
+ {
+ }
+
+ public function __getLastResponse()
+ {
+ if (!$this->shouldTrace) {
+ return null;
+ }
+
+ return 'Success!';
+ }
+
+ public function __getLastResponseHeaders()
+ {
+ }
+
+ public function __getTypes()
+ {
+ }
+
+ public function __setCookie(string $name, string $value = null)
+ {
+ }
+
+ public function __setLocation(string $new_location = null)
+ {
+ }
+
+ public function __setSoapHeaders($soapheaders)
+ {
+ }
+
+ public function __soapCall(
+ string $function_name,
+ array $arguments,
+ array $options = [],
+ $input_headers = [],
+ &$output_headers = []
+ ) {
+ }
+
+ public function SoapClient(mixed $wsdl, array $options = [])
+ {
+ return new static($wsdl, $options);
+ }
+
+}
\ No newline at end of file
diff --git a/tests/TestCase.php b/tests/TestCase.php
index d74c8ba..167d79f 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -4,11 +4,17 @@
use Orchestra\Testbench\TestCase as OrchestraTestCase;
use RicorocksDigitalAgency\Soap\Providers\SoapServiceProvider;
+use RicorocksDigitalAgency\Soap\Tests\Mocks\MockSoapClient;
abstract class TestCase extends OrchestraTestCase
{
const EXAMPLE_SOAP_ENDPOINT = "http://www.dneonline.com/calculator.asmx?WSDL";
+ protected function fakeClient()
+ {
+ $this->app->bind(\SoapClient::class, MockSoapClient::class);
+ }
+
protected function getPackageProviders($app)
{
return [SoapServiceProvider::class];
diff --git a/tests/Tracing/SoapTracingTest.php b/tests/Tracing/SoapTracingTest.php
new file mode 100644
index 0000000..45400a3
--- /dev/null
+++ b/tests/Tracing/SoapTracingTest.php
@@ -0,0 +1,49 @@
+fakeClient();
+ }
+
+ /** @test */
+ public function a_trace_can_be_requested_at_time_of_request()
+ {
+ $response = Soap::to(static::EXAMPLE_SOAP_ENDPOINT)
+ ->trace()
+ ->call('Add', ['intA' => 10, 'intB' => 25]);
+
+ $this->assertEquals('World', $response->trace()->xmlRequest);
+ $this->assertEquals('Success!', $response->trace()->xmlResponse);
+ }
+
+ /** @test */
+ public function a_trace_can_be_requested_globally()
+ {
+ Soap::trace();
+
+ $response = Soap::to(static::EXAMPLE_SOAP_ENDPOINT)
+ ->call('Add', ['intA' => 10, 'intB' => 25]);
+
+ $this->assertEquals('World', $response->trace()->xmlRequest);
+ $this->assertEquals('Success!', $response->trace()->xmlResponse);
+ }
+
+ /** @test */
+ public function by_default_the_trace_has_no_content_on_the_response()
+ {
+ $response = Soap::to(static::EXAMPLE_SOAP_ENDPOINT)
+ ->call('Add', ['intA' => 10, 'intB' => 25]);
+
+ $this->assertEmpty($response->trace()->xmlRequest);
+ $this->assertEmpty($response->trace()->xmlResponse);
+ }
+}
diff --git a/tests/Tracing/TraceTest.php b/tests/Tracing/TraceTest.php
new file mode 100644
index 0000000..636439a
--- /dev/null
+++ b/tests/Tracing/TraceTest.php
@@ -0,0 +1,48 @@
+World';
+
+ $trace = Trace::thisXmlRequest($xml);
+
+ $this->assertSame($xml, $trace->xmlRequest);
+ }
+
+ /** @test */
+ public function the_trace_object_has_a_thisXmlResponse_method()
+ {
+ $request = 'World';
+ $response = 'Success!';
+
+ $trace = Trace::thisXmlRequest($request)->thisXmlResponse($response);
+
+ $this->assertSame($response, $trace->xmlResponse);
+ }
+
+ /** @test */
+ public function a_null_trace_returns_gracefully()
+ {
+ $trace = Trace::thisXmlRequest(null)->thisXmlResponse(null);
+
+ $this->assertNull($trace->xmlRequest);
+ $this->assertNull($trace->xmlResponse);
+ }
+
+ /** @test */
+ public function a_fresh_trace_returns_gracefully()
+ {
+ $trace = new Trace;
+
+ $this->assertNull($trace->xmlRequest);
+ $this->assertNull($trace->xmlResponse);
+ }
+}