Skip to content

Commit

Permalink
Add upload order document operation (#112)
Browse files Browse the repository at this point in the history
- Add upload order document operation
- Fixes php version used in DockerFile
- Add composer scripts commands
- Fixes unit tests to work with more recent version of PHPUnit
  • Loading branch information
mdumoulin authored Nov 23, 2023
1 parent 3afd3f2 commit 40d2020
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 67 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM php:5.6-cli-alpine
FROM php:7.1-cli-alpine

ARG MOUNTPOINT=/var/www
ARG COMPOSER_BIN_DIR=/usr/local/bin
Expand Down
9 changes: 6 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@
},
"scripts": {
"test": [
"@php vendor/bin/phpunit",
"@php vendor/bin/sfcs src --progress -vvv"
]
"@tests-unit",
"@phpcs"
],
"tests-unit": "vendor/bin/phpunit",
"phpcs": "vendor/bin/sfcs src --progress -vvv",
"phpcsfix": "vendor/bin/sfcs src --progress -vvv --autofix"
},
"scripts-descriptions": {
"test" : "Run PHPUnit tests suites and Coding standards validator"
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ version: '3'

services:
sf-php-sdk-dev:
container_name: sfeed.php-sdk-dev
image: php-sdk-dev
build:
context: .
Expand Down
5 changes: 5 additions & 0 deletions docs/development/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ docker-compose build
docker-compose run sf-php-sdk-dev composer install --dev
```

3. Connect to container
```bash
docker-compose run sf-php-sdk-dev /bin/sh
```

## Code checks

To help you test your code against our requirements, there is a composer test script configured :
Expand Down
20 changes: 20 additions & 0 deletions docs/manual/resources/order.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,23 @@ $operation

$orderApi->execute($operation);
```

### Upload documents

To upload order documents, you need the following parameters :
1. [mandatory] `$reference` : Order reference (eg: 'reference1')
2. [mandatory] `$channelName` : The channel where the order is from (eg: 'amazon')
3. [mandatory] `$documents` : One or more documents to upload

Example :

```php
namespace ShoppingFeed\Sdk\Api\Order;

$operation = new OrderOperation();
$operation
->uploadDocument('ref1', 'leroymerlin', new Document\Invoice('/tmp/amazon_ref1_invoice.pdf'))
->uploadDocument('ref2', 'leroymerlin', new Document\Invoice('/tmp/amazon_ref2_invoice.pdf'));

$orderApi->execute($operation);
```
35 changes: 35 additions & 0 deletions src/Api/Order/Document/AbstractDocument.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace ShoppingFeed\Sdk\Api\Order\Document;

abstract class AbstractDocument
{
/**
* @var string
*/
private $path;

/**
* @var string
*/
private $type;

public function __construct(
string $path,
string $type
)
{
$this->path = $path;
$this->type = $type;
}

public function getPath(): string
{
return $this->path;
}

public function getType(): string
{
return $this->type;
}
}
11 changes: 11 additions & 0 deletions src/Api/Order/Document/Invoice.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace ShoppingFeed\Sdk\Api\Order\Document;

class Invoice extends AbstractDocument
{
public function __construct(string $path)
{
parent::__construct($path, 'invoice');
}
}
117 changes: 98 additions & 19 deletions src/Api/Order/OrderOperation.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
<?php
namespace ShoppingFeed\Sdk\Api\Order;

use RuntimeException;
use ShoppingFeed\Sdk\Api;
use ShoppingFeed\Sdk\Api\Order\Document\AbstractDocument;
use ShoppingFeed\Sdk\Exception;
use ShoppingFeed\Sdk\Hal;
use ShoppingFeed\Sdk\Operation;
use ShoppingFeed\Sdk\Exception;

class OrderOperation extends Operation\AbstractBulkOperation
{
/**
* Operation types
*/
const TYPE_ACCEPT = 'accept';
const TYPE_CANCEL = 'cancel';
const TYPE_REFUSE = 'refuse';
const TYPE_SHIP = 'ship';
const TYPE_REFUND = 'refund';
const TYPE_ACKNOWLEDGE = 'acknowledge';
const TYPE_UNACKNOWLEDGE = 'unacknowledge';
public const TYPE_ACCEPT = 'accept';
public const TYPE_CANCEL = 'cancel';
public const TYPE_REFUSE = 'refuse';
public const TYPE_SHIP = 'ship';
public const TYPE_REFUND = 'refund';
public const TYPE_ACKNOWLEDGE = 'acknowledge';
public const TYPE_UNACKNOWLEDGE = 'unacknowledge';
public const TYPE_UPLOAD_DOCUMENTS = 'upload-documents';

/**
* @var array
Expand All @@ -30,6 +33,7 @@ class OrderOperation extends Operation\AbstractBulkOperation
self::TYPE_REFUND,
self::TYPE_ACKNOWLEDGE,
self::TYPE_UNACKNOWLEDGE,
self::TYPE_UPLOAD_DOCUMENTS,
];

/**
Expand Down Expand Up @@ -179,6 +183,27 @@ public function unacknowledge($reference, $channelName)
return $this;
}

/**
* @param string $reference The channel's order reference
* @param string $channelName The channel's name
* @param Document\AbstractDocument $document
*
* @return OrderOperation
*
* @throws Exception\InvalidArgumentException
*/
public function uploadDocument($reference, $channelName, Document\AbstractDocument $document): self
{
$this->addOperation(
$reference,
$channelName,
self::TYPE_UPLOAD_DOCUMENTS,
['document' => $document]
);

return $this;
}

/**
* Execute all declared operations
*
Expand All @@ -192,10 +217,7 @@ public function execute(Hal\HalLink $link)
$resources = new \ArrayObject();

foreach ($this->allowedOperationTypes as $type) {
$this->eachBatch(
$this->createRequestGenerator($type, $link, $requests),
$type
);
$this->populateRequests($type, $link, $requests);
}

$link->batchSend(
Expand All @@ -213,24 +235,81 @@ function (Hal\HalResource $batch) use ($resources) {
);
}

private function populateRequests($type, Hal\HalLink $link, \ArrayAccess $requests): void
{
// Upload documents require dedicated processing because of file upload specificities
if (self::TYPE_UPLOAD_DOCUMENTS === $type) {
$this->populateRequestsForUploadDocuments($link, $requests);
return;
}

$this->eachBatch(
function (array $chunk) use ($type, $link, &$requests) {
$requests[] = $link->createRequest(
'POST',
['operation' => $type],
['order' => $chunk]
);
},
$type
);
}

/**
* Create request generation callback
* Create requests for upload documents operation. We batch request by 20
* to not send too many files at once.
*
* @param string $type
* @param Hal\HalLink $link
* @param array $requests
*
* @return \Closure
* @return \Psr\Http\Message\RequestInterface
*/
private function createRequestGenerator($type, Hal\HalLink $link, \ArrayAccess $requests)
private function populateRequestsForUploadDocuments(Hal\HalLink $link, \ArrayAccess $requests)
{
return function (array $chunk) use ($type, $link, &$requests) {
$type = self::TYPE_UPLOAD_DOCUMENTS;

foreach (array_chunk($this->getOperations($type), 20) as $batch) {
$body = [];
$orders = [];

foreach ($batch as $operation) {
/** @var AbstractDocument $document */
$document = $operation['document'];

$resource = fopen($document->getPath(), 'rb');

if (false === $resource) {
throw new RuntimeException(
sprintf('Unable to read "%s"', $document->getPath())
);
}

$body[] = [
'name' => 'files[]',
'contents' => $resource,
];

$orders[] = [
'reference' => $operation['reference'],
'channelName' => $operation['channelName'],
'documents' => [
['type' => $document->getType()],
],
];
}

$body[] = [
'name' => 'body',
'contents' => json_encode(['order' => $orders]),
];

$requests[] = $link->createRequest(
'POST',
['operation' => $type],
['order' => $chunk]
$body,
['Content-Type' => 'multipart/form-data']
);
};
}
}

/**
Expand Down
24 changes: 16 additions & 8 deletions src/Hal/HalLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public function withAddedHref($path, $variables = [])
{
$instance = clone $this;
$instance->href = rtrim($instance->getUri($variables), '/') .
'/' . ltrim($path, '/');
'/' . ltrim($path, '/');

return $instance;
}
Expand Down Expand Up @@ -262,15 +262,23 @@ public function send($request, array $config = [])
*
* @return \Psr\Http\Message\RequestInterface
*/
public function createRequest($method, array $variables = [], $body = null)
public function createRequest($method, array $variables = [], $body = null, $headers = [])
{
$uri = $this->getUri($variables);
$method = strtoupper($method);
$headers = [];
$uri = $this->getUri($variables);
$method = strtoupper($method);

if ((null !== $body && '' !== $body) && in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'])) {
$headers['Content-Type'] = 'application/json';
$body = Json::encode($body);
$hasBody = null !== $body && '' !== $body;

if ($hasBody && in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'])) {
if (! isset($headers['Content-Type'])) {
$headers['Content-Type'] = 'application/json';
}

switch ($headers['Content-Type']) {
case 'application/json':
$body = Json::encode($body);
break;
}
}

return $this->client->createRequest($method, $uri, $headers, $body);
Expand Down
5 changes: 5 additions & 0 deletions src/Http/Adapter/GuzzleHTTPAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public function batchSend(array $requests, array $options = [])
*/
public function createRequest($method, $uri, array $headers = [], $body = null)
{
if (isset($headers['Content-Type']) && 'multipart/form-data' === $headers['Content-Type']) {
unset($headers['Content-Type']);
$body = new GuzzleHttp\Psr7\MultipartStream($body);
}

return new GuzzleHttp\Psr7\Request($method, $uri, $headers, $body);
}

Expand Down
Loading

0 comments on commit 40d2020

Please sign in to comment.