Skip to content

Commit 3463bcc

Browse files
authored
Merge pull request #94 from tyx/87WhenIattachfiles-ver2
🆕 Support multipart request
2 parents a151386 + e81d343 commit 3463bcc

File tree

6 files changed

+112
-9
lines changed

6 files changed

+112
-9
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
"silex/silex": "~2.0",
3737
"symfony/process": "~2.1|~3.0",
3838
"guzzlehttp/psr7": "^1.3",
39-
"php-http/curl-client": "^1.5",
40-
"php-http/mock-client": "^1.0"
39+
"php-http/mock-client": "^1.0",
40+
"php-http/guzzle6-adapter": "^1.1.1"
4141
},
4242
"config": {
4343
"optimize-autoloader": true,
2.33 KB
Loading

features/send_request.feature

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,22 @@ Feature: Test send API request
4040
Then the response status code should be 200
4141
And the JSON node "headers.header" should have 1 element
4242
And the JSON node "headers.header[0]" should be equal to "value2"
43+
44+
Scenario: Attaching files and sending POST request
45+
Given I add "Content-type" header equal to "application/json"
46+
And I attach the following files:
47+
| name | path |
48+
| json-schema | features/bootstrap/fixtures/json-schema.json |
49+
| test-img | features/bootstrap/fixtures/test-img.jpg |
50+
When I send a POST request to "post-with-files" with parameters:
51+
| username | pablo |
52+
| password | money |
53+
| terms_accepted | 1 |
54+
Then the response status code should be 200
55+
And the response should be in JSON
56+
And the JSON node "post_files_count" should be equal to "2"
57+
And the JSON node "post_fields.username" should be equal to "pablo"
58+
And the JSON node "post_fields.password" should be equal to "money"
59+
And the JSON node "post_fields.terms_accepted" should be equal to "1"
60+
And the JSON node "content_type_header_value" should not contain "application/json"
61+
And the JSON node "content_type_header_value" should contain "multipart/form-data"

src/Rest/RestApiBrowser.php

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class RestApiBrowser
2727
/** @var array */
2828
private $requestHeaders = [];
2929

30+
/** @var array */
31+
private $requestFiles = [];
32+
3033
/** @var ResponseStorage */
3134
private $responseStorage;
3235

@@ -99,6 +102,10 @@ public function sendRequest($method, $uri, $body = null)
99102
$uri = rtrim($this->host, '/').'/'.ltrim($uri, '/');
100103
}
101104

105+
if (is_array($body)) {
106+
$body = $this->buildMultipartBody($body);
107+
}
108+
102109
$this->request = $this->messageFactory->createRequest($method, $uri, $this->requestHeaders, $body);
103110
$this->response = $this->httpClient->sendRequest($this->request);
104111
$this->requestHeaders = [];
@@ -138,6 +145,7 @@ public function setRequestHeader($name, $value)
138145
*/
139146
public function addRequestHeader($name, $value)
140147
{
148+
$name = strtolower($name);
141149
if (isset($this->requestHeaders[$name])) {
142150
$this->requestHeaders[$name] .= ', '.$value;
143151
} else {
@@ -150,18 +158,60 @@ public function addRequestHeader($name, $value)
150158
*/
151159
private function removeRequestHeader($headerName)
152160
{
161+
$headerName = strtolower($headerName);
153162
if (array_key_exists($headerName, $this->requestHeaders)) {
154163
unset($this->requestHeaders[$headerName]);
155164
}
156165
}
157166

167+
/**
168+
* @param string $name
169+
* @param string $path
170+
*/
171+
public function addFileToRequest($name, $path)
172+
{
173+
$this->requestFiles[] = [
174+
'name' => $name,
175+
'path' => $path,
176+
];
177+
}
178+
179+
/**
180+
* @param array $body
181+
*
182+
* @return \GuzzleHttp\Psr7\MultipartStream
183+
*/
184+
private function buildMultipartBody($body)
185+
{
186+
$multiparts = array_merge(
187+
array_map(
188+
function ($key, $value) {
189+
return ['name' => $key, 'contents' => $value];
190+
},
191+
array_keys($body),
192+
$body
193+
),
194+
array_map(
195+
function ($file) {
196+
return ['name' => $file['name'], 'contents' => fopen($file['path'], 'r')];
197+
},
198+
$this->requestFiles
199+
)
200+
);
201+
202+
$boundary = sha1(uniqid('', true));
203+
$this->setRequestHeader('Content-Type', 'multipart/form-data; boundary='.$boundary);
204+
205+
return new \GuzzleHttp\Psr7\MultipartStream($multiparts, $boundary);
206+
}
207+
158208
/**
159209
* @param string $uri
160210
*
161211
* @return bool
162212
*/
163213
private function hasHost($uri)
164214
{
165-
return strpos($uri, '://') !== false;
215+
return false !== strpos($uri, '://');
166216
}
167217
}

src/RestApiContext.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Behat\Gherkin\Node\PyStringNode;
99
use Psr\Http\Message\ResponseInterface;
1010
use Ubirak\RestApiBehatExtension\Rest\RestApiBrowser;
11+
use Behat\Gherkin\Node\TableNode;
1112

1213
class RestApiContext implements Context, SnippetAcceptingContext
1314
{
@@ -46,6 +47,30 @@ public function iSendARequestWithBody($method, $url, PyStringNode $body)
4647
$this->restApiBrowser->sendRequest($method, $url, (string) $body);
4748
}
4849

50+
/**
51+
* Sends HTTP request to specific URL with raw body from PyString.
52+
*
53+
* @param string $method request method
54+
* @param string $url relative url
55+
* @param TableNode $parameters
56+
*
57+
* @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)" with parameters:$/
58+
*/
59+
public function iSendARequestWithParameters($method, $url, TableNode $parameters = null)
60+
{
61+
$this->restApiBrowser->sendRequest($method, $url, $parameters->getRowsHash());
62+
}
63+
64+
/**
65+
* @When I attach the following files:
66+
*/
67+
public function iAttachTheFollowingFiles(TableNode $files)
68+
{
69+
foreach ($files as $file) {
70+
$this->restApiBrowser->addFileToRequest($file['name'], $file['path']);
71+
}
72+
}
73+
4974
/**
5075
* @param string $code status code
5176
*

www/index.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
<?php
22

33
/**
4-
* Totally copied from https://github.com/Behat/WebApiExtension
4+
* Totally copied from https://github.com/Behat/WebApiExtension.
55
*/
6-
7-
use Silex\Application;
86
use Symfony\Component\HttpFoundation\JsonResponse;
97
use Symfony\Component\HttpFoundation\Request;
108

11-
require_once __DIR__ . '/../vendor/autoload.php';
9+
require_once __DIR__.'/../vendor/autoload.php';
1210

1311
$app = new Silex\Application();
1412

1513
$app->match(
1614
'echo',
17-
function(Request $req) {
15+
function (Request $req) {
1816
$ret = array(
1917
'warning' => 'Do not expose this service in production : it is intrinsically unsafe',
2018
);
@@ -57,7 +55,7 @@ function(Request $req) {
5755
$app->match(
5856
'error_random',
5957
function (Request $request) {
60-
$statusCode = time() % 3 <= 0 ? 200 : 502 ;
58+
$statusCode = time() % 3 <= 0 ? 200 : 502;
6159

6260
return new JsonResponse([], $statusCode);
6361
}
@@ -69,4 +67,15 @@ function (Request $request) {
6967
}
7068
);
7169

70+
$app->match(
71+
'post-with-files',
72+
function (Request $request) {
73+
return new JsonResponse([
74+
'content_type_header_value' => $request->headers->get('content-type'),
75+
'post_files_count' => count($request->files),
76+
'post_fields' => $request->request->all(),
77+
]);
78+
}
79+
);
80+
7281
$app->run();

0 commit comments

Comments
 (0)