diff --git a/src/Api/Management/Query/StatsQueryInterface.php b/src/Api/Management/Query/StatsQueryInterface.php index efef8e76..d230f23e 100644 --- a/src/Api/Management/Query/StatsQueryInterface.php +++ b/src/Api/Management/Query/StatsQueryInterface.php @@ -37,7 +37,7 @@ public function getMetrics(): array; /** * @param string[] $metrics * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setMetrics(array $metrics): StatsQueryInterface; @@ -49,7 +49,7 @@ public function getTimeRange(): Period; /** * @param Period $timeRange * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setTimeRange(Period $timeRange): StatsQueryInterface; @@ -61,7 +61,7 @@ public function getTimeUnit(): ?string; /** * @param string|null $timeUnit * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setTimeUnit(?string $timeUnit): StatsQueryInterface; @@ -73,7 +73,7 @@ public function getSortBy(): ?string; /** * @param string|null $sortBy * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setSortBy(?string $sortBy): StatsQueryInterface; @@ -85,7 +85,7 @@ public function getSort(): ?string; /** * @param string|null $sort * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setSort(?string $sort): StatsQueryInterface; @@ -97,7 +97,7 @@ public function getTopK(): ?int; /** * @param int|null $topK * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setTopK(?int $topK): StatsQueryInterface; @@ -109,7 +109,7 @@ public function getLimit(): ?int; /** * @param int|null $limit * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setLimit(?int $limit): StatsQueryInterface; @@ -121,7 +121,7 @@ public function getOffset(): ?int; /** * @param int|null $offset * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setOffset(?int $offset): StatsQueryInterface; @@ -133,7 +133,7 @@ public function getRealtime(): ?bool; /** * @param bool|null $realtime * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setRealtime(?bool $realtime): StatsQueryInterface; @@ -145,7 +145,7 @@ public function getAccuracy(): ?int; /** * @param int|null $accuracy * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setAccuracy(?int $accuracy): StatsQueryInterface; @@ -157,7 +157,7 @@ public function getTsAscending(): bool; /** * @param bool $tsAscending * - * @return StatsQueryInterface + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface */ public function setTsAscending(bool $tsAscending): StatsQueryInterface; @@ -169,7 +169,7 @@ public function getFilter(): ?string; /** * @param string|null $filter * - * @return StatsQuery + * @return \Apigee\Edge\Api\Management\Query\StatsQueryInterface * * @see https://docs.apigee.com/analytics-services/reference/analytics-reference#filters */ diff --git a/src/Api/Monetization/Controller/CompanyReportDefinitionController.php b/src/Api/Monetization/Controller/CompanyReportDefinitionController.php new file mode 100644 index 00000000..6b7252a1 --- /dev/null +++ b/src/Api/Monetization/Controller/CompanyReportDefinitionController.php @@ -0,0 +1,63 @@ +companyName = $developerId; + parent::__construct($organization, $client, $reportDefinitionController, $entitySerializer); + } + + /** + * @inheritdoc + */ + protected function getBaseEndpointUri(): UriInterface + { + return $this->client->getUriFactory()->createUri("/mint/organizations/{$this->organization}/companies/{$this->companyName}/report-definitions"); + } + + /** + * @inheritdoc + */ + protected function getEntityClass(): string + { + return CompanyReportDefinition::class; + } +} diff --git a/src/Api/Monetization/Controller/CompanyReportDefinitionControllerInterface.php b/src/Api/Monetization/Controller/CompanyReportDefinitionControllerInterface.php new file mode 100644 index 00000000..5e9d1229 --- /dev/null +++ b/src/Api/Monetization/Controller/CompanyReportDefinitionControllerInterface.php @@ -0,0 +1,23 @@ +developerId = $developerId; + parent::__construct($organization, $client, $reportDefinitionController, $entitySerializer); + } + + /** + * @inheritdoc + */ + protected function getBaseEndpointUri(): UriInterface + { + return $this->client->getUriFactory()->createUri("/mint/organizations/{$this->organization}/developers/{$this->developerId}/report-definitions"); + } + + /** + * @inheritdoc + */ + protected function getEntityClass(): string + { + return DeveloperReportDefinition::class; + } +} diff --git a/src/Api/Monetization/Controller/DeveloperReportDefinitionControllerInterface.php b/src/Api/Monetization/Controller/DeveloperReportDefinitionControllerInterface.php new file mode 100644 index 00000000..02b9202a --- /dev/null +++ b/src/Api/Monetization/Controller/DeveloperReportDefinitionControllerInterface.php @@ -0,0 +1,23 @@ + $sort]; + } + + return $this->listEntitiesInRange($this->getBaseEndpointUri()->withQuery(http_build_query($queryParams)), $limit, $page); + } +} diff --git a/src/Api/Monetization/Controller/LegalEntityReportDefinitionController.php b/src/Api/Monetization/Controller/LegalEntityReportDefinitionController.php new file mode 100644 index 00000000..2cac6697 --- /dev/null +++ b/src/Api/Monetization/Controller/LegalEntityReportDefinitionController.php @@ -0,0 +1,99 @@ +reportDefinitionController = $reportDefinitionController ?? new ReportDefinitionController($organization, $client, $entitySerializer); + parent::__construct($organization, $client, $entitySerializer); + } + + /** + * @inheritdoc + */ + public function generateReport(AbstractCriteria $criteria): string + { + return $this->reportDefinitionController->generateReport($criteria); + } + + /** + * @inheritdoc + */ + public function delete(string $entityId): void + { + $this->reportDefinitionController->delete($entityId); + } + + /** + * @inheritdoc + */ + public function load(string $entityId): EntityInterface + { + return $this->reportDefinitionController->load($entityId); + } + + /** + * @inheritdoc + */ + public function update(EntityInterface $entity): void + { + // @see \Apigee\Edge\Api\Monetization\Normalizer\LegalEntityReportDefinitionNormalizer::normalize() + $this->reportDefinitionController->update($entity); + } +} diff --git a/src/Api/Monetization/Controller/PaginatedListingHelperTrait.php b/src/Api/Monetization/Controller/PaginatedListingHelperTrait.php index fb649025..8526794d 100644 --- a/src/Api/Monetization/Controller/PaginatedListingHelperTrait.php +++ b/src/Api/Monetization/Controller/PaginatedListingHelperTrait.php @@ -31,6 +31,10 @@ protected function listAllEntities(UriInterface $uri): array parse_str($uri->getQuery(), $query_params); $query_params['all'] = 'true'; + // This makes easier testing and also creates a query parameter list + // that is easy to read. + ksort($query_params); + return $this->listEntities($uri->withQuery(http_build_query($query_params))); } @@ -47,6 +51,10 @@ protected function listEntitiesInRange(UriInterface $uri, int $limit = null, int $query_params['size'] = $limit; } + // This makes easier testing and also creates a query parameter list + // that is easy to read. + ksort($query_params); + return $this->listEntities($uri->withQuery(http_build_query($query_params))); } } diff --git a/src/Api/Monetization/Controller/ReportDefinitionController.php b/src/Api/Monetization/Controller/ReportDefinitionController.php new file mode 100644 index 00000000..6f8dfa11 --- /dev/null +++ b/src/Api/Monetization/Controller/ReportDefinitionController.php @@ -0,0 +1,84 @@ +getReportTypeFromCriteria($criteria))) . '-reports'; + $response = $this->getClient()->post( + $this->client->getUriFactory()->createUri("/mint/organizations/{$this->organization}/{$reportType}"), + $this->getEntitySerializer()->serialize($criteria, 'json'), + ['Accept' => 'application/octet-stream'] + ); + + return (string) $response->getBody(); + } + + /** + * @inheritdoc + */ + protected function getBaseEndpointUri(): UriInterface + { + return $this->client->getUriFactory()->createUri("/mint/organizations/{$this->organization}/report-definitions"); + } + + /** + * @inheritdoc + */ + protected function getEntityClass(): string + { + return ReportDefinition::class; + } +} diff --git a/src/Api/Monetization/Controller/ReportDefinitionControllerInterface.php b/src/Api/Monetization/Controller/ReportDefinitionControllerInterface.php new file mode 100644 index 00000000..106d4f9e --- /dev/null +++ b/src/Api/Monetization/Controller/ReportDefinitionControllerInterface.php @@ -0,0 +1,59 @@ +companyReportDefinitionClass, $format, $context); + } + + /** + * @inheritdoc + */ + public function supportsDenormalization($data, $type, $format = null) + { + if (parent::supportsDenormalization($data, $type, $format)) { + // Non-developer specific reports API also returns the developer + // property, but it is always NULL. + return is_object($data->developer) && $data->developer->isCompany; + } + + return false; + } +} diff --git a/src/Api/Monetization/Denormalizer/DeveloperReportDefinitionDenormalizer.php b/src/Api/Monetization/Denormalizer/DeveloperReportDefinitionDenormalizer.php new file mode 100644 index 00000000..f26f87c4 --- /dev/null +++ b/src/Api/Monetization/Denormalizer/DeveloperReportDefinitionDenormalizer.php @@ -0,0 +1,53 @@ +developerReportDefinitionClass, $format, $context); + } + + /** + * @inheritDoc + */ + public function supportsDenormalization($data, $type, $format = null) + { + if (parent::supportsDenormalization($data, $type, $format)) { + // Non-developer specific reports API also returns the developer + // property, but it is always NULL. + return is_object($data->developer) && !$data->developer->isCompany; + } + + return false; + } +} diff --git a/src/Api/Monetization/Denormalizer/ReportCriteriaDenormalizer.php b/src/Api/Monetization/Denormalizer/ReportCriteriaDenormalizer.php new file mode 100644 index 00000000..25a80829 --- /dev/null +++ b/src/Api/Monetization/Denormalizer/ReportCriteriaDenormalizer.php @@ -0,0 +1,137 @@ + 4.1 therefore we have to use a + // workaround here. + // https://symfony.com/doc/master/components/serializer.html#serializing-interfaces-and-abstract-classes + if (AbstractCriteria::class === $class && isset($context[static::CONTEXT_REPORT_DEFINITION_TYPE])) { + switch ($context[static::CONTEXT_REPORT_DEFINITION_TYPE]) { + case ReportDefinitionInterface::TYPE_BILLING: + $class = BillingReportCriteria::class; + break; + case ReportDefinitionInterface::TYPE_PREPAID_BALANCE: + $class = PrepaidBalanceReportCriteria::class; + break; + case ReportDefinitionInterface::TYPE_REVENUE: + $class = RevenueReportCriteria::class; + break; + + default: + throw new InvalidArgumentException("Invalid report definition type: {$context[static::CONTEXT_REPORT_DEFINITION_TYPE]}."); + } + } + + foreach ($data as $property => $propertyValue) { + if (is_array($propertyValue)) { + foreach ($propertyValue as $item => $value) { + if (is_object($value) && property_exists($value, 'id') && property_exists($value, 'orgId')) { + $data->{$property}[$item] = $value->id; + } + } + } + } + + // "monetizationPackageIds" and "pkgCriteria" contains the same + // information just in different format. + if (property_exists($data, 'pkgCriteria')) { + foreach ($data->pkgCriteria as $pkgCriterion) { + $data->monetizationPackageIds[] = $pkgCriterion['id']; + } + // Make sure values are unique. + $data->monetizationPackageIds = array_unique($data->monetizationPackageIds); + } + + // "prodCriteria" and "productIds" contains the same + // information just in different format. + if (property_exists($data, 'prodCriteria')) { + foreach ($data->prodCriteria as $prodCriterion) { + $data->productIds[] = $prodCriterion['id']; + } + // Make sure values are unique. + $data->productIds = array_unique($data->productIds); + } + + $denormalized = parent::denormalize($data, $class, $format, $context); + + // According to the API documentation it is always UTC. + // https://docs.apigee.com/api-platform/monetization/create-reports#createreportdefapi + $this->fixTimeZoneOnDenormalization($data, $denormalized, new \DateTimeZone('UTC')); + + return $denormalized; + } + + /** + * @inheritDoc + */ + public function supportsDenormalization($data, $type, $format = null) + { + // Do not apply this on array objects. ArrayDenormalizer takes care of them. + if ('[]' === substr($type, -2)) { + return false; + } + + return is_a($type, AbstractCriteria::class, true); + } +} diff --git a/src/Api/Monetization/Denormalizer/ReportDefinitionDenormalizer.php b/src/Api/Monetization/Denormalizer/ReportDefinitionDenormalizer.php new file mode 100644 index 00000000..14b305b9 --- /dev/null +++ b/src/Api/Monetization/Denormalizer/ReportDefinitionDenormalizer.php @@ -0,0 +1,57 @@ +company; } diff --git a/src/Api/Monetization/Entity/DeveloperAcceptedRatePlanInterface.php b/src/Api/Monetization/Entity/DeveloperAcceptedRatePlanInterface.php index 0cf5c3c3..e90fecd7 100644 --- a/src/Api/Monetization/Entity/DeveloperAcceptedRatePlanInterface.php +++ b/src/Api/Monetization/Entity/DeveloperAcceptedRatePlanInterface.php @@ -18,11 +18,8 @@ namespace Apigee\Edge\Api\Monetization\Entity; -interface DeveloperAcceptedRatePlanInterface extends AcceptedRatePlanInterface +use Apigee\Edge\Api\Monetization\Entity\Property\DeveloperPropertyInterface; + +interface DeveloperAcceptedRatePlanInterface extends AcceptedRatePlanInterface, DeveloperPropertyInterface { - /** - * @return \Apigee\Edge\Api\Monetization\Entity\DeveloperInterface|null - * It can be null only when a new rate plan gets accepted. - */ - public function getDeveloper(): ?DeveloperInterface; } diff --git a/src/Api/Monetization/Entity/DeveloperInterface.php b/src/Api/Monetization/Entity/DeveloperInterface.php index c82a4ea7..0c7a15c5 100644 --- a/src/Api/Monetization/Entity/DeveloperInterface.php +++ b/src/Api/Monetization/Entity/DeveloperInterface.php @@ -18,10 +18,8 @@ namespace Apigee\Edge\Api\Monetization\Entity; -interface DeveloperInterface extends LegalEntityInterface +use Apigee\Edge\Api\Monetization\Entity\Property\CompanyPropertyInterface; + +interface DeveloperInterface extends LegalEntityInterface, CompanyPropertyInterface { - /** - * @return \Apigee\Edge\Api\Monetization\Entity\Company|null - */ - public function getCompany(): ?Company; } diff --git a/src/Api/Monetization/Entity/DeveloperReportDefinition.php b/src/Api/Monetization/Entity/DeveloperReportDefinition.php new file mode 100644 index 00000000..031517eb --- /dev/null +++ b/src/Api/Monetization/Entity/DeveloperReportDefinition.php @@ -0,0 +1,26 @@ +criteria; + } + + /** + * @inheritdoc + */ + public function setCriteria(AbstractCriteria $criteria): void + { + $this->criteria = $criteria; + } + + /** + * @inheritdoc + */ + public function getLastModified(): ?\DateTimeImmutable + { + return $this->lastModified; + } + + /** + * @inheritdoc + * + * @internal + */ + public function setLastModified(\DateTimeImmutable $lastModified): void + { + $this->lastModified = $lastModified; + } +} diff --git a/src/Api/Monetization/Entity/ReportDefinitionInterface.php b/src/Api/Monetization/Entity/ReportDefinitionInterface.php new file mode 100644 index 00000000..dd3d80f1 --- /dev/null +++ b/src/Api/Monetization/Entity/ReportDefinitionInterface.php @@ -0,0 +1,51 @@ + 'company', - ]; - - return $mapping; + return [ + 'developer' => 'company', + ]; } } diff --git a/src/Api/Monetization/NameConverter/CompanyReportDefinitionNameConverter.php b/src/Api/Monetization/NameConverter/CompanyReportDefinitionNameConverter.php new file mode 100644 index 00000000..0d804aff --- /dev/null +++ b/src/Api/Monetization/NameConverter/CompanyReportDefinitionNameConverter.php @@ -0,0 +1,38 @@ + 'company', + ]; + } +} diff --git a/src/Api/Monetization/NameConverter/ReportCriteriaNameConverter.php b/src/Api/Monetization/NameConverter/ReportCriteriaNameConverter.php new file mode 100644 index 00000000..1465bf1c --- /dev/null +++ b/src/Api/Monetization/NameConverter/ReportCriteriaNameConverter.php @@ -0,0 +1,41 @@ + 'apps', + 'currCriteria' => 'currencies', + 'devCriteria' => 'developers', + 'devCustomAttributes' => 'developerAttributes', + 'monetizationPackageIds' => 'apiPackages', + 'productIds' => 'apiProducts', + 'showRevSharePct' => 'showRevenueSharePercentage', + 'showTxDetail' => 'showTransactionDetail', + 'showTxType' => 'showTransactionType', + 'transactionCustomAttributes' => 'transactionAttributes', + ]; + } +} diff --git a/src/Api/Monetization/NameConverter/ReportDefinitionNameConverter.php b/src/Api/Monetization/NameConverter/ReportDefinitionNameConverter.php new file mode 100644 index 00000000..f2f3bbee --- /dev/null +++ b/src/Api/Monetization/NameConverter/ReportDefinitionNameConverter.php @@ -0,0 +1,32 @@ + 'criteria', + ]; + } +} diff --git a/src/Api/Monetization/Normalizer/CompanyReportDefinitionNormalizer.php b/src/Api/Monetization/Normalizer/CompanyReportDefinitionNormalizer.php new file mode 100644 index 00000000..ed90b4e6 --- /dev/null +++ b/src/Api/Monetization/Normalizer/CompanyReportDefinitionNormalizer.php @@ -0,0 +1,51 @@ +developer); + + return $normalized; + } +} diff --git a/src/Api/Monetization/Normalizer/ReportCriteriaNormalizer.php b/src/Api/Monetization/Normalizer/ReportCriteriaNormalizer.php new file mode 100644 index 00000000..a3f358d0 --- /dev/null +++ b/src/Api/Monetization/Normalizer/ReportCriteriaNormalizer.php @@ -0,0 +1,96 @@ +organization = $organization; + $nameConverter = $nameConverter ?? new ReportCriteriaNameConverter(); + parent::__construct($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor); + } + + /** + * @inheritdoc + * + * @psalm-suppress InvalidReturnType Returning an object here is required + * for creating a valid Apigee Edge request. + */ + public function normalize($object, $format = null, array $context = []) + { + /** @var object $normalized */ + $normalized = parent::normalize($object, $format, $context); + + $addOrganizationIdCallback = function (string $id) { + return (object) ['id' => $id, 'orgId' => $this->organization]; + }; + + // Fix the structure of these properties. + foreach (['appCriteria', 'devCriteria', 'currCriteria'] as $property) { + $normalized->{$property} = array_map($addOrganizationIdCallback, $normalized->{$property}); + } + + // According to the API documentation it is always UTC. + // https://docs.apigee.com/api-platform/monetization/create-reports#createreportdefapi + $this->fixTimeZoneOnNormalization($object, $normalized, new \DateTimeZone('UTC')); + + // Just in case, do not send empty array values either to this API. + foreach ($normalized as $property => $value) { + if (is_array($value) && empty($value)) { + unset($normalized->{$property}); + } + } + + return $normalized; + } + + /** + * @inheritDoc + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof AbstractCriteria; + } +} diff --git a/src/Api/Monetization/Normalizer/ReportDefinitionNormalizer.php b/src/Api/Monetization/Normalizer/ReportDefinitionNormalizer.php new file mode 100644 index 00000000..9666363a --- /dev/null +++ b/src/Api/Monetization/Normalizer/ReportDefinitionNormalizer.php @@ -0,0 +1,74 @@ +type = $this->getReportTypeFromCriteria($object->getCriteria()); + + return $normalized; + } + + /** + * @inheritDoc + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof ReportDefinitionInterface; + } +} diff --git a/src/Api/Monetization/Serializer/ReportDefinitionSerializer.php b/src/Api/Monetization/Serializer/ReportDefinitionSerializer.php new file mode 100644 index 00000000..09405fd7 --- /dev/null +++ b/src/Api/Monetization/Serializer/ReportDefinitionSerializer.php @@ -0,0 +1,106 @@ +supportsDecoding($format)) { + $decoded = $this->decode($data, $format, $context); + $this->addReportDefinitionTypeToContext($decoded, $context); + } + + return parent::deserialize($data, $type, $format, $context); + } + + /** + * @inheritdoc + */ + public function denormalize($data, $class, $format = null, array $context = []) + { + $this->addReportDefinitionTypeToContext($data, $context); + + return parent::denormalize($data, $class, $format, $context); + } + + /** + * Add report definition type to the serialization context. + * + * @param mixed $data + * @param array $context + */ + final protected function addReportDefinitionTypeToContext($data, array &$context): void + { + if (is_object($data) && property_exists($data, 'type')) { + $context[ReportCriteriaDenormalizer::CONTEXT_REPORT_DEFINITION_TYPE] = $data->type; + } + } +} diff --git a/src/Api/Monetization/Structure/Reports/Criteria/AbstractBillingCriteria.php b/src/Api/Monetization/Structure/Reports/Criteria/AbstractBillingCriteria.php new file mode 100644 index 00000000..c9ba2101 --- /dev/null +++ b/src/Api/Monetization/Structure/Reports/Criteria/AbstractBillingCriteria.php @@ -0,0 +1,93 @@ +billingMonth = $billingMonth; + $this->billingYear = $billingYear; + } + + /** + * @param string $billingMonth + * + * @return \self + */ + public function setBillingMonth(string $billingMonth): self + { + $this->billingMonth = $billingMonth; + + return $this; + } + + /** + * @param int $billingYear + * + * @return \self + */ + public function setBillingYear(int $billingYear): self + { + $this->billingYear = $billingYear; + + return $this; + } + + /** + * @return string + */ + public function getBillingMonth(): string + { + return $this->billingMonth; + } + + /** + * @return int + */ + public function getBillingYear(): int + { + return $this->billingYear; + } +} diff --git a/src/Api/Monetization/Structure/Reports/Criteria/AbstractCriteria.php b/src/Api/Monetization/Structure/Reports/Criteria/AbstractCriteria.php new file mode 100644 index 00000000..5d5ecb4b --- /dev/null +++ b/src/Api/Monetization/Structure/Reports/Criteria/AbstractCriteria.php @@ -0,0 +1,358 @@ +apps; + } + + /** + * @param string ...$appIds + * + * @return \self + */ + public function apps(string ...$appIds): self + { + $this->apps = $appIds; + + return $this; + } + + /** + * @return string[] + */ + public function getCurrencies(): array + { + return $this->currencies; + } + + /** + * @param string ...$currencyIds + * + * @return \self + */ + public function currencies(string ...$currencyIds): self + { + $this->currencies = $currencyIds; + + return $this; + } + + /** + * @return string|null + */ + public function getCurrencyOption(): ?string + { + return $this->currencyOption; + } + + /** + * @param string|null $currencyOption + * + * @return \self + */ + public function currencyOption(?string $currencyOption): self + { + // This tweak allows to reset the previously configured currency option + // by calling this method with an empty string or null. + $this->currencyOption = $currencyOption; + + return $this; + } + + /** + * @param string ...$developerIds + * + * @return \self + */ + public function developers(string ...$developerIds): self + { + $this->developers = $developerIds; + + return $this; + } + + /** + * @return array + */ + public function getDevelopers(): array + { + return $this->developers; + } + + /** + * @return string[] + */ + public function getApiPackages(): array + { + return $this->apiPackages; + } + + /** + * @param string ...$apiPackageIds + * + * @return \self + */ + public function apiPackages(string ...$apiPackageIds): self + { + $this->apiPackages = $apiPackageIds; + + return $this; + } + + /** + * @return string[] + */ + public function getApiProducts(): array + { + return $this->apiProducts; + } + + /** + * @param string ...$apiProductIds + * + * @return \self + */ + public function apiProducts(string ...$apiProductIds): self + { + $this->apiProducts = $apiProductIds; + + return $this; + } + + /** + * @return string[] + */ + public function getPricingTypes(): array + { + return $this->pricingTypes; + } + + /** + * @param string ...$pricingTypes + * + * @return \self + */ + public function pricingTypes(string ...$pricingTypes): self + { + $this->pricingTypes = $pricingTypes; + + return $this; + } + + /** + * @return string[] + */ + public function getRatePlanLevels(): array + { + return $this->ratePlanLevels; + } + + /** + * @param string ...$ratePlanLevels + * + * @return \self + */ + public function ratePlanLevels(string ...$ratePlanLevels): self + { + $this->ratePlanLevels = $ratePlanLevels; + + return $this; + } + + /** + * @return bool + */ + public function getShowRevenueSharePercentage(): bool + { + return $this->showRevenueSharePercentage; + } + + /** + * @return bool + */ + public function getShowSummary(): bool + { + return $this->showSummary; + } + + /** + * @return bool + */ + public function getShowTransactionDetail(): bool + { + return $this->showTransactionDetail; + } + + /** + * @return bool + */ + public function getShowTransactionType(): bool + { + return $this->showTransactionType; + } + + /** + * @param bool $show + * + * @return \self + */ + public function showRevenueSharePercentage(bool $show): self + { + $this->showRevenueSharePercentage = $show; + + return $this; + } + + /** + * @param bool $show + * + * @return \self + */ + public function showSummary(bool $show): self + { + $this->showSummary = $show; + + return $this; + } + + /** + * @param bool $show + * + * @return \self + */ + public function showTransactionDetail(bool $show): self + { + $this->showTransactionDetail = $show; + + return $this; + } + + /** + * @param bool $show + * + * @return \self + */ + public function showTransactionType(bool $show): self + { + $this->showTransactionType = $show; + + return $this; + } +} diff --git a/src/Api/Monetization/Structure/Reports/Criteria/BillingReportCriteria.php b/src/Api/Monetization/Structure/Reports/Criteria/BillingReportCriteria.php new file mode 100644 index 00000000..cb0c2c47 --- /dev/null +++ b/src/Api/Monetization/Structure/Reports/Criteria/BillingReportCriteria.php @@ -0,0 +1,25 @@ +groupBy; + } + + /** + * @param string ...$groupBy + * + * @return \self + */ + public function groupBy(string ...$groupBy): self + { + $this->groupBy = $groupBy; + + return $this; + } +} diff --git a/src/Api/Monetization/Structure/Reports/Criteria/PrepaidBalanceReportCriteria.php b/src/Api/Monetization/Structure/Reports/Criteria/PrepaidBalanceReportCriteria.php new file mode 100644 index 00000000..81198a50 --- /dev/null +++ b/src/Api/Monetization/Structure/Reports/Criteria/PrepaidBalanceReportCriteria.php @@ -0,0 +1,23 @@ +fromDate = $fromDate; + $this->endDate = $endDate; + } + + /** + * @return \DateTimeImmutable + */ + public function getFromDate(): \DateTimeImmutable + { + return $this->fromDate; + } + + /** + * @return \DateTimeImmutable|null + */ + public function getEndDate(): ?\DateTimeImmutable + { + return $this->endDate; + } + + /** + * @return string[] + */ + public function getDeveloperAttributes(): array + { + return $this->developerAttributes; + } + + /** + * @param string ...$developerAttributes + * return \self + */ + public function developerAttributes(string ...$developerAttributes): self + { + $this->developerAttributes = $developerAttributes; + + return $this; + } + + /** + * @return string[] + */ + public function getTransactionAttributes(): array + { + return $this->transactionAttributes; + } + + /** + * @param string ...$transactionAttributes + * + * @return \self + */ + public function transactionAttributes(string ...$transactionAttributes): self + { + $this->transactionAttributes = $transactionAttributes; + + return $this; + } +} diff --git a/src/Api/Monetization/Structure/Reports/Criteria/TransactionTypesCriteriaTrait.php b/src/Api/Monetization/Structure/Reports/Criteria/TransactionTypesCriteriaTrait.php new file mode 100644 index 00000000..c1a074f6 --- /dev/null +++ b/src/Api/Monetization/Structure/Reports/Criteria/TransactionTypesCriteriaTrait.php @@ -0,0 +1,47 @@ +transactionTypes; + } + + /** + * @param string ...$transactionTypes + * + * @return \self + */ + public function transactionTypes(string ...$transactionTypes): self + { + $this->transactionTypes = $transactionTypes; + + return $this; + } +} diff --git a/src/Api/Monetization/Utility/ReportTypeFromCriteriaHelperTrait.php b/src/Api/Monetization/Utility/ReportTypeFromCriteriaHelperTrait.php new file mode 100644 index 00000000..cb79c60f --- /dev/null +++ b/src/Api/Monetization/Utility/ReportTypeFromCriteriaHelperTrait.php @@ -0,0 +1,61 @@ +{$setter}(); - } else { - $objectOrArray->{$setter}(...$value); - } - } catch (\TypeError $typeError) { - self::processTypeErrorOnSetValue($typeError->getMessage(), $typeError->getTrace(), 0); - - // Rethrow the exception if it could not be transformed - // to an invalid argument exception. - throw $typeError; + $setter = null; + // Support setPropertyName() and propertyName() setters. + foreach (['set' . ucfirst((string) $propertyPath), (string) $propertyPath] as $methodName) { + if (method_exists($objectOrArray, $methodName)) { + $setter = $methodName; + break; } - } else { + } + + if (null === $setter) { throw new AccessException("Setter method not found for {$propertyPath} property.", 0, $exception); } + + try { + if (empty($value)) { + // Clear the value of the property. + $objectOrArray->{$setter}(); + } else { + $objectOrArray->{$setter}(...$value); + } + } catch (\TypeError $typeError) { + self::processTypeErrorOnSetValue($typeError->getMessage(), $typeError->getTrace(), 0); + + // Rethrow the exception if it could not be transformed + // to an invalid argument exception. + throw $typeError; + } } else { throw $exception; } diff --git a/src/Serializer/EntitySerializer.php b/src/Serializer/EntitySerializer.php index 603db57e..95600ffd 100755 --- a/src/Serializer/EntitySerializer.php +++ b/src/Serializer/EntitySerializer.php @@ -185,9 +185,9 @@ public function setPropertiesFromResponse(ResponseInterface $response, EntityInt /** * @inheritdoc */ - public function decode($data, $format, array $context = []): void + public function decode($data, $format, array $context = []) { - $this->serializer->decode($data, $format, $context = []); + return $this->serializer->decode($data, $format, $context); } /** diff --git a/src/Structure/BaseObject.php b/src/Structure/BaseObject.php index 2587185f..277543e8 100644 --- a/src/Structure/BaseObject.php +++ b/src/Structure/BaseObject.php @@ -23,6 +23,8 @@ */ abstract class BaseObject { + use ObjectCopyHelperTrait; + /** * BaseObject constructor. * @@ -59,42 +61,4 @@ public function __construct(array $values = []) } } } - - /** - * Deep clone for objects. - * - * Inspired by https://github.com/kore/DataObject/blob/master/src/Kore/DataObject/DataObject.php - */ - public function __clone() - { - $ro = new \ReflectionObject($this); - foreach ($ro->getProperties() as $property) { - $property->setAccessible(true); - $value = $property->getValue($this); - if (is_object($value)) { - $property->setValue($this, clone $value); - } - if (is_array($value)) { - $this->cloneArray($value); - $property->setValue($this, $value); - } - } - } - - /** - * Deep clone for arrays. - * - * @param array $array - */ - private function cloneArray(array &$array): void - { - foreach ($array as $key => $value) { - if (is_object($value)) { - $array[$key] = clone $value; - } - if (is_array($value)) { - $this->cloneArray($array[$key]); - } - } - } } diff --git a/src/Structure/ObjectCopyHelperTrait.php b/src/Structure/ObjectCopyHelperTrait.php new file mode 100644 index 00000000..2598cd67 --- /dev/null +++ b/src/Structure/ObjectCopyHelperTrait.php @@ -0,0 +1,60 @@ +getProperties() as $property) { + $property->setAccessible(true); + $value = $property->getValue($this); + if (is_object($value)) { + $property->setValue($this, clone $value); + } + if (is_array($value)) { + $this->cloneArray($value); + $property->setValue($this, $value); + } + } + } + + /** + * Deep clone for arrays. + * + * @param array $array + */ + private function cloneArray(array &$array): void + { + foreach ($array as $key => $value) { + if (is_object($value)) { + $array[$key] = clone $value; + } + if (is_array($value)) { + $this->cloneArray($array[$key]); + } + } + } +} diff --git a/tests/Api/Management/Controller/ApiProductControllerTest.php b/tests/Api/Management/Controller/ApiProductControllerTest.php index 3e26a786..f247020e 100644 --- a/tests/Api/Management/Controller/ApiProductControllerTest.php +++ b/tests/Api/Management/Controller/ApiProductControllerTest.php @@ -24,7 +24,7 @@ use Apigee\Edge\Tests\Test\Controller\DefaultAPIClientAwareTrait; use Apigee\Edge\Tests\Test\Controller\EntityControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTester; -use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTraitTest; +use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationTestControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityDeleteOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityLoadOperationControllerTestTrait; @@ -43,7 +43,7 @@ class ApiProductControllerTest extends EntityControllerTestBase use ApiProductTestEntityProviderTrait; use DefaultAPIClientAwareTrait; // The order of these trait matters. Check @depends in test methods. - use EntityCreateOperationControllerTraitTest; + use EntityCreateOperationControllerTestTrait; use EntityLoadOperationControllerTestTrait; use EntityUpdateOperationControllerTestTrait; use EntityDeleteOperationControllerTestTrait; diff --git a/tests/Api/Management/Controller/AppControllerTestBase.php b/tests/Api/Management/Controller/AppControllerTestBase.php index c74b772e..9a0994ba 100644 --- a/tests/Api/Management/Controller/AppControllerTestBase.php +++ b/tests/Api/Management/Controller/AppControllerTestBase.php @@ -23,7 +23,7 @@ use Apigee\Edge\Tests\Api\Management\EntitySerializer\AppSerializerValidator; use Apigee\Edge\Tests\Test\Controller\DefaultAPIClientAwareTrait; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTester; -use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTraitTest; +use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationTestControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityDeleteOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityLoadOperationControllerTestTrait; @@ -41,7 +41,7 @@ abstract class AppControllerTestBase extends EntityControllerTestBase use ApiProductTestEntityProviderTrait; use DefaultAPIClientAwareTrait; // The order of these trait matters. Check @depends in test methods. - use EntityCreateOperationControllerTraitTest; + use EntityCreateOperationControllerTestTrait; use EntityLoadOperationControllerTestTrait; use EntityUpdateOperationControllerTestTrait; use EntityDeleteOperationControllerTestTrait; diff --git a/tests/Api/Management/Controller/CompanyControllerTest.php b/tests/Api/Management/Controller/CompanyControllerTest.php index 09762b7e..39cab72e 100644 --- a/tests/Api/Management/Controller/CompanyControllerTest.php +++ b/tests/Api/Management/Controller/CompanyControllerTest.php @@ -25,7 +25,7 @@ use Apigee\Edge\Tests\Test\Controller\DefaultAPIClientAwareTrait; use Apigee\Edge\Tests\Test\Controller\EntityControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTester; -use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTraitTest; +use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationTestControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityDeleteOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityLoadOperationControllerTestTrait; @@ -46,7 +46,7 @@ class CompanyControllerTest extends EntityControllerTestBase use DefaultAPIClientAwareTrait; use MarkOnlineTestSkippedAwareTrait; // The order of these trait matters. Check @depends in test methods. - use EntityCreateOperationControllerTraitTest; + use EntityCreateOperationControllerTestTrait; use EntityLoadOperationControllerTestTrait; use EntityUpdateOperationControllerTestTrait; use EntityDeleteOperationControllerTestTrait; diff --git a/tests/Api/Management/Controller/DeveloperControllerTest.php b/tests/Api/Management/Controller/DeveloperControllerTest.php index 28eb59a7..cc23faa2 100644 --- a/tests/Api/Management/Controller/DeveloperControllerTest.php +++ b/tests/Api/Management/Controller/DeveloperControllerTest.php @@ -26,7 +26,7 @@ use Apigee\Edge\Tests\Test\Controller\DefaultAPIClientAwareTrait; use Apigee\Edge\Tests\Test\Controller\EntityControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTester; -use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTraitTest; +use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationTestControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityDeleteOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityLoadOperationControllerTestTrait; @@ -50,7 +50,7 @@ class DeveloperControllerTest extends EntityControllerTestBase use MarkOnlineTestSkippedAwareTrait; use MockClientAwareTrait; // The order of these trait matters. Check @depends in test methods. - use EntityCreateOperationControllerTraitTest; + use EntityCreateOperationControllerTestTrait; use EntityLoadOperationControllerTestTrait; use EntityUpdateOperationControllerTestTrait; use EntityDeleteOperationControllerTestTrait; diff --git a/tests/Api/Monetization/Controller/CompanyReportDefinitionControllerTest.php b/tests/Api/Monetization/Controller/CompanyReportDefinitionControllerTest.php new file mode 100644 index 00000000..82d0e8b4 --- /dev/null +++ b/tests/Api/Monetization/Controller/CompanyReportDefinitionControllerTest.php @@ -0,0 +1,53 @@ +getMockHttpClient(); + $accepted_content_type = 'application/octet-stream'; + $httpClient->setDefaultResponse(new Response(200, ['Content-Type' => $accepted_content_type], 'foo,bar,baz')); + /** @var \Apigee\Edge\Api\Monetization\Controller\ReportDefinitionControllerInterface $controller */ + $controller = static::entityController(static::mockApiClient()); + $controller->generateReport(static::getNewReportDefinition()->getCriteria()); + $this->assertEquals($accepted_content_type, static::mockApiClient()->getJournal()->getLastRequest()->getHeaderLine('Accept')); + $this->assertEquals("/v1/mint/organizations/{$test_org}/billing-reports", static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getPath()); + $controller->generateReport(new PrepaidBalanceReportCriteria('JANUARY', 2019)); + $this->assertEquals("/v1/mint/organizations/{$test_org}/prepaid-balance-reports", static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getPath()); + $controller->generateReport(new RevenueReportCriteria(new \DateTimeImmutable('yesterday'), new \DateTimeImmutable('now'))); + $this->assertEquals("/v1/mint/organizations/{$test_org}/revenue-reports", static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getPath()); + } + + /** + * @expectedException \Apigee\Edge\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to identify report type. + */ + public function testGenerateReportWithUnknownCriteria(): void + { + $class = new class() extends AbstractCriteria { + }; + /** @var \Apigee\Edge\Api\Monetization\Controller\ReportDefinitionControllerInterface $controller */ + $controller = static::entityController(static::mockApiClient()); + $controller->generateReport(new $class()); + } + + public function testFilteredEntities(): void + { + /** @var \Apigee\Edge\Tests\Test\HttpClient\MockHttpClient $httpClient */ + $httpClient = static::mockApiClient()->getMockHttpClient(); + $httpClient->setDefaultResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode((object) [[]]))); + /** @var \Apigee\Edge\Api\Monetization\Controller\ReportDefinitionControllerInterface $controller */ + $controller = static::entityController(static::mockApiClient()); + $controller->getFilteredEntities(); + $this->assertEquals($this->expectedFilteredReportDefinitionUrl(), static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getPath()); + $this->assertEquals('page=1', static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getQuery()); + $controller->getFilteredEntities(1, 2); + $this->assertEquals('page=2&size=1', static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getQuery()); + $controller->getFilteredEntities(1, 2, 'fooo'); + $this->assertEquals('page=2&size=1&sort=fooo', static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getQuery()); + } + + abstract protected function expectedFilteredReportDefinitionUrl(): string; + + /** + * @inheritDoc + */ + protected function getEntitySerializer(): EntitySerializerInterface + { + static $validator; + if (null === $validator) { + $validator = new ReportDefinitionSerializer($this->entitySerializer()); + } + + return $validator; + } + + /** + * @inheritDoc + */ + protected function entitySerializerValidator(): EntitySerializerValidatorInterface + { + static $validator; + if (null === $validator) { + $validator = new ReportDefinitionSerializerValidator($this->entitySerializer()); + } + + return $validator; + } + + /** + * @inheritDoc + */ + protected static function entityCreateOperationTestController(): EntityCreateOperationTestControllerTesterInterface + { + return new EntityCreateOperationControllerTester(static::entityController()); + } + + /** + * @inheritDoc + */ + protected static function getNewEntity(): EntityInterface + { + return static::getNewReportDefinition(!TestClientFactory::isOfflineClient(static::defaultAPIClient())); + } + + /** + * @inheritDoc + */ + protected function entityForUpdateTest(EntityInterface $existing): EntityInterface + { + return static::getUpdatedReportDefinition($existing, !TestClientFactory::isOfflineClient(static::defaultAPIClient())); + } +} diff --git a/tests/Api/Monetization/Controller/SupportedCurrencyControllerTest.php b/tests/Api/Monetization/Controller/SupportedCurrencyControllerTest.php index dc1ae80a..2ff5bca7 100644 --- a/tests/Api/Monetization/Controller/SupportedCurrencyControllerTest.php +++ b/tests/Api/Monetization/Controller/SupportedCurrencyControllerTest.php @@ -25,7 +25,7 @@ use Apigee\Edge\Tests\Test\Controller\EntityControllerTester; use Apigee\Edge\Tests\Test\Controller\EntityControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTester; -use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTraitTest; +use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationTestControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityUpdateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\TestClientFactory; @@ -40,7 +40,7 @@ class SupportedCurrencyControllerTest extends EntityControllerTestBase { use SupportedCurrencyEntityProviderTrait; // The order of these trait matters. Check @depends in test methods. - use EntityCreateOperationControllerTraitTest; + use EntityCreateOperationControllerTestTrait; use EntityLoadOperationControllerTestTrait; use EntityUpdateOperationControllerTestTrait; use EntityDeleteOperationControllerTestTrait; diff --git a/tests/Api/Monetization/Controller/TermsAndConditionsControllerTest.php b/tests/Api/Monetization/Controller/TermsAndConditionsControllerTest.php index 6d2f77ba..6e38cd6f 100644 --- a/tests/Api/Monetization/Controller/TermsAndConditionsControllerTest.php +++ b/tests/Api/Monetization/Controller/TermsAndConditionsControllerTest.php @@ -26,7 +26,7 @@ use Apigee\Edge\Tests\Test\Controller\EntityControllerTester; use Apigee\Edge\Tests\Test\Controller\EntityControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTester; -use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTraitTest; +use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\EntityCreateOperationTestControllerTesterInterface; use Apigee\Edge\Tests\Test\Controller\EntityUpdateOperationControllerTestTrait; use Apigee\Edge\Tests\Test\Controller\MockClientAwareTrait; @@ -44,7 +44,7 @@ class TermsAndConditionsControllerTest extends EntityControllerTestBase use TermsAndConditionsEntityProviderTrait; use MockClientAwareTrait; // The order of these trait matters. Check @depends in test methods. - use EntityCreateOperationControllerTraitTest; + use EntityCreateOperationControllerTestTrait; use EntityLoadOperationControllerTestTrait; use EntityUpdateOperationControllerTestTrait; use EntityDeleteOperationControllerTestTrait; @@ -60,7 +60,7 @@ public function testGetEntities(): void $controller->getEntities(); $this->assertEquals('all=true', static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getQuery()); $controller->getEntities(true); - $this->assertEquals('current=true&all=true', static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getQuery()); + $this->assertEquals('all=true¤t=true', static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getQuery()); $controller->getPaginatedEntityList(); $this->assertEquals('page=1', static::mockApiClient()->getJournal()->getLastRequest()->getUri()->getQuery()); $controller->getPaginatedEntityList(1, 2, true); @@ -70,7 +70,7 @@ public function testGetEntities(): void } /** - * @inheritdoc + * @inheritDoc */ protected static function entityController(ClientInterface $client = null): EntityControllerTesterInterface { @@ -80,7 +80,7 @@ protected static function entityController(ClientInterface $client = null): Enti } /** - * @inheritdoc + * @inheritDoc */ protected static function entityCreateOperationTestController(): EntityCreateOperationTestControllerTesterInterface { @@ -88,7 +88,7 @@ protected static function entityCreateOperationTestController(): EntityCreateOpe } /** - * @inheritdoc + * @inheritDoc */ protected static function getNewEntity(): EntityInterface { @@ -96,7 +96,7 @@ protected static function getNewEntity(): EntityInterface } /** - * @inheritdoc + * @inheritDoc */ protected function entityForUpdateTest(EntityInterface $existing): EntityInterface { @@ -104,7 +104,7 @@ protected function entityForUpdateTest(EntityInterface $existing): EntityInterfa } /** - * @inheritdoc + * @inheritDoc */ protected function getTestEntityForTimezoneConversion(): MintEntityInterface { diff --git a/tests/Api/Monetization/Denormalizer/ReportCriteriaDenormalizerTest.php b/tests/Api/Monetization/Denormalizer/ReportCriteriaDenormalizerTest.php new file mode 100644 index 00000000..5783584e --- /dev/null +++ b/tests/Api/Monetization/Denormalizer/ReportCriteriaDenormalizerTest.php @@ -0,0 +1,113 @@ +setSerializer(new Serializer([new DateTimeNormalizer([DateTimeNormalizer::FORMAT_KEY => EntityInterface::DATE_FORMAT])])); + } + + /** + * @expectedException \Error + */ + public function testDenormalizeWithAbtractClassNoContext(): void + { + static::$denormalizer->denormalize((object) [], AbstractCriteria::class, 'json'); + } + + /** + * @expectedException \Apigee\Edge\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid report definition type: foo. + */ + public function testDenormalizeWithAbstractClassUnknownReportType(): void + { + static::$denormalizer->denormalize((object) [], AbstractCriteria::class, 'json', [ReportCriteriaDenormalizer::CONTEXT_REPORT_DEFINITION_TYPE => 'foo']); + } + + /** + * @dataProvider denormalizeWithTypeDataProvider + */ + public function testDenormalizeWithKnownTypes(string $reportType, string $expectedClass, array $constructArgs): void + { + $this->assertInstanceOf($expectedClass, static::$denormalizer->denormalize((object) $constructArgs, AbstractCriteria::class, 'json', [ReportCriteriaDenormalizer::CONTEXT_REPORT_DEFINITION_TYPE => $reportType])); + } + + public function testDataDenormalization(): void + { + $data = (object) [ + 'billingMonth' => 'JANUARY', 'billingYear' => 2019, + 'pkgCriteria' => (object) [ + [ + 'id' => 'fooPkg', + 'orgId' => 'orgId', + ], + [ + 'id' => 'barPkg', + 'orgId' => 'orgId', + ], + ], + 'monetizationPackageIds' => ['barPkg', 'bazPkg'], + 'prodCriteria' => (object) [ + [ + 'id' => 'fooProd', + 'orgId' => 'orgId', + ], + [ + 'id' => 'barProd', + 'orgId' => 'orgId', + ], + ], + 'productIds' => ['barProd', 'bazProd'], + ]; + + $obj = static::$denormalizer->denormalize($data, AbstractCriteria::class, 'json', [ReportCriteriaDenormalizer::CONTEXT_REPORT_DEFINITION_TYPE => ReportDefinitionInterface::TYPE_BILLING]); + $this->assertEquals(['barPkg', 'bazPkg', 'fooPkg'], $obj->getApiPackages()); + $this->assertEquals(['barProd', 'bazProd', 'fooProd'], $obj->getApiProducts()); + } + + public function denormalizeWithTypeDataProvider(): array + { + return [ + [ReportDefinitionInterface::TYPE_BILLING, BillingReportCriteria::class, ['billingMonth' => 'JANUARY', 'billingYear' => 2019]], + [ReportDefinitionInterface::TYPE_PREPAID_BALANCE, PrepaidBalanceReportCriteria::class, ['billingMonth' => 'JANUARY', 'billingYear' => 2019]], + [ReportDefinitionInterface::TYPE_REVENUE, RevenueReportCriteria::class, ['fromDate' => '2015-07-01 00:00:00', 'toDate' => '2015-08-01 13:35:00']], + ]; + } +} diff --git a/tests/Api/Monetization/Entity/ReportDefinitionEntityProviderTrait.php b/tests/Api/Monetization/Entity/ReportDefinitionEntityProviderTrait.php new file mode 100644 index 00000000..c16262ab --- /dev/null +++ b/tests/Api/Monetization/Entity/ReportDefinitionEntityProviderTrait.php @@ -0,0 +1,55 @@ +groupBy('PACKAGE', 'PRODUCT', 'DEVELOPER')->ratePlanLevels('STANDARD', 'DEVELOPER')->developers('developer@example.com'); + $entity = new ReportDefinition([ + 'name' => $randomData ? static::randomGenerator()->machineName() : 'PHPUnit', + 'description' => $randomData ? static::randomGenerator()->text() : 'test report definition provider', + 'criteria' => $criteria, + ]); + + return $entity; + } + + protected static function getUpdatedReportDefinition(ReportDefinitionInterface $existing, bool $randomData = true): ReportDefinitionInterface + { + $updated = clone $existing; + $updated->setDescription($randomData ? static::randomGenerator()->text() : '(edited) test report definition provider'); + /** @var \Apigee\Edge\Api\Monetization\Structure\Reports\Criteria\BillingReportCriteria $criteria */ + $criteria = $updated->getCriteria(); + $criteria->setBillingMonth('FEBRUARY'); + $criteria->showSummary(!$criteria->getShowSummary()); + $updated->setCriteria($criteria); + + return $updated; + } +} diff --git a/tests/Api/Monetization/EntitySerializer/ReportDefinitionSerializerValidator.php b/tests/Api/Monetization/EntitySerializer/ReportDefinitionSerializerValidator.php new file mode 100644 index 00000000..3ee3ccd6 --- /dev/null +++ b/tests/Api/Monetization/EntitySerializer/ReportDefinitionSerializerValidator.php @@ -0,0 +1,36 @@ +developer); + parent::validate($input, $entity); + } +} diff --git a/tests/PropertyAccess/PropertyAccessorDecoratorTest.php b/tests/PropertyAccess/PropertyAccessorDecoratorTest.php index 73429e6d..0f5b2b6f 100644 --- a/tests/PropertyAccess/PropertyAccessorDecoratorTest.php +++ b/tests/PropertyAccess/PropertyAccessorDecoratorTest.php @@ -48,6 +48,9 @@ public static function setUpBeforeClass(): void /** @var string */ private $shouldBeAString; + /** @var string */ + private $queryBuilderParam; + public function __construct() { // Fake invalid value. @@ -85,6 +88,22 @@ public function setShouldBeAString(string $shouldBeAString): void { $this->shouldBeAString = $shouldBeAString; } + + /** + * @return string + */ + public function getQueryBuilderParam(): string + { + return $this->queryBuilderParam; + } + + /** + * @param string $queryBuilderParam + */ + public function queryBuilderParam(string $queryBuilderParam): void + { + $this->queryBuilderParam = $queryBuilderParam; + } }; } @@ -146,6 +165,12 @@ public function testSetValueWithInvalidVariableLengthArgs(string $property, $val } } + public function testSetValueOnQueryBuilderParameter(): void + { + $this->propertyAccessor->setValue(static::$testObj, 'queryBuilderParam', 'foo'); + $this->assertEquals('foo', static::$testObj->getQueryBuilderParam()); + } + public function exceptionsToGetOnGetValue(): array { return [ diff --git a/tests/Test/Controller/EntityCreateOperationControllerTestTrait.php b/tests/Test/Controller/EntityCreateOperationControllerTestTrait.php index 83351462..10a316ab 100644 --- a/tests/Test/Controller/EntityCreateOperationControllerTestTrait.php +++ b/tests/Test/Controller/EntityCreateOperationControllerTestTrait.php @@ -29,7 +29,7 @@ * * @see \Apigee\Edge\Controller\EntityCreateOperationControllerInterface */ -trait EntityCreateOperationControllerTraitTest +trait EntityCreateOperationControllerTestTrait { use EntityControllerAwareTestTrait; use EntitySerializerAwareTestTrait; diff --git a/tests/offline-test-data/v1/mint/organizations/phpunit/companies/phpunit/report-definitions/POST.json b/tests/offline-test-data/v1/mint/organizations/phpunit/companies/phpunit/report-definitions/POST.json new file mode 100644 index 00000000..50a9c1c5 --- /dev/null +++ b/tests/offline-test-data/v1/mint/organizations/phpunit/companies/phpunit/report-definitions/POST.json @@ -0,0 +1,118 @@ +{ + "description": "test report definition provider", + "developer": { + "approxTaxRate": 123.4567, + "billingType": "PREPAID", + "broker": false, + "developerCategory": { + "description": "Test developer category.", + "id": "312004df-fc1e-4bc3-8ae0-f7a9652c1ac2", + "name": "PHPUnit" + }, + "email": "phpunit###phpunit@APIGEE-GEN.FOR-COMPANY", + "id": "f67ea7a0-0839-4057-84fc-40a1a38a44e1", + "isCompany": true, + "name": "PHPUnit", + "organization": { + "approveTrusted": false, + "approveUntrusted": false, + "billingCycle": "CALENDAR_MONTH", + "country": "US", + "currency": "USD", + "description": "phpunit-description", + "hasBillingAdjustment": false, + "hasBroker": false, + "hasSelfBilling": false, + "hasSeparateInvoiceForProduct": false, + "id": "phpunit", + "issueNettingStmt": false, + "name": "name", + "nettingStmtPerCurrency": false, + "regNo": "123456-regno", + "selfBillingAsExchOrg": false, + "selfBillingForAllDev": false, + "separateInvoiceForFees": false, + "status": "ACTIVE", + "supportedBillingType": "BOTH", + "taxEngineExternalId": "APIGEE", + "taxModel": "HYBRID", + "transactionRelayURL": "http://localhost", + "taxRegNo": "12345", + "timezone": "Australia/Eucla", + "logoUrl": "http://example.com" + }, + "status": "ACTIVE", + "type": "UNTRUSTED", + "taxExemptAuthNo": "taxExemptAuthNo" + }, + "id": "phpunit", + "lastModified": "2019-04-08 09:50:17", + "mintCriteria": { + "billingMonth": "JANUARY", + "billingYear": 2019, + "currencyOption": "LOCAL", + "devCriteria": [ + { + "id": "developer@example.com", + "orgId": "phpunit" + } + ], + "groupBy": [ + "PACKAGE", + "PRODUCT", + "DEVELOPER" + ], + "monetizationPackageIds": [ + "phpunit" + ], + "ratePlanLevels": [ + "STANDARD", + "DEVELOPER" + ], + "showRevSharePct": false, + "showSummary": false, + "showTxDetail": false, + "showTxType": false, + "transactionTypes": [ + "PURCHASE", + "REFUND", + "CREDIT", + "BALANCE", + "SETUPFEES", + "TERMINATIONFEES", + "RECURRINGFEES", + "TRUEUP", + "PROVIDER_AUDIT" + ] + }, + "name": "PHPUnit", + "organization" : { + "approveTrusted": false, + "approveUntrusted": false, + "billingCycle": "CALENDAR_MONTH", + "country": "US", + "currency": "USD", + "description": "phpunit-description", + "hasBillingAdjustment": false, + "hasBroker": false, + "hasSelfBilling": false, + "hasSeparateInvoiceForProduct": false, + "id": "phpunit", + "issueNettingStmt": false, + "name": "name", + "nettingStmtPerCurrency": false, + "regNo": "123456-regno", + "selfBillingAsExchOrg": false, + "selfBillingForAllDev": false, + "separateInvoiceForFees": false, + "status": "ACTIVE", + "supportedBillingType": "BOTH", + "taxEngineExternalId": "APIGEE", + "taxModel": "HYBRID", + "transactionRelayURL": "http://localhost", + "taxRegNo": "12345", + "timezone": "Australia/Eucla", + "logoUrl": "http://example.com" + }, + "type": "BILLING" +} diff --git a/tests/offline-test-data/v1/mint/organizations/phpunit/developers/phpunit@example.com/report-definitions/POST.json b/tests/offline-test-data/v1/mint/organizations/phpunit/developers/phpunit@example.com/report-definitions/POST.json new file mode 100644 index 00000000..5082f27f --- /dev/null +++ b/tests/offline-test-data/v1/mint/organizations/phpunit/developers/phpunit@example.com/report-definitions/POST.json @@ -0,0 +1,118 @@ +{ + "description": "test report definition provider", + "developer": { + "approxTaxRate": 123.4567, + "billingType": "PREPAID", + "broker": false, + "developerCategory": { + "description": "Test developer category.", + "id": "312004df-fc1e-4bc3-8ae0-f7a9652c1ac2", + "name": "PHPUnit" + }, + "email": "phpunit@example.com", + "id": "f67ea7a0-0839-4057-84fc-40a1a38a44e1", + "isCompany": false, + "name": "PHPUnit", + "organization": { + "approveTrusted": false, + "approveUntrusted": false, + "billingCycle": "CALENDAR_MONTH", + "country": "US", + "currency": "USD", + "description": "phpunit-description", + "hasBillingAdjustment": false, + "hasBroker": false, + "hasSelfBilling": false, + "hasSeparateInvoiceForProduct": false, + "id": "phpunit", + "issueNettingStmt": false, + "name": "name", + "nettingStmtPerCurrency": false, + "regNo": "123456-regno", + "selfBillingAsExchOrg": false, + "selfBillingForAllDev": false, + "separateInvoiceForFees": false, + "status": "ACTIVE", + "supportedBillingType": "BOTH", + "taxEngineExternalId": "APIGEE", + "taxModel": "HYBRID", + "transactionRelayURL": "http://localhost", + "taxRegNo": "12345", + "timezone": "Australia/Eucla", + "logoUrl": "http://example.com" + }, + "status": "ACTIVE", + "type": "UNTRUSTED", + "taxExemptAuthNo": "taxExemptAuthNo" + }, + "id": "phpunit", + "lastModified": "2019-04-08 09:50:17", + "mintCriteria": { + "billingMonth": "JANUARY", + "billingYear": 2019, + "currencyOption": "LOCAL", + "devCriteria": [ + { + "id": "developer@example.com", + "orgId": "phpunit" + } + ], + "groupBy": [ + "PACKAGE", + "PRODUCT", + "DEVELOPER" + ], + "monetizationPackageIds": [ + "phpunit" + ], + "ratePlanLevels": [ + "STANDARD", + "DEVELOPER" + ], + "showRevSharePct": false, + "showSummary": false, + "showTxDetail": false, + "showTxType": false, + "transactionTypes": [ + "PURCHASE", + "REFUND", + "CREDIT", + "BALANCE", + "SETUPFEES", + "TERMINATIONFEES", + "RECURRINGFEES", + "TRUEUP", + "PROVIDER_AUDIT" + ] + }, + "name": "PHPUnit", + "organization" : { + "approveTrusted": false, + "approveUntrusted": false, + "billingCycle": "CALENDAR_MONTH", + "country": "US", + "currency": "USD", + "description": "phpunit-description", + "hasBillingAdjustment": false, + "hasBroker": false, + "hasSelfBilling": false, + "hasSeparateInvoiceForProduct": false, + "id": "phpunit", + "issueNettingStmt": false, + "name": "name", + "nettingStmtPerCurrency": false, + "regNo": "123456-regno", + "selfBillingAsExchOrg": false, + "selfBillingForAllDev": false, + "separateInvoiceForFees": false, + "status": "ACTIVE", + "supportedBillingType": "BOTH", + "taxEngineExternalId": "APIGEE", + "taxModel": "HYBRID", + "transactionRelayURL": "http://localhost", + "taxRegNo": "12345", + "timezone": "Australia/Eucla", + "logoUrl": "http://example.com" + }, + "type": "BILLING" +} diff --git a/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/POST.json b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/POST.json new file mode 100644 index 00000000..3ed35cbc --- /dev/null +++ b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/POST.json @@ -0,0 +1,73 @@ +{ + "description": "test report definition provider", + "developer": null, + "id": "phpunit", + "lastModified": "2019-04-08 09:50:17", + "mintCriteria": { + "billingMonth": "JANUARY", + "billingYear": 2019, + "devCriteria": [ + { + "id": "developer@example.com", + "orgId": "phpunit" + } + ], + "groupBy": [ + "PACKAGE", + "PRODUCT", + "DEVELOPER" + ], + "monetizationPackageIds": [ + "phpunit" + ], + "ratePlanLevels": [ + "STANDARD", + "DEVELOPER" + ], + "showRevSharePct": false, + "showSummary": false, + "showTxDetail": false, + "showTxType": false, + "transactionTypes": [ + "PURCHASE", + "REFUND", + "CREDIT", + "BALANCE", + "SETUPFEES", + "TERMINATIONFEES", + "RECURRINGFEES", + "TRUEUP", + "PROVIDER_AUDIT" + ] + }, + "name": "PHPUnit", + "organization" : { + "approveTrusted": false, + "approveUntrusted": false, + "billingCycle": "CALENDAR_MONTH", + "country": "US", + "currency": "USD", + "description": "phpunit-description", + "hasBillingAdjustment": false, + "hasBroker": false, + "hasSelfBilling": false, + "hasSeparateInvoiceForProduct": false, + "id": "phpunit", + "issueNettingStmt": false, + "name": "name", + "nettingStmtPerCurrency": false, + "regNo": "123456-regno", + "selfBillingAsExchOrg": false, + "selfBillingForAllDev": false, + "separateInvoiceForFees": false, + "status": "ACTIVE", + "supportedBillingType": "BOTH", + "taxEngineExternalId": "APIGEE", + "taxModel": "HYBRID", + "transactionRelayURL": "http://localhost", + "taxRegNo": "12345", + "timezone": "Australia/Eucla", + "logoUrl": "http://example.com" + }, + "type": "BILLING" +} diff --git a/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/DELETE.json b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/DELETE.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/DELETE.json @@ -0,0 +1 @@ +{} diff --git a/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/GET.json b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/GET.json new file mode 100644 index 00000000..3ed35cbc --- /dev/null +++ b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/GET.json @@ -0,0 +1,73 @@ +{ + "description": "test report definition provider", + "developer": null, + "id": "phpunit", + "lastModified": "2019-04-08 09:50:17", + "mintCriteria": { + "billingMonth": "JANUARY", + "billingYear": 2019, + "devCriteria": [ + { + "id": "developer@example.com", + "orgId": "phpunit" + } + ], + "groupBy": [ + "PACKAGE", + "PRODUCT", + "DEVELOPER" + ], + "monetizationPackageIds": [ + "phpunit" + ], + "ratePlanLevels": [ + "STANDARD", + "DEVELOPER" + ], + "showRevSharePct": false, + "showSummary": false, + "showTxDetail": false, + "showTxType": false, + "transactionTypes": [ + "PURCHASE", + "REFUND", + "CREDIT", + "BALANCE", + "SETUPFEES", + "TERMINATIONFEES", + "RECURRINGFEES", + "TRUEUP", + "PROVIDER_AUDIT" + ] + }, + "name": "PHPUnit", + "organization" : { + "approveTrusted": false, + "approveUntrusted": false, + "billingCycle": "CALENDAR_MONTH", + "country": "US", + "currency": "USD", + "description": "phpunit-description", + "hasBillingAdjustment": false, + "hasBroker": false, + "hasSelfBilling": false, + "hasSeparateInvoiceForProduct": false, + "id": "phpunit", + "issueNettingStmt": false, + "name": "name", + "nettingStmtPerCurrency": false, + "regNo": "123456-regno", + "selfBillingAsExchOrg": false, + "selfBillingForAllDev": false, + "separateInvoiceForFees": false, + "status": "ACTIVE", + "supportedBillingType": "BOTH", + "taxEngineExternalId": "APIGEE", + "taxModel": "HYBRID", + "transactionRelayURL": "http://localhost", + "taxRegNo": "12345", + "timezone": "Australia/Eucla", + "logoUrl": "http://example.com" + }, + "type": "BILLING" +} diff --git a/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/PUT.json b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/PUT.json new file mode 100644 index 00000000..483cf63d --- /dev/null +++ b/tests/offline-test-data/v1/mint/organizations/phpunit/report-definitions/phpunit/PUT.json @@ -0,0 +1,73 @@ +{ + "description": "(edited) test report definition provider", + "developer": null, + "id": "phpunit", + "lastModified": "2019-04-08 09:50:17", + "mintCriteria": { + "billingMonth": "FEBRUARY", + "billingYear": 2019, + "devCriteria": [ + { + "id": "developer@example.com", + "orgId": "phpunit" + } + ], + "groupBy": [ + "PACKAGE", + "PRODUCT", + "DEVELOPER" + ], + "monetizationPackageIds": [ + "phpunit" + ], + "ratePlanLevels": [ + "STANDARD", + "DEVELOPER" + ], + "showRevSharePct": false, + "showSummary": true, + "showTxDetail": false, + "showTxType": false, + "transactionTypes": [ + "PURCHASE", + "REFUND", + "CREDIT", + "BALANCE", + "SETUPFEES", + "TERMINATIONFEES", + "RECURRINGFEES", + "TRUEUP", + "PROVIDER_AUDIT" + ] + }, + "name": "PHPUnit", + "organization" : { + "approveTrusted": false, + "approveUntrusted": false, + "billingCycle": "CALENDAR_MONTH", + "country": "US", + "currency": "USD", + "description": "phpunit-description", + "hasBillingAdjustment": false, + "hasBroker": false, + "hasSelfBilling": false, + "hasSeparateInvoiceForProduct": false, + "id": "phpunit", + "issueNettingStmt": false, + "name": "name", + "nettingStmtPerCurrency": false, + "regNo": "123456-regno", + "selfBillingAsExchOrg": false, + "selfBillingForAllDev": false, + "separateInvoiceForFees": false, + "status": "ACTIVE", + "supportedBillingType": "BOTH", + "taxEngineExternalId": "APIGEE", + "taxModel": "HYBRID", + "transactionRelayURL": "http://localhost", + "taxRegNo": "12345", + "timezone": "Australia/Eucla", + "logoUrl": "http://example.com" + }, + "type": "BILLING" +}