Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@
}
],
"require": {
"php": ">=8.1",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"saloonphp/saloon": "^3.4",
"openspout/openspout": "^4.23"
"php": ">=8.1",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"saloonphp/saloon": "^3.4",
"openspout/openspout": "^4.23"
},
"require-dev": {
"phpunit/phpunit": "^8.0 || ^9.0",
"composer/semver": "^3.4",
"symfony/console": "^6.3",
"psy/psysh": "^0.11.22",
"voku/simple_html_dom": "^4.8",
"highsidelabs/saloon-sdk-generator": "dev-master",
"laravel/pint": "^1.13"
},
"autoload": {
"psr-4": {
"SellingPartnerApi\\": "src/"
"SellingPartnerApi\\": "src/",
"Crescat\\SaloonSdkGenerator\\": "src/"
},
"files": [
"src/Generator/constants.php"
Expand Down
78 changes: 78 additions & 0 deletions src/BaseDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Crescat\SaloonSdkGenerator;

use ReflectionClass;
use ReflectionProperty;
use DateTime;

abstract class BaseDto implements \JsonSerializable
{
protected static array $complexArrayTypes = [];
protected static array $attributeMap = [];

// No constructor - let child classes handle their own typed constructors

public function toArray(): array
{
$result = [];
$reflection = new ReflectionClass($this);

foreach ($reflection->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
if ($property->isStatic()) {
continue;
}

$key = $property->getName();
if (!$property->isInitialized($this)) {
continue;
}

$value = $property->getValue($this);

if ($value === null) {
continue;
}

// Convert property name to camelCase for JSON
$jsonKey = $this->toCamelCase($key);

if ($value instanceof self) {
$result[$jsonKey] = $value->toArray();
} elseif ($value instanceof DateTime) {
$result[$jsonKey] = $value->format('Y-m-d\TH:i:s\Z');
} elseif (is_array($value)) {
$result[$jsonKey] = array_map(function ($item) {
if ($item instanceof self) {
return $item->toArray();
} elseif ($item instanceof DateTime) {
return $item->format('Y-m-d\TH:i:s\Z');
}
return $item;
}, $value);
} else {
$result[$jsonKey] = $value;
}
}

return $result;
}

public function jsonSerialize(): array
{
return $this->toArray();
}

public function toJson(): string
{
return json_encode($this->toArray());
}

protected function toCamelCase(string $string): string
{
// Convert snake_case or PascalCase to camelCase
$string = str_replace('_', '', ucwords($string, '_'));
return lcfirst($string);
}
}

122 changes: 122 additions & 0 deletions src/BaseResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

namespace Crescat\SaloonSdkGenerator;

use ReflectionClass;
use ReflectionProperty;
use DateTime;

abstract class BaseResponse implements \JsonSerializable
{
protected static array $complexArrayTypes = [];
protected static array $attributeMap = [];

// No constructor - let child classes handle their own typed constructors

/**
* Deserialize JSON data into a response object
*/
public static function deserialize(array $data, string $class): self
{
$reflection = new ReflectionClass($class);
$constructor = $reflection->getConstructor();

if (!$constructor) {
return new $class();
}

$params = [];
foreach ($constructor->getParameters() as $param) {
$paramName = $param->getName();

// Try different key formats (camelCase, snake_case, PascalCase)
$value = $data[$paramName]
?? $data[self::toSnakeCase($paramName)]
?? $data[ucfirst($paramName)]
?? null;

if ($value === null && $param->isDefaultValueAvailable()) {
$params[] = $param->getDefaultValue();
} else {
$params[] = $value;
}
}

return $reflection->newInstanceArgs($params);
}

/**
* Convert camelCase to snake_case
*/
protected static function toSnakeCase(string $string): string
{
return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $string));
}

public function toArray(): array
{
$result = [];
$reflection = new ReflectionClass($this);

foreach ($reflection->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
if ($property->isStatic()) {
continue;
}

$key = $property->getName();
if (!$property->isInitialized($this)) {
continue;
}

$value = $property->getValue($this);

if ($value === null) {
continue;
}

// Convert property name to camelCase for JSON
$jsonKey = $this->toCamelCase($key);

if (is_object($value) && method_exists($value, 'toArray')) {
$result[$jsonKey] = $value->toArray();
} elseif ($value instanceof DateTime) {
$result[$jsonKey] = $value->format('Y-m-d\TH:i:s\Z');
} elseif (is_array($value)) {
$result[$jsonKey] = array_map(function ($item) {
if (is_object($item) && method_exists($item, 'toArray')) {
return $item->toArray();
} elseif ($item instanceof DateTime) {
return $item->format('Y-m-d\TH:i:s\Z');
}
return $item;
}, $value);
} else {
$result[$jsonKey] = $value;
}
}

return $result;
}

public function jsonSerialize(): array
{
return $this->toArray();
}

public function toJson(): string
{
return json_encode($this->toArray());
}

protected function toCamelCase(string $string): string
{
// Convert snake_case or PascalCase to camelCase
$string = str_replace('_', '', ucwords($string, '_'));
return lcfirst($string);
}
}





28 changes: 17 additions & 11 deletions src/Seller/FBAInventoryV1/Requests/GetInventorySummaries.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace SellingPartnerApi\Seller\FBAInventoryV1\Requests;

use Exception;
Expand All @@ -16,14 +18,14 @@ class GetInventorySummaries extends Request
protected Method $method = Method::GET;

/**
* @param string $granularityType The granularity type for the inventory aggregation level.
* @param string $granularityId The granularity ID for the inventory aggregation level.
* @param array $marketplaceIds The marketplace ID for the marketplace for which to return inventory summaries.
* @param ?bool $details true to return inventory summaries with additional summarized inventory details and quantities. Otherwise, returns inventory summaries only (default value).
* @param ?DateTime $startDateTime A start date and time in ISO8601 format. If specified, all inventory summaries that have changed since then are returned. You must specify a date and time that is no earlier than 18 months prior to the date and time when you call the API. Note: Changes in inboundWorkingQuantity, inboundShippedQuantity and inboundReceivingQuantity are not detected.
* @param ?array $sellerSkus A list of seller SKUs for which to return inventory summaries. You may specify up to 50 SKUs.
* @param ?string $sellerSku A single seller SKU used for querying the specified seller SKU inventory summaries.
* @param ?string $nextToken String token returned in the response of your previous request. The string token will expire 30 seconds after being created.
* @param string $granularityType The granularity type for the inventory aggregation level.
* @param string $granularityId The granularity ID for the inventory aggregation level.
* @param array $marketplaceIds The marketplace ID for the marketplace for which to return inventory summaries.
* @param ?bool $details true to return inventory summaries with additional summarized inventory details and quantities. Otherwise, returns inventory summaries only (default value).
* @param ?DateTime $startDateTime A start date and time in ISO8601 format. If specified, all inventory summaries that have changed since then are returned. You must specify a date and time that is no earlier than 18 months prior to the date and time when you call the API. Note: Changes in inboundWorkingQuantity, inboundShippedQuantity and inboundReceivingQuantity are not detected.
* @param ?array $sellerSkus A list of seller SKUs for which to return inventory summaries. You may specify up to 50 SKUs.
* @param ?string $sellerSku A single seller SKU used for querying the specified seller SKU inventory summaries.
* @param ?string $nextToken String token returned in the response of your previous request. The string token will expire 30 seconds after being created.
*/
public function __construct(
protected string $granularityType,
Expand All @@ -42,10 +44,14 @@ public function defaultQuery(): array
return array_filter([
'granularityType' => $this->granularityType,
'granularityId' => $this->granularityId,
'marketplaceIds' => $this->marketplaceIds,
'details' => $this->details,
// Fix: Amazon FBA Inventory API expects marketplaceIds as a comma-separated string, not an array
// The API will reject marketplaceIds[0]=VALUE format that Saloon generates from arrays
'marketplaceIds' => implode(',', $this->marketplaceIds),
// Fix: Amazon expects 'true'/'false' strings, not 1/0 integers
'details' => $this->details !== null ? ($this->details ? 'true' : 'false') : null,
'startDateTime' => $this->startDateTime?->format(\DateTime::RFC3339),
'sellerSkus' => $this->sellerSkus,
// Fix: Same issue with sellerSkus - convert to comma-separated string
'sellerSkus' => $this->sellerSkus ? implode(',', $this->sellerSkus) : null,
'sellerSku' => $this->sellerSku,
'nextToken' => $this->nextToken,
]);
Expand Down
9 changes: 7 additions & 2 deletions src/Seller/ReportsV20210630/Requests/GetReportDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ public function __construct(
protected string $reportDocumentId,
protected string $reportType,
) {
$rdtMiddleware = new RestrictedDataToken($this->resolveEndpoint(), 'GET', []);
$this->middleware()->onRequest($rdtMiddleware);
$reports = json_decode(file_get_contents(RESOURCE_DIR.'/reports.json'), true);
$isRestricted = isset($reports[$this->reportType]) && $reports[$this->reportType]['restricted'];

if ($isRestricted) {
$rdtMiddleware = new RestrictedDataToken($this->resolveEndpoint(), 'GET', []);
$this->middleware()->onRequest($rdtMiddleware);
}
$this->middleware()->onRequest(new RestrictedReport);
}

Expand Down