Skip to content

Commit

Permalink
Feature/upgrade pagination (#34)
Browse files Browse the repository at this point in the history
* Upgrade pagination to v3 for `getAll()`

* Fix styling
  • Loading branch information
timothydc authored Oct 21, 2024
1 parent c820c9a commit f56e185
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .php-cs-fixer.cache
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"php":"8.3.12","version":"3.64.0","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces_position":true,"class_definition":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","do","else","elseif","final","for","foreach","function","if","interface","namespace","private","protected","public","static","switch","trait","try","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["method","property"]},"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"ordered_imports":{"sort_algorithm":"alpha"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"unary_operator_spaces":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Repositories\/TokenRepository.php":"4c7e3cae90c4b82a4af71eb79379f13c","src\/Jobs\/RemoveResourceFromLightspeedRetail.php":"41ced6695106572e195bdcceb437affb","src\/Jobs\/Middleware\/RateLimited.php":"70d1a708795723a8057fe003dd412b36","src\/Jobs\/SendResourceToLightspeedRetail.php":"06a6665dcb710064051010be0d64a747","src\/Interfaces\/AutomaticSynchronisationInterface.php":"6cb6f34adfdc0f3e493f78aa7d94c39f","src\/Interfaces\/TokenInterface.php":"5eec333b8948a0d4600ab72f0d6b267e","src\/Models\/LightspeedRetailResource.php":"db317c4a52377a0b866ec89b38a9d9da","src\/Models\/ApiToken.php":"c887efa1d1ad1c326cb9347ebbf6eff1","src\/LightspeedRetailApiServiceProvider.php":"a574b45dffc99069e530e6f67f396082","src\/LightspeedRetailApi.php":"838334c91e74a59599635f7d9d4ae4c4","src\/Facades\/LightspeedRetailApi.php":"f41d2efd10fb66074f2f44de7c8e7b01","src\/Traits\/HasLightspeedRetailResources.php":"6ea826caae2eda6d63a63ecd25d8d188","src\/Traits\/QueryBuilder.php":"e72a6c086b2c5a61356a1ec8fcd6e537","src\/Traits\/RetailResources.php":"fbf6b52348377cb0cce4f2984110029f","src\/Console\/Commands\/VerifyApiConnectionCommand.php":"64a49fe8835fe34cd9c03202293925d6","src\/Console\/Commands\/GenerateAuthenticationUrlCommand.php":"8d352858337963a7ece67a91fdd023cb","src\/Exceptions\/MissingLightspeedResourceException.php":"108edcafb4771e6f40821797cc260720","src\/Exceptions\/LightspeedRetailException.php":"a3cb67cc8fd5dfae2a7225f9df909b8c","src\/Exceptions\/IncorrectModelConfigurationException.php":"b4de6f62f6e26945c2469624cae159e4","src\/Exceptions\/DuplicateResourceException.php":"8076d0af87afde30f566ee40c77e1002","src\/Exceptions\/WaitingForSynchronisationException.php":"b14988adbf4b339d5c2bba0e45cc6d76","src\/Exceptions\/AuthenticationException.php":"9f33b80f486f0430c2246f3cdcd470fe","src\/Http\/Controllers\/SaveAccessTokenController.php":"d43a70b5e4246e5081b50fc084b32ad9","src\/Resource.php":"ff289351a1a289dbbc8de113c0f21a2a","src\/Actions\/GenerateRetailPayloadAction.php":"93eaaa027b7a586c63e10b5cda82668b","src\/Actions\/SaveLightspeedRetailResourceAction.php":"884115c83dedcc5fc6f27de0cbaea264","src\/Actions\/DispatchLightspeedRetailResourceAction.php":"2cf053306a01ac64fd20fde07cf724ca","src\/Actions\/RemoveResourceFromLightspeedRetailAction.php":"161b8081b7bcca676d534258c6832b8e","src\/Services\/Lightspeed\/ResourceManufacturer.php":"a5ec65218d0162dd4e7a78e071d37040","src\/Services\/Lightspeed\/ResourceImage.php":"8abf66fc267f9023bba1bc2af12cdeec","src\/Services\/Lightspeed\/ResourceAccount.php":"ab6ea568d93dcf4454210a33158ca552","src\/Services\/Lightspeed\/ResourceSale.php":"4b218338038f3f287cd11226e272f726","src\/Services\/Lightspeed\/ResourceCustomer.php":"be314a66626c215ad3e059518a81d1d0","src\/Services\/Lightspeed\/ResourceVendor.php":"f9998cabd45498adbf4c8047431a32c3","src\/Services\/Lightspeed\/ResourceCategory.php":"410ce3d3405e45d4c33a1a1d58216da0","src\/Services\/Lightspeed\/ResourceItem.php":"8f9c0c0707461ad9d40f5484612991e9","src\/Services\/ApiClient.php":"09e9c9fec6ce1bb37c937c0d837fe86a","src\/Events\/ResourceSendEvent.php":"c381cdc55bb013a5861669015aac691b","src\/Scope.php":"935d170d40bf7c19761e7561fa18b52e","tests\/TestCase.php":"4b54880a083676440aaeb071629a9601"}}
{"php":"8.3.12","version":"3.64.0","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces_position":true,"class_definition":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","do","else","elseif","final","for","foreach","function","if","interface","namespace","private","protected","public","static","switch","trait","try","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["method","property"]},"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"ordered_imports":{"sort_algorithm":"alpha"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"unary_operator_spaces":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/Repositories\/TokenRepository.php":"4c7e3cae90c4b82a4af71eb79379f13c","src\/Jobs\/RemoveResourceFromLightspeedRetail.php":"41ced6695106572e195bdcceb437affb","src\/Jobs\/Middleware\/RateLimited.php":"70d1a708795723a8057fe003dd412b36","src\/Jobs\/SendResourceToLightspeedRetail.php":"06a6665dcb710064051010be0d64a747","src\/Interfaces\/AutomaticSynchronisationInterface.php":"6cb6f34adfdc0f3e493f78aa7d94c39f","src\/Interfaces\/TokenInterface.php":"5eec333b8948a0d4600ab72f0d6b267e","src\/Models\/LightspeedRetailResource.php":"db317c4a52377a0b866ec89b38a9d9da","src\/Models\/ApiToken.php":"c887efa1d1ad1c326cb9347ebbf6eff1","src\/LightspeedRetailApiServiceProvider.php":"a574b45dffc99069e530e6f67f396082","src\/LightspeedRetailApi.php":"838334c91e74a59599635f7d9d4ae4c4","src\/Facades\/LightspeedRetailApi.php":"f41d2efd10fb66074f2f44de7c8e7b01","src\/Traits\/HasLightspeedRetailResources.php":"6ea826caae2eda6d63a63ecd25d8d188","src\/Traits\/QueryBuilder.php":"274c265ee9054f7536f7ba27139884d2","src\/Traits\/RetailResources.php":"fbf6b52348377cb0cce4f2984110029f","src\/Console\/Commands\/VerifyApiConnectionCommand.php":"64a49fe8835fe34cd9c03202293925d6","src\/Console\/Commands\/GenerateAuthenticationUrlCommand.php":"8d352858337963a7ece67a91fdd023cb","src\/Exceptions\/MissingLightspeedResourceException.php":"108edcafb4771e6f40821797cc260720","src\/Exceptions\/LightspeedRetailException.php":"a3cb67cc8fd5dfae2a7225f9df909b8c","src\/Exceptions\/IncorrectModelConfigurationException.php":"b4de6f62f6e26945c2469624cae159e4","src\/Exceptions\/DuplicateResourceException.php":"8076d0af87afde30f566ee40c77e1002","src\/Exceptions\/WaitingForSynchronisationException.php":"b14988adbf4b339d5c2bba0e45cc6d76","src\/Exceptions\/AuthenticationException.php":"9f33b80f486f0430c2246f3cdcd470fe","src\/Http\/Controllers\/SaveAccessTokenController.php":"d43a70b5e4246e5081b50fc084b32ad9","src\/Resource.php":"ff289351a1a289dbbc8de113c0f21a2a","src\/Actions\/GenerateRetailPayloadAction.php":"93eaaa027b7a586c63e10b5cda82668b","src\/Actions\/SaveLightspeedRetailResourceAction.php":"884115c83dedcc5fc6f27de0cbaea264","src\/Actions\/DispatchLightspeedRetailResourceAction.php":"2cf053306a01ac64fd20fde07cf724ca","src\/Actions\/RemoveResourceFromLightspeedRetailAction.php":"161b8081b7bcca676d534258c6832b8e","src\/Services\/Lightspeed\/ResourceManufacturer.php":"a5ec65218d0162dd4e7a78e071d37040","src\/Services\/Lightspeed\/ResourceImage.php":"8abf66fc267f9023bba1bc2af12cdeec","src\/Services\/Lightspeed\/ResourceAccount.php":"ab6ea568d93dcf4454210a33158ca552","src\/Services\/Lightspeed\/ResourceSale.php":"4b218338038f3f287cd11226e272f726","src\/Services\/Lightspeed\/ResourceCustomer.php":"be314a66626c215ad3e059518a81d1d0","src\/Services\/Lightspeed\/ResourceVendor.php":"f9998cabd45498adbf4c8047431a32c3","src\/Services\/Lightspeed\/ResourceCategory.php":"410ce3d3405e45d4c33a1a1d58216da0","src\/Services\/Lightspeed\/ResourceItem.php":"8f9c0c0707461ad9d40f5484612991e9","src\/Services\/ApiClient.php":"84cc8e0b56737a28e7580d027f82f8da","src\/Events\/ResourceSendEvent.php":"c381cdc55bb013a5861669015aac691b","src\/Scope.php":"935d170d40bf7c19761e7561fa18b52e","tests\/TestCase.php":"4b54880a083676440aaeb071629a9601"}}
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [v1.1.0] - 2024-10-21
### Changed
- Upgrade pagination to v3 for `getAll()`

## [v1.0.0] - 2024-10-20
### Fixed
- Load multiple relations
Expand All @@ -26,7 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Everything. Initial alpha release

[Unreleased]: https://github.com/timothydc/laravel-lightspeed-retail-api/compare/v1.0.0...HEAD
[Unreleased]: https://github.com/timothydc/laravel-lightspeed-retail-api/compare/v1.1.0...HEAD
[v1.1.0]: https://github.com/timothydc/laravel-lightspeed-retail-api/compare/v1.0.0...v1.1.0
[v1.0.0]: https://github.com/timothydc/laravel-lightspeed-retail-api/compare/v0.21-alpha...v1.0.0
[v0.21-alpha]: https://github.com/timothydc/laravel-lightspeed-retail-api/compare/v0.20-alpha...v0.21-alpha
[v0.20-alpha]: https://github.com/timothydc/laravel-lightspeed-retail-api/compare/v0.19-alpha...v0.20-alpha
Expand Down
112 changes: 52 additions & 60 deletions src/Services/ApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,32 +69,26 @@ public function isConfigured(): bool

public function getAll(string $resource = null, int $id = null, array $query = []): Collection
{
if (! array_key_exists('offset', $query)) {
$query['offset'] = 0;
}

if (! array_key_exists('limit', $query)) {
$query['limit'] = self::API_RESULT_LIMIT;
}

$results = [];

while (($result = $this->get($resource, $id, $query))->count() > 0) {
$results = collect();
while (($result = $this->getWithPagination($resource, $id, $query))->count() > 0) {
$result = Collection::unwrap($result);

if ($query['limit'] === 1) {
$result = [$result];
}

$results = array_merge($result, $results);
// merge resource results
$results = $results->merge($result[$resource]);

$query['offset'] += $query['limit'];
// prepare next page
if (isset($result['@attributes']['after']) && $result['@attributes']['after'] !== '') {
$query['after'] = $result['@attributes']['after'];
} else {
// exit while loop when there is no "next" page
break;
}
}

return collect($results);
return $results;
}

public function get(string $resource = null, int $id = null, array $query = []): Collection
public function get(string $resource = null, int $id = null, array $query = [], bool $withPagination = false): Collection
{
$responseObject = Http::withHeaders(['Accept' => 'application/json'])
->withOptions(['handler' => $this->createHandlerStack()])
Expand All @@ -109,56 +103,25 @@ public function get(string $resource = null, int $id = null, array $query = []):
return collect($response['Account']);
}

// fix Lightspeed unstructured way of returning an array when a multi dimensional array is expected
// fix Lightspeed unstructured way of returning an array when a multidimensional array is expected
if (isset($response['@attributes']['count']) && $response['@attributes']['count'] === 1) {
$response[$resource] = [$response[$resource]];
}

// for pagination, add @attributes meta data to result
if ($withPagination) {
$response['@attributes'] = $this->extractAttributes($response);
$response[$resource] = collect($response[$resource] ?? []);

return collect($response);
}

return collect($response[$resource] ?? []);
}

public function getWithPagination(string $resource = null, int $id = null, array $query = []): Collection
{
$responseObject = Http::withHeaders(['Accept' => 'application/json'])
->withOptions(['handler' => $this->createHandlerStack()])
->get($this->getUrl($resource, $id) . $this->buildQueryString($query));

$this->logAction('GET ' . $this->getUrl($resource, $id), ['params' => func_get_args(), 'status' => $responseObject->status()]);

$response = $responseObject->json();

// unstructured way of requesting the "Account" resource
if (! $resource) {
return collect($response['Account']);
}

// fix Lightspeed unstructured way of returning an array when a multi dimensional array is expected
if (isset($response['@attributes']['count']) && $response['@attributes']['count'] === 1) {
$response[$resource] = [$response[$resource]];
}

$attributes = $response['@attributes'];
$after = '';
$before = '';

if ($attributes['next'] !== '') {
$matches = [];
preg_match('/after=([^&]+)/', $attributes['next'], $matches);
$after = array_key_exists(1, $matches) ? $matches[1] : '';
}

if ($attributes['previous'] !== '') {
$matches = [];
preg_match('/before=([^&]+)/', $attributes['previous'], $matches);
$before = array_key_exists(1, $matches) ? $matches[1] : '';
}

$attributes['after'] = $after;
$attributes['before'] = $before;
$response['@attributes'] = collect($attributes);
$response[$resource] = collect($response[$resource] ?? []);

return collect($response);
return $this->get($resource, $id, $query, true);
}

/**
Expand Down Expand Up @@ -505,4 +468,33 @@ protected function refreshToken(): void
Log::emergency(self::class . ' Unable to refresh token.', [$response]);
}
}

protected function extractAttributes(array $data): Collection
{
if (! array_key_exists('@attributes', $data)) {
return collect();
}

$attributes = $data['@attributes'] ?? [];

// extract "next" key from URL
if (array_key_exists('next', $attributes) && $attributes['next'] !== '') {
// parse URL and extract query parameters
parse_str(parse_url($attributes['next'])['query'], $queryParameters);
$after = $queryParameters['after'] ?? '';
}

// extract "previous" key from URL
if (array_key_exists('previous', $attributes) && $attributes['previous'] !== '') {
// parse URL and extract query parameters
parse_str(parse_url($attributes['previous'])['query'], $queryParameters);
$before = $queryParameters['before'] ?? '';
}

// add new keys to $attributes
$attributes['after'] = $after ?? '';
$attributes['before'] = $before ?? '';

return collect($attributes);
}
}
6 changes: 3 additions & 3 deletions src/Traits/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ private function operatorMapping(): array

private function buildQueryString(array $parameters = []) : string
{
$parameters = $this->buildQueryParameters($parameters);
$queryParameters = $this->buildQueryParameters($parameters);

return $parameters ? '?' . $parameters : '';
return $queryParameters ? '?' . $queryParameters : '';
}

private function buildQueryParameters(array $parameters = []): string
Expand Down Expand Up @@ -84,7 +84,7 @@ private function _getOperator($operator): string
if (array_key_exists($operator, $this->operatorMapping())) {
$parsedOperator = $this->operatorMapping()[$operator];

if ($operator != $this->operator_equal) {
if ($operator !== $this->operator_equal) {
$parsedOperator = '=' . $parsedOperator . ',';
}

Expand Down

0 comments on commit f56e185

Please sign in to comment.