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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions src/Data/Deployment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php

declare(strict_types=1);

namespace PhpDevKits\ForgeSdk\Data;

use DateTimeImmutable;
use InvalidArgumentException;
use JsonSerializable;
use Override;
use Throwable;

final readonly class Deployment implements JsonSerializable
{
public function __construct(
public string $id,
public DeploymentCommit $commit,
public string $type,
public string $status,
public ?DateTimeImmutable $startedAt,
public ?DateTimeImmutable $endedAt,
public DateTimeImmutable $createdAt,
public DateTimeImmutable $updatedAt,
) {}

/**
* @param array<array-key, mixed> $data A JSON:API `DeploymentResource` object.
*/
public static function from(array $data): self
{
$id = $data['id'] ?? null;
if (! is_string($id)) {
throw new InvalidArgumentException('Deployment data is missing the `id` field.');
}

$attributes = $data['attributes'] ?? null;
if (! is_array($attributes)) {
throw new InvalidArgumentException('Deployment data is missing the `attributes` object.');
}

$commit = $attributes['commit'] ?? null;
if (! is_array($commit)) {
throw new InvalidArgumentException('Deployment `attributes.commit` must be an object.');
}

return new self(
id: $id,
commit: DeploymentCommit::from($commit),
type: self::requireString($attributes, 'type'),
status: self::requireString($attributes, 'status'),
startedAt: self::optionalDate($attributes, 'started_at'),
endedAt: self::optionalDate($attributes, 'ended_at'),
createdAt: self::requireDate($attributes, 'created_at'),
updatedAt: self::requireDate($attributes, 'updated_at'),
);
}

/**
* @param array<array-key, mixed> $attributes
*/
private static function requireString(array $attributes, string $key): string
{
$value = $attributes[$key] ?? null;
if (! is_string($value)) {
throw new InvalidArgumentException(sprintf('Deployment `attributes.%s` must be a string.', $key));
}

return $value;
}

/**
* @param array<array-key, mixed> $attributes
*/
private static function requireDate(array $attributes, string $key): DateTimeImmutable
{
$value = $attributes[$key] ?? null;
if (! is_string($value)) {
throw new InvalidArgumentException(sprintf('Deployment `attributes.%s` must be a string.', $key));
}

return self::parseDate($value, $key);
}

/**
* Started/ended timestamps are null until the deployment actually starts
* and finishes, despite the spec marking them required.
*
* @param array<array-key, mixed> $attributes
*/
private static function optionalDate(array $attributes, string $key): ?DateTimeImmutable
{
$value = $attributes[$key] ?? null;
if ($value === null) {
return null;
}

if (! is_string($value)) {
throw new InvalidArgumentException(sprintf('Deployment `attributes.%s` must be a string or null.', $key));
}

return self::parseDate($value, $key);
}

private static function parseDate(string $value, string $key): DateTimeImmutable
{
try {
return new DateTimeImmutable($value);
} catch (Throwable $throwable) {
throw new InvalidArgumentException(sprintf('Deployment `attributes.%s` is not a valid date-time.', $key), 0, $throwable);
}
}

/**
* @return array{id: string, commit: DeploymentCommit, type: string, status: string, started_at: ?string, ended_at: ?string, created_at: string, updated_at: string}
*/
#[Override]
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'commit' => $this->commit,
'type' => $this->type,
'status' => $this->status,
'started_at' => $this->startedAt?->format(DATE_ATOM),
'ended_at' => $this->endedAt?->format(DATE_ATOM),
'created_at' => $this->createdAt->format(DATE_ATOM),
'updated_at' => $this->updatedAt->format(DATE_ATOM),
];
}
}
68 changes: 68 additions & 0 deletions src/Data/DeploymentCommit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace PhpDevKits\ForgeSdk\Data;

use InvalidArgumentException;
use JsonSerializable;
use Override;

/**
* The `commit` sub-object on a {@see Deployment}. Every field is nullable —
* a deployment that isn't tied to a specific commit (e.g. a manual redeploy)
* may have none of them.
*/
final readonly class DeploymentCommit implements JsonSerializable
{
public function __construct(
public ?string $hash,
public ?string $author,
public ?string $message,
public ?string $branch,
) {}

/**
* @param array<array-key, mixed> $data
*/
public static function from(array $data): self
{
return new self(
hash: self::optionalString($data, 'hash'),
author: self::optionalString($data, 'author'),
message: self::optionalString($data, 'message'),
branch: self::optionalString($data, 'branch'),
);
}

/**
* @param array<array-key, mixed> $data
*/
private static function optionalString(array $data, string $key): ?string
{
$value = $data[$key] ?? null;
if ($value === null) {
return null;
}

if (! is_string($value)) {
throw new InvalidArgumentException(sprintf('DeploymentCommit `%s` must be a string or null.', $key));
}

return $value;
}

/**
* @return array{hash: ?string, author: ?string, message: ?string, branch: ?string}
*/
#[Override]
public function jsonSerialize(): array
{
return [
'hash' => $this->hash,
'author' => $this->author,
'message' => $this->message,
'branch' => $this->branch,
];
}
}
62 changes: 62 additions & 0 deletions src/Data/DeploymentScript.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace PhpDevKits\ForgeSdk\Data;

use InvalidArgumentException;
use JsonSerializable;
use Override;

/**
* A site's deployment script. `content` is null when no script has been set.
*/
final readonly class DeploymentScript implements JsonSerializable
{
public function __construct(
public string $id,
public ?string $content,
public bool $autoSource,
) {}

/**
* @param array<array-key, mixed> $data A JSON:API `DeploymentScriptResource` object.
*/
public static function from(array $data): self
{
$id = $data['id'] ?? null;
if (! is_string($id)) {
throw new InvalidArgumentException('DeploymentScript data is missing the `id` field.');
}

$attributes = $data['attributes'] ?? null;
if (! is_array($attributes)) {
throw new InvalidArgumentException('DeploymentScript data is missing the `attributes` object.');
}

$content = $attributes['content'] ?? null;
if ($content !== null && ! is_string($content)) {
throw new InvalidArgumentException('DeploymentScript `attributes.content` must be a string or null.');
}

$autoSource = $attributes['auto_source'] ?? null;
if (! is_bool($autoSource)) {
throw new InvalidArgumentException('DeploymentScript `attributes.auto_source` must be a boolean.');
}

return new self(id: $id, content: $content, autoSource: $autoSource);
}

/**
* @return array{id: string, content: ?string, auto_source: bool}
*/
#[Override]
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'content' => $this->content,
'auto_source' => $this->autoSource,
];
}
}
51 changes: 51 additions & 0 deletions src/Data/ListDeploymentsOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace PhpDevKits\ForgeSdk\Data;

final readonly class ListDeploymentsOptions
{
public function __construct(
public ?int $size = null,
public ?string $cursor = null,
public ?string $sort = null,
public ?string $commitHash = null,
public ?string $commitMessage = null,
public ?string $commitAuthor = null,
) {}

/**
* @return array<string, int|string>
*/
public function toQuery(): array
{
$query = [];

if ($this->size !== null) {
$query['page[size]'] = $this->size;
}

if ($this->cursor !== null) {
$query['page[cursor]'] = $this->cursor;
}

if ($this->sort !== null) {
$query['sort'] = $this->sort;
}

if ($this->commitHash !== null) {
$query['filter[commit_hash]'] = $this->commitHash;
}

if ($this->commitMessage !== null) {
$query['filter[commit_message]'] = $this->commitMessage;
}

if ($this->commitAuthor !== null) {
$query['filter[commit_author]'] = $this->commitAuthor;
}

return $query;
}
}
30 changes: 30 additions & 0 deletions src/Data/UpdateDeploymentScriptData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace PhpDevKits\ForgeSdk\Data;

/**
* Input payload for PUT /orgs/{org}/servers/{server}/sites/{site}/deployments/script.
*/
final readonly class UpdateDeploymentScriptData
{
public function __construct(
public string $content,
public ?bool $autoSource = null,
) {}

/**
* @return array<string, mixed>
*/
public function toArray(): array
{
$payload = ['content' => $this->content];

if ($this->autoSource !== null) {
$payload['auto_source'] = $this->autoSource;
}

return $payload;
}
}
23 changes: 23 additions & 0 deletions src/Enums/DeploymentStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace PhpDevKits\ForgeSdk\Enums;

/**
* Lifecycle states of a deployment.
*
* Mirrors the `DeploymentStatus` enum in Forge's OpenAPI spec.
* `Data\Deployment->status` stays a plain string for forward-compatibility;
* these cases are a convenience for client code.
*/
enum DeploymentStatus: string
{
case Cancelled = 'cancelled';
case Deploying = 'deploying';
case Failed = 'failed';
case FailedBuild = 'failed-build';
case Finished = 'finished';
case Pending = 'pending';
case Queued = 'queued';
}
Loading
Loading