Skip to content

Commit

Permalink
feat: add repository structure
Browse files Browse the repository at this point in the history
  • Loading branch information
Katalam committed May 11, 2024
1 parent 2337391 commit 3123db4
Show file tree
Hide file tree
Showing 27 changed files with 7,826 additions and 39 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"Katalam\\OnOfficeAdapter\\OnOfficeAdapterServiceProvider"
],
"aliases": {
"OnOfficeAdapter": "Katalam\\OnOfficeAdapter\\Facades\\OnOfficeAdapter"
"EstateRepository": "Katalam\\OnOfficeAdapter\\Facades\\EstateRepository"
}
}
},
Expand Down
6 changes: 0 additions & 6 deletions config/onoffice-adapter.php

This file was deleted.

13 changes: 13 additions & 0 deletions config/onoffice.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

// config for Katalam/OnOfficeAdapter
return [
'base_url' => 'https://api.onoffice.de/api/stable/api.php',
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],

'token' => env('ON_OFFICE_TOKEN', ''),
'secret' => env('ON_OFFICE_SECRET', ''),
];
13 changes: 13 additions & 0 deletions src/Enums/OnOfficeAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Katalam\OnOfficeAdapter\Enums;

enum OnOfficeAction: string
{
case Read = 'urn:onoffice-de-ns:smart:2.5:smartml:action:read';
case Create = 'urn:onoffice-de-ns:smart:2.5:smartml:action:create';
case Modify = 'urn:onoffice-de-ns:smart:2.5:smartml:action:modify';
case Get = 'urn:onoffice-de-ns:smart:2.5:smartml:action:get';
case Do = 'urn:onoffice-de-ns:smart:2.5:smartml:action:do';
case Delete = 'urn:onoffice-de-ns:smart:2.5:smartml:action:delete';
}
9 changes: 9 additions & 0 deletions src/Enums/OnOfficeResourceId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Katalam\OnOfficeAdapter\Enums;

enum OnOfficeResourceId: string
{
case Estate = 'estate';
case None = '';
}
10 changes: 10 additions & 0 deletions src/Enums/OnOfficeResourceType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Katalam\OnOfficeAdapter\Enums;

enum OnOfficeResourceType: string
{
case Estate = 'estate';
case Fields = 'fields';
case File = 'file';
}
9 changes: 9 additions & 0 deletions src/Exceptions/OnOfficeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Katalam\OnOfficeAdapter\Exceptions;

use Exception;

class OnOfficeException extends Exception
{
}
17 changes: 17 additions & 0 deletions src/Facades/EstateRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Katalam\OnOfficeAdapter\Facades;

use Illuminate\Support\Facades\Facade;

/**
* @see \Katalam\OnOfficeAdapter\Repositories\EstateRepository
* @method static all()
*/
class EstateRepository extends Facade
{
protected static function getFacadeAccessor(): string
{
return \Katalam\OnOfficeAdapter\Repositories\EstateRepository::class;
}
}
16 changes: 0 additions & 16 deletions src/Facades/OnOfficeAdapter.php

This file was deleted.

7 changes: 0 additions & 7 deletions src/OnOfficeAdapter.php

This file was deleted.

18 changes: 16 additions & 2 deletions src/OnOfficeAdapterServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Katalam\OnOfficeAdapter;

use Illuminate\Support\Facades\Http;
use Katalam\OnOfficeAdapter\Services\OnOfficeService;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Katalam\OnOfficeAdapter\Commands\OnOfficeAdapterCommand;
Expand All @@ -17,9 +19,21 @@ public function configurePackage(Package $package): void
*/
$package
->name('laravel-onoffice-adapter')
->hasConfigFile()
->hasConfigFile('onoffice')
->hasViews()
->hasMigration('create_laravel-onoffice-adapter_table')
->hasCommand(OnOfficeAdapterCommand::class);
}


public function bootingPackage(): void
{
Http::macro('onOffice', function () {
return Http::withHeaders(config('onoffice.headers'))
->baseUrl(config('onoffice.base_url'));
});

$this->app->scoped(OnOfficeService::class, function () {
return new OnOfficeService();
});
}
}
36 changes: 36 additions & 0 deletions src/Repositories/EstateRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Katalam\OnOfficeAdapter\Repositories;

use Illuminate\Support\Collection;
use Katalam\OnOfficeAdapter\Enums\OnOfficeAction;
use Katalam\OnOfficeAdapter\Enums\OnOfficeResourceType;
use Katalam\OnOfficeAdapter\Exceptions\OnOfficeException;
use Katalam\OnOfficeAdapter\Services\OnOfficeService;

readonly class EstateRepository
{
public function __construct(
private OnOfficeService $onOfficeService,
)
{
}


public function all(): Collection
{
return $this->onOfficeService->requestAll(/**
* @throws OnOfficeException
*/ function (int $pageSize, int $offset) {
return $this->onOfficeService->requestApi(
OnOfficeAction::Read,
OnOfficeResourceType::Estate,
parameters: [
OnOfficeService::DATA => ['Id'],
OnOfficeService::LISTLIMIT => $pageSize,
OnOfficeService::LISTOFFSET => $offset,
]
);
});
}
}
158 changes: 158 additions & 0 deletions src/Services/OnOfficeService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php

namespace Katalam\OnOfficeAdapter\Services;

use Illuminate\Http\Client\Response;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Katalam\OnOfficeAdapter\Enums\OnOfficeAction;
use Katalam\OnOfficeAdapter\Enums\OnOfficeResourceId;
use Katalam\OnOfficeAdapter\Enums\OnOfficeResourceType;
use Katalam\OnOfficeAdapter\Exceptions\OnOfficeException;

class OnOfficeService
{
/*
* Parameter constants for the onOffice API request.
*/
public const DATA = 'data';
public const LISTLIMIT = 'listlimit';
public const LISTOFFSET = 'listoffset';

private string $token;

private string $secret;

public function __construct()
{
$this->token = config('onoffice.token');
$this->secret = config('onoffice.secret');
}

public function getToken(): string
{
return $this->token;
}

public function getSecret(): string
{
return $this->secret;
}

/*
* Generates a HMAC for the onOffice API request.
* The new HMAC is calculated by concatenating the values of the parameters
* timestamp, token, resourcetype and actionid in this order.
* A SHA256 hash is formed from this string (with the secret as the key)
* and the resulting binary string must then be base64 encoded.
*
* Read more: https://apidoc.onoffice.de/onoffice-api-request/request-elemente/action/#hmac
*/
private function getHmac(OnOfficeAction $actionId, OnOfficeResourceType $resourceType): string
{
return base64_encode(
hash_hmac(
'sha256',
implode(
'',
[
'timestamp' => Carbon::now()->timestamp,
'token' => $this->token,
'resourcetype' => $resourceType->value,
'actionid' => $actionId->value,
]
),
$this->secret,
true
)
);
}

/*
* Makes a request to the onOffice API.
* Throws an exception if the request fails.
*
* Read more: https://apidoc.onoffice.de/onoffice-api-request/aufbau/
*/
/**
* @throws OnOfficeException
*/
public function requestApi(
OnOfficeAction $actionId,
OnOfficeResourceType $resourceType,
OnOfficeResourceId $resourceId = OnOfficeResourceId::None,
string|int $identifier = '',
array $parameters = [],
): Response {
$response = Http::onOffice()
->post('/', [
'token' => $this->token,
'request' => [
'actions' => [
[
'actionid' => $actionId->value,
'resourceid' => $resourceId->value,
'resourcetype' => $resourceType->value,
'identifier' => $identifier,
'timestamp' => Carbon::now()->timestamp,
'hmac' => $this->getHmac($actionId, $resourceType),
'hmac_version' => 2,
'parameters' => $parameters,
],
],
],
]);

if ($response->json('status.code') !== 200) {
throw new OnOfficeException('Failed to request OnOffice API');
}

return $response;
}


/**
* Makes a paginated request to the onOffice API.
* With a max page calculation based on
* the total count of records,
* of the first request.
*/
public function requestAll(
callable $request,
string $resultPath = 'response.results.0.data.records',
string $countPath = 'response.results.0.data.meta.cntabsolute',
int $pageSize = 200,
int $offset = 0
): Collection
{
$maxPage = 0;
$data = collect();
do {
$response = $request($pageSize, $offset);

if ($response->json('status.code') !== 200) {
Log::error('Failed to request estates from onOffice API');

return $data;
}

// If the maxPage is 0,
// we need to calculate it from the total count of estates
// and the page size,
// the first time we get the response from the API
if ($maxPage === 0) {
$countAbsolute = $response->json($countPath);
$maxPage = ceil($countAbsolute / $pageSize);
}

$data->push(...$response->json($resultPath));

$offset += $pageSize;
$currentPage = $offset / $pageSize;
} while ($maxPage > $currentPage);

return $data;
}
}
5 changes: 0 additions & 5 deletions tests/ExampleTest.php

This file was deleted.

24 changes: 24 additions & 0 deletions tests/Repositories/EstateRepositoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

use Illuminate\Support\Facades\Http;
use Katalam\OnOfficeAdapter\Facades\EstateRepository;
use Katalam\OnOfficeAdapter\Tests\Stubs\ReadEstateResponse;

it('can get all', function () {
Http::preventStrayRequests();
Http::fake([
'*' => Http::sequence([
// Each response will have 600 estates to simulate pagination
ReadEstateResponse::make(estateId: 1, count: 600),
ReadEstateResponse::make(estateId: 2, count: 600),
ReadEstateResponse::make(estateId: 3, count: 600),
]),
]);

$estates = EstateRepository::all();

expect($estates)
->toHaveCount(3)
->and($estates->first()['id'])->toBe(1)
->and($estates->last()['id'])->toBe(3);
});
19 changes: 19 additions & 0 deletions tests/Services/OnOfficeServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

use Illuminate\Support\Str;
use Katalam\OnOfficeAdapter\Services\OnOfficeService;

it('can use the config token and secret', function () {
$token = Str::random();
$secret = Str::random();

config([
'onoffice.token' => $token,
'onoffice.secret' => $secret,
]);

$onOfficeService = app(OnOfficeService::class);

expect($onOfficeService->getToken())->toBe($token)
->and($onOfficeService->getSecret())->toBe($secret);
});
Loading

0 comments on commit 3123db4

Please sign in to comment.