From cec5f9829370f205679ed1b6ca092b80f129e643 Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Mon, 11 Dec 2023 21:28:56 -0500 Subject: [PATCH 01/14] Actually send the body --- src/Api/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Api/Client.php b/src/Api/Client.php index ca8dc7a..36d1bf8 100644 --- a/src/Api/Client.php +++ b/src/Api/Client.php @@ -115,7 +115,7 @@ public function request(?string $path, ?array $data = [], ?string $method = 'GET 'Content-Type' => 'application/json', 'User-Agent' => 'SPINEN/'.$this->getVersion(), ], - // 'body' => empty($data) ? null : json_encode($data), + 'body' => empty($data) ? null : json_encode($data), ], uri: $this->uri($path), ) From 160459005bb8bbc194c02bc9d3d69dd69bf75bb1 Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Mon, 11 Dec 2023 21:29:50 -0500 Subject: [PATCH 02/14] Better exception handling of request errors --- src/Api/Client.php | 47 ++++++++++++++++++++++++--------- src/Exceptions/ApiException.php | 29 ++++++++++++++++++++ 2 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 src/Exceptions/ApiException.php diff --git a/src/Api/Client.php b/src/Api/Client.php index 36d1bf8..182d814 100644 --- a/src/Api/Client.php +++ b/src/Api/Client.php @@ -2,11 +2,16 @@ namespace Spinen\Ncentral\Api; +use Exception; use GuzzleHttp\Client as Guzzle; use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\TransferException; use Illuminate\Support\Str; use RuntimeException; +use Spinen\Ncentral\Exceptions\ApiException; use Spinen\Ncentral\Exceptions\ClientConfigurationException; +use Spinen\Ncentral\Exceptions\ResourceNotFoundException; use Spinen\Ncentral\Exceptions\TokenException; use Spinen\Version\Version; @@ -72,6 +77,33 @@ public function getVersion() return new Version(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'VERSION'); } + /** + * Process exception + * + * @throws GuzzleException + * @throws RuntimeException + * @throws ApiException + */ + protected function processException(GuzzleException $e): void + { + if (!is_a($e, RequestException::class)) { + throw $e; + } + + /** @var RequestException $e */ + + $body = $e->getResponse()->getBody()->getContents(); + + $results = json_decode($body, true); + + throw new ApiException( + body: $body, + code: $results['status'], + message: $results['message'], + previous: $e, + ); + } + /** * Shortcut to 'POST' request * @@ -123,10 +155,7 @@ public function request(?string $path, ?array $data = [], ?string $method = 'GET ->getContents(), ); } catch (GuzzleException $e) { - // TODO: Figure out what to do with this error - // TODO: Consider returning [] for 401's? - - throw $e; + $this->processException($e); } } @@ -165,10 +194,7 @@ public function refreshToken(): Token return $this->token; } catch (GuzzleException $e) { - // TODO: Figure out what to do with this error - // TODO: Consider returning [] for 401's? - - throw $e; + $this->processException($e); } } @@ -204,10 +230,7 @@ public function requestToken(): Token return $this->token; } catch (GuzzleException $e) { - // TODO: Figure out what to do with this error - // TODO: Consider returning [] for 401's? - - throw $e; + $this->processException($e); } } diff --git a/src/Exceptions/ApiException.php b/src/Exceptions/ApiException.php new file mode 100644 index 0000000..4b3c753 --- /dev/null +++ b/src/Exceptions/ApiException.php @@ -0,0 +1,29 @@ +body = $body; + } + + public function getBody(): string + { + return $this->body; + } +} From 8081e54526c275cc7832fea0d16503050de8a88d Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Mon, 11 Dec 2023 21:32:15 -0500 Subject: [PATCH 03/14] Format with pint --- src/Api/Client.php | 9 +++------ src/Exceptions/ApiException.php | 2 +- src/Support/Builder.php | 15 +++++++-------- src/Support/Collection.php | 2 +- src/Support/Model.php | 2 +- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Api/Client.php b/src/Api/Client.php index 182d814..d462740 100644 --- a/src/Api/Client.php +++ b/src/Api/Client.php @@ -6,12 +6,10 @@ use GuzzleHttp\Client as Guzzle; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Exception\TransferException; use Illuminate\Support\Str; use RuntimeException; use Spinen\Ncentral\Exceptions\ApiException; use Spinen\Ncentral\Exceptions\ClientConfigurationException; -use Spinen\Ncentral\Exceptions\ResourceNotFoundException; use Spinen\Ncentral\Exceptions\TokenException; use Spinen\Version\Version; @@ -80,18 +78,17 @@ public function getVersion() /** * Process exception * + * @throws ApiException * @throws GuzzleException * @throws RuntimeException - * @throws ApiException */ protected function processException(GuzzleException $e): void { - if (!is_a($e, RequestException::class)) { + if (! is_a($e, RequestException::class)) { throw $e; } /** @var RequestException $e */ - $body = $e->getResponse()->getBody()->getContents(); $results = json_decode($body, true); @@ -310,7 +307,7 @@ public function setToken(Token|string $token): self * in the configs, but if a url is passed in as a second parameter then it is used. * If no url is found it will use the hard-coded v2 Ncentral API URL. */ - public function uri(string $path = null, string $url = null): string + public function uri(?string $path = null, ?string $url = null): string { if ($path && Str::startsWith($path, 'http')) { return $path; diff --git a/src/Exceptions/ApiException.php b/src/Exceptions/ApiException.php index 4b3c753..1ce6292 100644 --- a/src/Exceptions/ApiException.php +++ b/src/Exceptions/ApiException.php @@ -15,7 +15,7 @@ public function __construct( string $message, int $code, ?Throwable $previous = null, - string $body = null, + ?string $body = null, ) { parent::__construct(message: $message, code: $code, previous: $previous); diff --git a/src/Support/Builder.php b/src/Support/Builder.php index 4c07ead..716ea1a 100644 --- a/src/Support/Builder.php +++ b/src/Support/Builder.php @@ -6,7 +6,6 @@ use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Arr; use Illuminate\Support\Collection as LaravelCollection; -use Illuminate\Support\Str; use Illuminate\Support\Traits\Conditionable; use Spinen\Ncentral\Concerns\HasClient; use Spinen\Ncentral\Customer; @@ -136,7 +135,7 @@ public function debug(bool $debug = true): self * @throws NoClientException * @throws TokenException */ - public function get(array|string $properties = ['*'], string $extra = null): Collection|Model + public function get(array|string $properties = ['*'], ?string $extra = null): Collection|Model { $properties = Arr::wrap($properties); @@ -169,7 +168,7 @@ public function get(array|string $properties = ['*'], string $extra = null): Col ->setLinks($links) ->setPagination(count: $count, page: $page, pages: $pages, pageSize: $pageSize) // If never a collection, only return the first - ->unless($this->getModel()->collection, fn(Collection $c): Model => $c->first()); + ->unless($this->getModel()->collection, fn (Collection $c): Model => $c->first()); } /** @@ -195,7 +194,7 @@ public function getModel(): Model * * @throws InvalidRelationshipException */ - public function getPath(string $extra = null): ?string + public function getPath(?string $extra = null): ?string { $w = (array) $this->wheres; $id = Arr::pull($w, $this->getModel()->getKeyName()); @@ -222,7 +221,7 @@ public function find(int|string $id, array|string $properties = ['*']): Model /** * Order newest to oldest */ - public function latest(string $column = null): self + public function latest(?string $column = null): self { $column ??= $this->getModel()->getCreatedAtColumn(); @@ -284,7 +283,7 @@ public function newInstanceForModel(string $model): self /** * Order oldest to newest */ - public function oldest(string $column = null): self + public function oldest(?string $column = null): self { $column ??= $this->getModel()->getCreatedAtColumn(); @@ -317,7 +316,7 @@ public function orderByDesc(string $column): self * * @throws InvalidRelationshipException */ - public function page(int|string $number, int|string $size = null): self + public function page(int|string $number, int|string|null $size = null): self { return $this->where('pageNumber', (int) $number) ->when($size, fn (self $b): self => $b->paginate($size)); @@ -328,7 +327,7 @@ public function page(int|string $number, int|string $size = null): self * * @throws InvalidRelationshipException */ - public function paginate(int|string $size = null): self + public function paginate(int|string|null $size = null): self { return $this->unless($size, fn (self $b): self => $b->where('paginate', false)) ->when($size, fn (self $b): self => $b->where('paginate', true)->where('pageSize', (int) $size)); diff --git a/src/Support/Collection.php b/src/Support/Collection.php index d019c0f..e1ec63d 100644 --- a/src/Support/Collection.php +++ b/src/Support/Collection.php @@ -87,7 +87,7 @@ public function setLinks(array $links = []): self /** * Set pagination */ - public function setPagination(int $count = null, int $page = null, int $pages = null, int $pageSize = null): self + public function setPagination(?int $count = null, ?int $page = null, ?int $pages = null, ?int $pageSize = null): self { $this->pagination = array_merge($this->pagination, compact('count', 'page', 'pages', 'pageSize')); diff --git a/src/Support/Model.php b/src/Support/Model.php index ed629b6..74c8eaa 100644 --- a/src/Support/Model.php +++ b/src/Support/Model.php @@ -150,7 +150,7 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab /** * Model constructor. */ - public function __construct(?array $attributes = [], Model $parentModel = null) + public function __construct(?array $attributes = [], ?Model $parentModel = null) { // All dates from API comes as epoch with milliseconds $this->dateFormat = 'Uv'; From bcfe94b396283e2446a3800735cc6a124ca17196 Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 19:55:45 -0500 Subject: [PATCH 04/14] Move peel wrapper to model to be used by save & update --- src/Support/Builder.php | 14 +------------- src/Support/Model.php | 12 ++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Support/Builder.php b/src/Support/Builder.php index 716ea1a..ce3f687 100644 --- a/src/Support/Builder.php +++ b/src/Support/Builder.php @@ -151,7 +151,7 @@ public function get(array|string $properties = ['*'], ?string $extra = null): Co $pageSize = $response['pageSize'] ?? null; // Peel off the key if exist - $response = $this->peelWrapperPropertyIfNeeded(Arr::wrap($response)); + $response = $this->getModel()->peelWrapperPropertyIfNeeded(Arr::wrap($response)); // Convert to a collection of filtered objects casted to the class return (new Collection((array_values($response) === $response) ? $response : [$response])) @@ -333,18 +333,6 @@ public function paginate(int|string|null $size = null): self ->when($size, fn (self $b): self => $b->where('paginate', true)->where('pageSize', (int) $size)); } - /** - * Peel of the wrapping property if it exist. - * - * @throws InvalidRelationshipException - */ - protected function peelWrapperPropertyIfNeeded(array $properties): array - { - return array_key_exists($this->getModel()->getResponseKey(), $properties) - ? $properties[$this->getModel()->getResponseKey()] - : $properties; - } - /** * Set the class to cast the response * diff --git a/src/Support/Model.php b/src/Support/Model.php index 74c8eaa..72d6244 100644 --- a/src/Support/Model.php +++ b/src/Support/Model.php @@ -608,6 +608,18 @@ public function offsetUnset($offset): void unset($this->attributes[$offset], $this->relations[$offset]); } + /** + * Peel of the wrapping property if it exist. + * + * @throws InvalidRelationshipException + */ + public function peelWrapperPropertyIfNeeded(array $properties): array + { + return array_key_exists($this->getResponseKey(), $properties) + ? $properties[$this->getResponseKey()] + : $properties; + } + /** * Laravel allows control of accessing missing attributes, so we just return false * From 8b8f7d0c5a7c7f6bf9dd5db44cd028d55f48a6ac Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 19:57:05 -0500 Subject: [PATCH 05/14] Allow setting extra at runtime --- src/Support/Model.php | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Support/Model.php b/src/Support/Model.php index 72d6244..ced5ab7 100644 --- a/src/Support/Model.php +++ b/src/Support/Model.php @@ -60,6 +60,11 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab */ public bool $exists = false; + /** + * Extra path to add to end of API endpoint. + */ + protected string $extra; + /** * Indicates if the IDs are auto-incrementing. */ @@ -343,6 +348,14 @@ public function getDefaultWheres(array $query = []): array ]; } + /** + * Any thing to add to the end of the path + */ + public function getExtra(): ?string + { + return $this->extra ?? null; + } + /** * Get the value indicating whether the IDs are incrementing. */ @@ -410,6 +423,9 @@ public function getPath($extra = null, array $query = []): ?string $path .= '/'.$this->getKey(); } + // Use the supplied extra or check if the model has an extra property + $extra ??= $this->getExtra(); + // Stick any extra things on the end if (! is_null($extra)) { $path .= '/'.ltrim($extra, '/'); @@ -707,7 +723,18 @@ public function saveOrFail(): bool /** * Set the readonly * - * @param bool $readonly + * @return $this + */ + public function setExtra($extra = null): self + { + $this->extra = $extra; + + return $this; + } + + /** + * Set the readonly + * * @return $this */ public function setReadonly($readonly = true): self From a6c35d2ed6708225d1849bd441682cba57b3072f Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 19:58:14 -0500 Subject: [PATCH 06/14] Allow setting read only at runtime --- src/Support/Model.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Support/Model.php b/src/Support/Model.php index ced5ab7..a75989c 100644 --- a/src/Support/Model.php +++ b/src/Support/Model.php @@ -194,7 +194,7 @@ public function __isset(string $key): bool */ public function __set($key, $value) { - if ($this->readonlyModel) { + if ($this->getReadonlyModel()) { throw new ModelReadonlyException(); } @@ -309,7 +309,7 @@ protected function convertBoolToString(mixed $value): mixed // public function delete(): bool // { // // TODO: Make sure that the model supports being deleted - // if ($this->readonlyModel) { + // if ($this->getReadonlyModel()) { // return false; // } @@ -443,6 +443,14 @@ public function getPath($extra = null, array $query = []): ?string return $path; } + /** + * Does the model allow updates? + */ + public function getReadonlyModel(): bool + { + return $this->readonlyModel ?? false; + } + /** * Get a relationship value from a method. * @@ -609,7 +617,7 @@ public function offsetGet($offset): mixed */ public function offsetSet($offset, $value): void { - if ($this->readonlyModel) { + if ($this->getReadonlyModel()) { throw new ModelReadonlyException(); } @@ -676,8 +684,7 @@ public function relationResolver($class, $key) */ public function save(): bool { - // TODO: Make sure that the model supports being saved - if ($this->readonlyModel) { + if ($this->getReadonlyModel()) { return false; } From 8d998efb48d449f7327dd3b415f6ca0de2f2e4fb Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:00:32 -0500 Subject: [PATCH 07/14] Clean up the expected exceptions --- src/Api/Client.php | 19 +++++++++++++------ src/Exceptions/TokenException.php | 9 --------- src/Support/Builder.php | 16 ++++++++++++---- src/Support/Model.php | 25 +++++++++++++++++++------ src/Support/Relations/BelongsTo.php | 6 ++++-- src/Support/Relations/HasMany.php | 6 ++++-- 6 files changed, 52 insertions(+), 29 deletions(-) delete mode 100644 src/Exceptions/TokenException.php diff --git a/src/Api/Client.php b/src/Api/Client.php index d462740..4e867b3 100644 --- a/src/Api/Client.php +++ b/src/Api/Client.php @@ -10,7 +10,6 @@ use RuntimeException; use Spinen\Ncentral\Exceptions\ApiException; use Spinen\Ncentral\Exceptions\ClientConfigurationException; -use Spinen\Ncentral\Exceptions\TokenException; use Spinen\Version\Version; /** @@ -34,8 +33,9 @@ public function __construct( /** * Shortcut to 'DELETE' request * + * @throws ApiException * @throws GuzzleException - * @throws TokenException + * @throws RuntimeException */ // TODO: Enable this once they add endpoints that support delete // public function delete(string $path): ?array @@ -46,8 +46,9 @@ public function __construct( /** * Shortcut to 'GET' request * + * @throws ApiException * @throws GuzzleException - * @throws TokenException + * @throws RuntimeException */ public function get(string $path): ?array { @@ -57,6 +58,7 @@ public function get(string $path): ?array /** * Get, return, or refresh the token * + * @throws ApiException * @throws GuzzleException * @throws RuntimeException */ @@ -104,8 +106,9 @@ protected function processException(GuzzleException $e): void /** * Shortcut to 'POST' request * + * @throws ApiException * @throws GuzzleException - * @throws TokenException + * @throws RuntimeException */ public function post(string $path, array $data): ?array { @@ -115,8 +118,9 @@ public function post(string $path, array $data): ?array /** * Shortcut to 'PUT' request * + * @throws ApiException * @throws GuzzleException - * @throws TokenException + * @throws RuntimeException */ // TODO: Enable this once they add endpoints that support put // public function put(string $path, array $data): ?array @@ -127,8 +131,9 @@ public function post(string $path, array $data): ?array /** * Make an API call to Ncentral * + * @throws ApiException * @throws GuzzleException - * @throws TokenException + * @throws RuntimeException */ public function request(?string $path, ?array $data = [], ?string $method = 'GET'): ?array { @@ -161,6 +166,7 @@ public function request(?string $path, ?array $data = [], ?string $method = 'GET /** * Refresh a token * + * @throws ApiException * @throws GuzzleException * @throws RuntimeException */ @@ -198,6 +204,7 @@ public function refreshToken(): Token /** * Request a token * + * @throws ApiException * @throws GuzzleException * @throws RuntimeException */ diff --git a/src/Exceptions/TokenException.php b/src/Exceptions/TokenException.php deleted file mode 100644 index 18d7fc0..0000000 --- a/src/Exceptions/TokenException.php +++ /dev/null @@ -1,9 +0,0 @@ -setRawAttributes($response, true); return true; - } catch (GuzzleException $e) { - // TODO: Do something with the error + } catch (RuntimeException $e) { + // TODO: Should we do something with the error? return false; } @@ -714,8 +723,12 @@ public function save(): bool /** * Save the model in Ncentral, but raise error if fail * + * @throws ApiException + * @throws InvalidCastException + * @throws LogicException + * @throws MissingAttributeException * @throws NoClientException - * @throws TokenException + * @throws RuntimeException * @throws UnableToSaveException */ public function saveOrFail(): bool diff --git a/src/Support/Relations/BelongsTo.php b/src/Support/Relations/BelongsTo.php index 6917cfd..c258ad8 100644 --- a/src/Support/Relations/BelongsTo.php +++ b/src/Support/Relations/BelongsTo.php @@ -3,9 +3,10 @@ namespace Spinen\Ncentral\Support\Relations; use GuzzleHttp\Exception\GuzzleException; +use RuntimeException; +use Spinen\Ncentral\Exceptions\ApiException; use Spinen\Ncentral\Exceptions\InvalidRelationshipException; use Spinen\Ncentral\Exceptions\NoClientException; -use Spinen\Ncentral\Exceptions\TokenException; use Spinen\Ncentral\Support\Builder; use Spinen\Ncentral\Support\Model; @@ -58,10 +59,11 @@ public function getForeignKeyName(): string /** * Get the results of the relationship. * + * @throws ApiException * @throws GuzzleException * @throws InvalidRelationshipException * @throws NoClientException - * @throws TokenException + * @throws RuntimeException */ public function getResults(): ?Model { diff --git a/src/Support/Relations/HasMany.php b/src/Support/Relations/HasMany.php index 598e89e..4efefc6 100644 --- a/src/Support/Relations/HasMany.php +++ b/src/Support/Relations/HasMany.php @@ -3,9 +3,10 @@ namespace Spinen\Ncentral\Support\Relations; use GuzzleHttp\Exception\GuzzleException; +use RuntimeException; +use Spinen\Ncentral\Exceptions\ApiException; use Spinen\Ncentral\Exceptions\InvalidRelationshipException; use Spinen\Ncentral\Exceptions\NoClientException; -use Spinen\Ncentral\Exceptions\TokenException; use Spinen\Ncentral\Support\Collection; /** @@ -16,10 +17,11 @@ class HasMany extends Relation /** * Get the results of the relationship. * + * @throws ApiException * @throws GuzzleException * @throws InvalidRelationshipException * @throws NoClientException - * @throws TokenException + * @throws RuntimeException */ public function getResults(): Collection { From f34ad4e77fd0a77dcaeeec1830f6c480dcd54a0a Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:01:04 -0500 Subject: [PATCH 08/14] Don't wrap properties in array before sending --- src/Support/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/Model.php b/src/Support/Model.php index dc22899..6613165 100644 --- a/src/Support/Model.php +++ b/src/Support/Model.php @@ -703,7 +703,7 @@ public function save(): bool } $response = $this->getClient() - ->post($this->getPath(), [$this->toArray()]); + ->post($this->getPath(), $this->toArray()); $this->exists = true; From 2bbfe4077382c81ee759546502f06b26ede2a49f Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:01:31 -0500 Subject: [PATCH 09/14] Peel off wrapper after saving --- src/Support/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/Model.php b/src/Support/Model.php index 6613165..1c22c71 100644 --- a/src/Support/Model.php +++ b/src/Support/Model.php @@ -710,7 +710,7 @@ public function save(): bool $this->wasRecentlyCreated = true; // Reset the model with the results as we get back the full model - $this->setRawAttributes($response, true); + $this->setRawAttributes($this->peelWrapperPropertyIfNeeded($response), true); return true; } catch (RuntimeException $e) { From 204834a010aeac851fd33aa8377aa09aed7005ba Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:02:48 -0500 Subject: [PATCH 10/14] Build out models for scheduled tasks --- src/DetailedScheduledTask.php | 50 ++++++++++++++++++++++++++++ src/ScheduledTask.php | 61 +++++++++++++++++++++++++++++++++-- src/Support/Builder.php | 4 +++ src/Support/Model.php | 1 - 4 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/DetailedScheduledTask.php diff --git a/src/DetailedScheduledTask.php b/src/DetailedScheduledTask.php new file mode 100644 index 0000000..688d180 --- /dev/null +++ b/src/DetailedScheduledTask.php @@ -0,0 +1,50 @@ + 'int', + 'taskId' => 'int', + ]; + + /** + * Path to API endpoint. + */ + protected string $extra = '/status/details'; + + /** + * Path to API endpoint. + */ + protected string $path = '/scheduled-tasks'; + + /** + * The primary key for the model. + */ + protected string $primaryKey = 'taskId'; + + /** + * Is the model readonly? + */ + protected bool $readonlyModel = true; +} diff --git a/src/ScheduledTask.php b/src/ScheduledTask.php index e01f10a..5f1f976 100644 --- a/src/ScheduledTask.php +++ b/src/ScheduledTask.php @@ -7,19 +7,76 @@ /** * Class ScheduledTask * - * @property + * @property ?array $deviceIds + * @property ?bool $isEnabled + * @property ?bool $isReactive + * @property ?int $applianceId + * @property ?int $customerId + * @property ?int $deviceId + * @property ?int $itemId + * @property ?int $parentId + * @property ?string $name + * @property ?string $type + * @property int $taskId */ class ScheduledTask extends Model { + /** + * The model's attributes. + * + * @var array + */ + protected $attributes = [ + // TODO: Should we set these defaults? + 'credential' => [ + 'type' => 'LocalSystem', + 'username' => null, + 'password' => null + ], + 'taskType' => 'AutomationPolicy', + ]; + /** * The attributes that should be cast to native types. * * @var array */ - protected $casts = []; + protected $casts = [ + 'applianceId' => 'int', + 'customerId' => 'int', + 'deviceId' => 'int', + 'isEnabled' => 'bool', + 'isReactive' => 'bool', + 'itemId' => 'int', + 'parentId' => 'int', + 'taskId' => 'int', + ]; /** * Path to API endpoint. */ protected string $path = '/scheduled-tasks'; + + /** + * The primary key for the model. + */ + protected string $primaryKey = 'taskId'; + + /** + * Any thing to add to the end of the path + */ + public function getExtra(): ?string + { + // N-able has create route different than get route + return $this->exists ? null : '/direct'; + } + + /** + * Does the model allow updates? + */ + public function getReadonlyModel(): bool + { + // Toggle readonly for existing as you cannot update + return $this->exists; + } } diff --git a/src/Support/Builder.php b/src/Support/Builder.php index fe9efb6..244e3a3 100644 --- a/src/Support/Builder.php +++ b/src/Support/Builder.php @@ -10,6 +10,7 @@ use RuntimeException; use Spinen\Ncentral\Concerns\HasClient; use Spinen\Ncentral\Customer; +use Spinen\Ncentral\DetailedScheduledTask; use Spinen\Ncentral\Device; use Spinen\Ncentral\DeviceTask; use Spinen\Ncentral\Exceptions\ApiException; @@ -17,6 +18,7 @@ use Spinen\Ncentral\Exceptions\ModelNotFoundException; use Spinen\Ncentral\Exceptions\NoClientException; use Spinen\Ncentral\Health; +use Spinen\Ncentral\ScheduledTask; use Spinen\Ncentral\ServerInfo; /** @@ -58,9 +60,11 @@ class Builder */ protected $rootModels = [ 'customers' => Customer::class, + 'detailedScheduledTasks' => DetailedScheduledTask::class, 'devices' => Device::class, 'deviceTasks' => DeviceTask::class, 'health' => Health::class, + 'scheduledTasks' => ScheduledTask::class, 'serverInfo' => ServerInfo::class, ]; diff --git a/src/Support/Model.php b/src/Support/Model.php index 1c22c71..53be5eb 100644 --- a/src/Support/Model.php +++ b/src/Support/Model.php @@ -423,7 +423,6 @@ public function getPath($extra = null, array $query = []): ?string $path = rtrim($this->path, '/'); // If have an id, then put it on the end - // NOTE: Ncentral treats creates & updates the same, so only on existing if ($this->exist && $this->getKey()) { $path .= '/'.$this->getKey(); } From c55d0b3ef7a0c2a14dbb7fff52624b003e75f2f6 Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:46:20 -0500 Subject: [PATCH 11/14] Better way to know which route to call for the model --- src/ScheduledTask.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ScheduledTask.php b/src/ScheduledTask.php index 5f1f976..54826ac 100644 --- a/src/ScheduledTask.php +++ b/src/ScheduledTask.php @@ -68,7 +68,7 @@ class ScheduledTask extends Model public function getExtra(): ?string { // N-able has create route different than get route - return $this->exists ? null : '/direct'; + return $this->taskId ? null : '/direct'; } /** From 7c03a3ba3c16359921fb9642b6cb65fb4e4777f9 Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:46:47 -0500 Subject: [PATCH 12/14] Check extra from builder too --- src/Support/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/Builder.php b/src/Support/Builder.php index 244e3a3..0b7c586 100644 --- a/src/Support/Builder.php +++ b/src/Support/Builder.php @@ -211,7 +211,7 @@ public function getPath(?string $extra = null): ?string $id = Arr::pull($w, $this->getModel()->getKeyName()); return $this->getModel() - ->getPath($extra.(is_null($id) ? null : '/'.$id), $w); + ->getPath($extra.(is_null($id) ? null : '/'.$id).$this->getModel()->getExtra(), $w); } /** From 5eb5084d8898d9ff8120c9ea48f5e043311f1088 Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:46:57 -0500 Subject: [PATCH 13/14] accessor to get the deatils --- src/ScheduledTask.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ScheduledTask.php b/src/ScheduledTask.php index 54826ac..7ee6233 100644 --- a/src/ScheduledTask.php +++ b/src/ScheduledTask.php @@ -62,6 +62,18 @@ class ScheduledTask extends Model */ protected string $primaryKey = 'taskId'; + /** + * Accessor to get the details + * + * @throws NoClientException + */ + public function getDetailsAttribute(): DetailedScheduledTask + { + return (new Builder())->setClient($this->getClient()) + ->detailedScheduledTasks() + ->find($this->taskId); + } + /** * Any thing to add to the end of the path */ From c4492c0667738618c504692966134d2c2d80c2a4 Mon Sep 17 00:00:00 2001 From: Jimmy Puckett Date: Tue, 12 Dec 2023 20:47:06 -0500 Subject: [PATCH 14/14] Format with pint --- src/ScheduledTask.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ScheduledTask.php b/src/ScheduledTask.php index 7ee6233..afa6a0c 100644 --- a/src/ScheduledTask.php +++ b/src/ScheduledTask.php @@ -2,6 +2,8 @@ namespace Spinen\Ncentral; +use Spinen\Ncentral\Exceptions\NoClientException; +use Spinen\Ncentral\Support\Builder; use Spinen\Ncentral\Support\Model; /** @@ -31,7 +33,7 @@ class ScheduledTask extends Model 'credential' => [ 'type' => 'LocalSystem', 'username' => null, - 'password' => null + 'password' => null, ], 'taskType' => 'AutomationPolicy', ];