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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ psalm.xml
testbench.yaml
/docs
/coverage

# OS
.DS_Store
23 changes: 23 additions & 0 deletions src/Data/Project.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace NieuwbouwOffice\PhpSdk\Data;

class Project
{
public function __construct(
public readonly string $uuid,
public readonly string $title,
public readonly string $created_at,
public readonly string $updated_at,
) {}

public static function fromResponse(array $data): self
{
return new self(
uuid: $data['Project_UUId'],
title: $data['Project_Titel'],
created_at: $data['Project_Timestamp'],
updated_at: $data['Laatst_Bijgewerkt'] ?? $data['Project_Timestamp'],
);
}
}
7 changes: 7 additions & 0 deletions src/Requests/Projects/GetProjectRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace NieuwbouwOffice\PhpSdk\Requests\Projects;

use NieuwbouwOffice\PhpSdk\Data\Project;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;

class GetProjectRequest extends Request
{
Expand All @@ -15,4 +17,9 @@ public function resolveEndpoint(): string
{
return "/projects/{$this->uuid}/";
}

public function createDtoFromResponse(Response $response): Project
{
return Project::fromResponse($response->json('data.object'));
}
}
13 changes: 13 additions & 0 deletions src/Requests/Projects/GetProjectsRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace NieuwbouwOffice\PhpSdk\Requests\Projects;

use NieuwbouwOffice\PhpSdk\Data\Project;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;

class GetProjectsRequest extends Request
{
Expand All @@ -13,4 +15,15 @@ public function resolveEndpoint(): string
{
return '/projects/';
}

/**
* @return Project[]
*/
public function createDtoFromResponse(Response $response): array
{
return array_map(
fn (array $object) => Project::fromResponse($object),
$response->json('data.objects'),
);
}
}
13 changes: 8 additions & 5 deletions src/Resources/ProjectResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@

namespace NieuwbouwOffice\PhpSdk\Resources;

use NieuwbouwOffice\PhpSdk\Data\Project;
use NieuwbouwOffice\PhpSdk\Requests\Projects\GetProjectRequest;
use NieuwbouwOffice\PhpSdk\Requests\Projects\GetProjectsRequest;
use Saloon\Http\BaseResource;
use Saloon\Http\Response;

class ProjectResource extends BaseResource
{
public function list(): Response
/**
* @return Project[]
*/
public function list(): array
{
return $this->connector->send(new GetProjectsRequest);
return $this->connector->send(new GetProjectsRequest)->dto();
}

public function get(string $uuid): Response
public function get(string $uuid): Project
{
return $this->connector->send(new GetProjectRequest($uuid));
return $this->connector->send(new GetProjectRequest($uuid))->dto();
}
}
17 changes: 17 additions & 0 deletions tests/Data/ProjectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

use NieuwbouwOffice\PhpSdk\Data\Project;

it('exposes the four properties as readonly public values', function () {
$project = new Project(
uuid: 'abc-123',
title: 'Project ABC',
created_at: '2026-01-01T00:00:00Z',
updated_at: '2026-02-01T00:00:00Z',
);

expect($project->uuid)->toBe('abc-123')
->and($project->title)->toBe('Project ABC')
->and($project->created_at)->toBe('2026-01-01T00:00:00Z')
->and($project->updated_at)->toBe('2026-02-01T00:00:00Z');
});
29 changes: 29 additions & 0 deletions tests/Requests/Projects/GetProjectRequestTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<?php

use NieuwbouwOffice\PhpSdk\Data\Project;
use NieuwbouwOffice\PhpSdk\NieuwbouwOffice;
use NieuwbouwOffice\PhpSdk\Requests\Projects\GetProjectRequest;
use Saloon\Enums\Method;
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;

it('uses the GET method', function () {
expect((new GetProjectRequest('abc-123'))->getMethod())->toBe(Method::GET);
Expand All @@ -15,3 +19,28 @@
expect((new GetProjectRequest('abc-123'))->resolveEndpoint())
->toBe('/projects/abc-123/');
});

it('creates a Project DTO from the response', function () {
$mockClient = new MockClient([
GetProjectRequest::class => MockResponse::make([
'data' => [
'object' => [
'Project_UUId' => 'e00afb7a1791a22eb8bca3707687c549',
'Project_Titel' => 'De Suikerzijde',
'Project_Timestamp' => '2025-12-02 14:43:53',
],
],
]),
]);

$connector = new NieuwbouwOffice('test-token');
$connector->withMockClient($mockClient);

$project = $connector->send(new GetProjectRequest('e00afb7a1791a22eb8bca3707687c549'))->dto();

expect($project)->toBeInstanceOf(Project::class)
->and($project->uuid)->toBe('e00afb7a1791a22eb8bca3707687c549')
->and($project->title)->toBe('De Suikerzijde')
->and($project->created_at)->toBe('2025-12-02 14:43:53')
->and($project->updated_at)->toBe('2025-12-02 14:43:53');
});
45 changes: 45 additions & 0 deletions tests/Requests/Projects/GetProjectsRequestTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<?php

use NieuwbouwOffice\PhpSdk\Data\Project;
use NieuwbouwOffice\PhpSdk\NieuwbouwOffice;
use NieuwbouwOffice\PhpSdk\Requests\Projects\GetProjectsRequest;
use Saloon\Enums\Method;
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;

it('uses the GET method', function () {
expect((new GetProjectsRequest)->getMethod())->toBe(Method::GET);
Expand All @@ -10,3 +14,44 @@
it('resolves to the /projects/ endpoint', function () {
expect((new GetProjectsRequest)->resolveEndpoint())->toBe('/projects/');
});

it('creates an array of Project DTOs from the response', function () {
$mockClient = new MockClient([
GetProjectsRequest::class => MockResponse::make([
'data' => [
'objects' => [
[
'Project_UUId' => 'e00afb7a1791a22eb8bca3707687c549',
'Project_Titel' => 'De Suikerzijde',
'Project_Timestamp' => '2025-12-02 14:43:53',
'Laatst_Bijgewerkt' => '2025-12-02 14:43:53',
],
[
'Project_UUId' => 'e51d280c47f8ed8b780adfc1bb25436a',
'Project_Titel' => 'Suikerzijde - Hanny van den Horsthof',
'Project_Timestamp' => '2025-06-12 16:53:48',
'Laatst_Bijgewerkt' => '2026-05-07 08:47:23',
],
],
],
'meta' => ['count' => 2],
]),
]);

$connector = new NieuwbouwOffice('test-token');
$connector->withMockClient($mockClient);

$projects = $connector->send(new GetProjectsRequest)->dto();

expect($projects)->toBeArray()->toHaveCount(2)
->and($projects[0])->toBeInstanceOf(Project::class)
->and($projects[0]->uuid)->toBe('e00afb7a1791a22eb8bca3707687c549')
->and($projects[0]->title)->toBe('De Suikerzijde')
->and($projects[0]->created_at)->toBe('2025-12-02 14:43:53')
->and($projects[0]->updated_at)->toBe('2025-12-02 14:43:53')
->and($projects[1])->toBeInstanceOf(Project::class)
->and($projects[1]->uuid)->toBe('e51d280c47f8ed8b780adfc1bb25436a')
->and($projects[1]->title)->toBe('Suikerzijde - Hanny van den Horsthof')
->and($projects[1]->created_at)->toBe('2025-06-12 16:53:48')
->and($projects[1]->updated_at)->toBe('2026-05-07 08:47:23');
});
55 changes: 34 additions & 21 deletions tests/Resources/ProjectResourceTest.php
Original file line number Diff line number Diff line change
@@ -1,57 +1,70 @@
<?php

use NieuwbouwOffice\PhpSdk\Data\Project;
use NieuwbouwOffice\PhpSdk\NieuwbouwOffice;
use NieuwbouwOffice\PhpSdk\Requests\Projects\GetProjectRequest;
use NieuwbouwOffice\PhpSdk\Requests\Projects\GetProjectsRequest;
use NieuwbouwOffice\PhpSdk\Resources\ProjectResource;
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
use Saloon\Http\Response;

it('list() sends a GetProjectsRequest and returns the response', function () {
it('list() sends a GetProjectsRequest and returns an array of Project DTOs', function () {
$mockClient = new MockClient([
GetProjectsRequest::class => MockResponse::make([
'data' => [
['uuid' => 'a', 'name' => 'Project A'],
['uuid' => 'b', 'name' => 'Project B'],
'objects' => [
[
'Project_UUId' => 'a',
'Project_Titel' => 'Project A',
'Project_Timestamp' => '2025-12-02 14:43:53',
'Laatst_Bijgewerkt' => '2025-12-02 14:43:53',
],
[
'Project_UUId' => 'b',
'Project_Titel' => 'Project B',
'Project_Timestamp' => '2025-06-12 16:53:48',
'Laatst_Bijgewerkt' => '2026-05-07 08:47:23',
],
],
],
'meta' => ['count' => 2],
]),
]);

$connector = new NieuwbouwOffice('test-token');
$connector->withMockClient($mockClient);

$response = $connector->projects()->list();
$projects = $connector->projects()->list();

expect($response)->toBeInstanceOf(Response::class)
->and($response->json())->toBe([
'data' => [
['uuid' => 'a', 'name' => 'Project A'],
['uuid' => 'b', 'name' => 'Project B'],
],
]);
expect($projects)->toBeArray()->toHaveCount(2)
->and($projects[0])->toBeInstanceOf(Project::class)
->and($projects[0]->uuid)->toBe('a')
->and($projects[1]->uuid)->toBe('b');

$mockClient->assertSent(GetProjectsRequest::class);
});

it('get() sends a GetProjectRequest with the given uuid and returns the response', function () {
it('get() sends a GetProjectRequest with the given uuid and returns a Project DTO', function () {
$mockClient = new MockClient([
GetProjectRequest::class => MockResponse::make([
'uuid' => 'abc-123',
'name' => 'Project ABC',
'data' => [
'object' => [
'Project_UUId' => 'abc-123',
'Project_Titel' => 'Project ABC',
'Project_Timestamp' => '2025-12-02 14:43:53',
],
],
]),
]);

$connector = new NieuwbouwOffice('test-token');
$connector->withMockClient($mockClient);

$response = $connector->projects()->get('abc-123');
$project = $connector->projects()->get('abc-123');

expect($response)->toBeInstanceOf(Response::class)
->and($response->json())->toBe([
'uuid' => 'abc-123',
'name' => 'Project ABC',
]);
expect($project)->toBeInstanceOf(Project::class)
->and($project->uuid)->toBe('abc-123')
->and($project->title)->toBe('Project ABC');

$mockClient->assertSent(function ($request) {
return $request instanceof GetProjectRequest
Expand Down
Loading