diff --git a/.gitignore b/.gitignore index e00e3ac..9d26831 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ phpunit.xml /build /.idea + +/src/Test.php diff --git a/.php_cs b/.php_cs index 3d5c5c5..86da788 100644 --- a/.php_cs +++ b/.php_cs @@ -3,6 +3,7 @@ return PhpCsFixer\Config::create() ->setRiskyAllowed(true) ->setUsingCache(true) + ->setCacheFile(__DIR__.'/.php_cs.cache') ->setRules(array( '@PSR2' => true, 'array_syntax' => ['syntax' => 'short'], @@ -31,4 +32,4 @@ return PhpCsFixer\Config::create() PhpCsFixer\Finder::create() ->in(__DIR__) ) - ; \ No newline at end of file + ; diff --git a/src/Actions/ManagesCompanies.php b/src/Actions/ManagesCompanies.php new file mode 100644 index 0000000..3545f57 --- /dev/null +++ b/src/Actions/ManagesCompanies.php @@ -0,0 +1,92 @@ +get('company/all', [ + 'query' => ['itemsPerPage' => $limit, 'page' => $page], + ]); + + return $this->fromCustifyCompanies($response['companies']); + } + + /** + * Get a single company. + * + * @param string $id + * + * @throws \TestMonitor\Custify\Exceptions\NotFoundException + * @throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @return \TestMonitor\Custify\Resources\Company + */ + public function company(string $id): Company + { + $response = $this->get("company/{$id}"); + + Validator::keysExists($response, ['companies']); + + // Simulate a not found response when the user_id does not exists. + if (empty($response['companies'])) { + throw new NotFoundException(); + } + + return $this->fromCustifyCompany($response['companies'][0]); + } + + /** + * Get a single company by company id. + * + * @param string $companyId + * + * @throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @throws \TestMonitor\Custify\Exceptions\NotFoundException + * @return \TestMonitor\Custify\Resources\Company + */ + public function companyByCompanyId(string $companyId): Company + { + $response = $this->get("company?company_id={$companyId}"); + + Validator::keysExists($response, ['companies']); + + // Simulate a not found response when the user_id does not exists. + if (empty($response['companies'])) { + throw new NotFoundException(); + } + + return $this->fromCustifyCompany($response['companies'][0]); + } + + /** + * Create or update a company. + * + * @param \TestMonitor\Custify\Resources\Company $company + * + *@throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @return \TestMonitor\Custify\Resources\Company + */ + public function createOrUpdateCompany(Company $company): Company + { + $response = $this->post('company', ['json' => $this->toCustifyCompany($company)]); + + return $this->fromCustifyCompany($response); + } +} diff --git a/src/Actions/ManagesPeople.php b/src/Actions/ManagesPeople.php index c87d95e..caf4789 100644 --- a/src/Actions/ManagesPeople.php +++ b/src/Actions/ManagesPeople.php @@ -2,7 +2,10 @@ namespace TestMonitor\Custify\Actions; +use TestMonitor\Custify\Validator; +use TestMonitor\Custify\Resources\Person; use TestMonitor\Custify\Transforms\TransformsPeople; +use TestMonitor\Custify\Exceptions\NotFoundException; trait ManagesPeople { @@ -17,12 +20,88 @@ trait ManagesPeople * @throws \TestMonitor\Custify\Exceptions\InvalidDataException * @return \TestMonitor\Custify\Resources\Person[] */ - public function people($page = 1, $limit = 10) + public function people($page = 1, $limit = 10): array { $response = $this->get('people/all', [ 'query' => ['itemsPerPage' => $limit, 'page' => $page], ]); - return $this->fromCustifyPeople($response); + return $this->fromCustifyPeople($response[0]['people']); + } + + /** + * Get a single person. + * + * @param string $id + * + * @throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @return \TestMonitor\Custify\Resources\Person + */ + public function person(string $id): Person + { + $response = $this->get("people/{$id}"); + + return $this->fromCustifyPerson($response); + } + + /** + * Get a single person by user id. + * + * @param string $userId + * + * @throws \TestMonitor\Custify\Exceptions\NotFoundException + * @throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @return \TestMonitor\Custify\Resources\Person + */ + public function personByUserId(string $userId): Person + { + $response = $this->get("people?user_id={$userId}"); + + Validator::keysExists($response[0], ['people']); + + // Simulate a not found response when the user_id does not exists. + if (empty($response[0]['people'])) { + throw new NotFoundException(); + } + + return $this->fromCustifyPerson($response[0]['people'][0]); + } + + /** + * Get a single person by email. + * + * @param string $email + * + * @throws \TestMonitor\Custify\Exceptions\NotFoundException + * @throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @return \TestMonitor\Custify\Resources\Person + */ + public function personByEmail(string $email): Person + { + $response = $this->get("people?email={$email}"); + + Validator::keysExists($response[0], ['people']); + + // Simulate a not found response when the user_id does not exists. + if (empty($response[0]['people'])) { + throw new NotFoundException(); + } + + return $this->fromCustifyPerson($response[0]['people'][0]); + } + + /** + * Create or update a person. + * + * @param \TestMonitor\Custify\Resources\Person $person + * + * @throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @return \TestMonitor\Custify\Resources\Person + */ + public function createOrUpdatePerson(Person $person): Person + { + $response = $this->post('people', ['json' => $this->toCustifyPerson($person)]); + + return $this->fromCustifyPerson($response); } } diff --git a/src/Client.php b/src/Client.php index 26b4432..701afc7 100644 --- a/src/Client.php +++ b/src/Client.php @@ -11,7 +11,8 @@ class Client { - use Actions\ManagesPeople; + use Actions\ManagesPeople, + Actions\ManagesCompanies; /** * @var string diff --git a/src/Resources/Company.php b/src/Resources/Company.php index f9fdf21..b05803f 100644 --- a/src/Resources/Company.php +++ b/src/Resources/Company.php @@ -32,8 +32,8 @@ class Company extends Resource */ public function __construct(array $attributes) { - $this->id = $attributes['id']; - $this->company_id = $attributes['company_id']; - $this->name = $attributes['name']; + $this->id = $attributes['id'] ?? ''; + $this->company_id = $attributes['company_id'] ?? ''; + $this->name = $attributes['name'] ?? ''; } } diff --git a/src/Resources/CustomAttributes.php b/src/Resources/CustomAttributes.php new file mode 100644 index 0000000..3cc4f05 --- /dev/null +++ b/src/Resources/CustomAttributes.php @@ -0,0 +1,56 @@ +attributes = $attributes; + } + + /** + * Gets a custom attribute. + * + * @param $name + * + * @return mixed|null + */ + public function __get($name) + { + return $this->attributes[$name] ?? null; + } + + /** + * Sets a custom attribute. + * + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * Returns the custom attributes as a key/value array. + * + * @return array + */ + public function toArray(): array + { + return $this->attributes; + } +} diff --git a/src/Resources/Person.php b/src/Resources/Person.php index d3281be..386c01f 100644 --- a/src/Resources/Person.php +++ b/src/Resources/Person.php @@ -25,6 +25,27 @@ class Person extends Resource */ public $email; + /** + * The name of the person. + * + * @var string + */ + public $name; + + /** + * The companies related to this person. + * + * @var array + */ + public $companies; + + /** + * The custom attributes for this person. + * + * @var \TestMonitor\Custify\Resources\CustomAttributes + */ + public $customAttributes; + /** * Create a new resource instance. * @@ -33,7 +54,12 @@ class Person extends Resource public function __construct(array $attributes) { $this->id = $attributes['id'] ?? ''; - $this->user_id = $attributes['user_id']; + $this->user_id = $attributes['user_id'] ?? ''; $this->email = $attributes['email']; + $this->name = $attributes['name'] ?? ''; + + $this->customAttributes = $attributes['custom_attributes'] ?? new CustomAttributes(); + + $this->companies = $attributes['companies'] ?? []; } } diff --git a/src/Transforms/TransformsCompanies.php b/src/Transforms/TransformsCompanies.php new file mode 100644 index 0000000..57cd4a1 --- /dev/null +++ b/src/Transforms/TransformsCompanies.php @@ -0,0 +1,54 @@ +fromCustifyCompany($company); + }, $companies); + } + + /** + * @param array $company + * + * @throws \TestMonitor\Custify\Exceptions\InvalidDataException + * @return \TestMonitor\Custify\Resources\Company + */ + protected function fromCustifyCompany($company): Company + { + Validator::keysExists($company, ['id', 'company_id']); + + return new Company([ + 'id' => $company['id'], + 'company_id' => $company['company_id'], + 'name' => $company['name'], + ]); + } + + /** + * @param Company $company + * + * @return array + */ + protected function toCustifyCompany(Company $company): array + { + return [ + 'company_id' => $company->company_id, + 'name' => $company->name, + ]; + } +} diff --git a/src/Transforms/TransformsPeople.php b/src/Transforms/TransformsPeople.php index 5cbe694..79841fc 100644 --- a/src/Transforms/TransformsPeople.php +++ b/src/Transforms/TransformsPeople.php @@ -4,6 +4,8 @@ use TestMonitor\Custify\Validator; use TestMonitor\Custify\Resources\Person; +use TestMonitor\Custify\Resources\Company; +use TestMonitor\Custify\Resources\CustomAttributes; trait TransformsPeople { @@ -30,12 +32,39 @@ protected function fromCustifyPeople($people): array */ protected function fromCustifyPerson($person): Person { - Validator::keysExists($person, ['id', 'user_id']); + Validator::keysExists($person, ['id', 'user_id', 'email']); return new Person([ 'id' => $person['id'], 'user_id' => $person['user_id'], 'email' => $person['email'], + 'name' => $person['name'] ?? '', + + 'custom_attributes' => new CustomAttributes($person['custom_attributes'] ?? []), + + 'companies' => array_map(function ($company) { + return new Company(['id' => $company]); + }, $person['companies'] ?? []), ]); } + + /** + * @param Person $person + * + * @return array + */ + protected function toCustifyPerson(Person $person): array + { + return [ + 'user_id' => $person->user_id, + 'email' => $person->email, + 'name' => $person->name, + + 'custom_attributes' => $person->customAttributes->toArray(), + + 'companies' => array_map(function (Company $company) { + return ['company_id' => $company->company_id]; + }, $person->companies ?? []), + ]; + } } diff --git a/tests/CompanyTest.php b/tests/CompanyTest.php new file mode 100644 index 0000000..0df07fa --- /dev/null +++ b/tests/CompanyTest.php @@ -0,0 +1,171 @@ +token = '12345'; + $this->company = ['id' => '1', 'company_id' => 'abcde', 'name' => 'Company']; + } + + public function tearDown(): void + { + Mockery::close(); + } + + /** @test */ + public function it_should_return_a_list_of_companies() + { + // Given + $custify = new Client($this->token); + + $custify->setClient($service = Mockery::mock('\GuzzleHttp\Client')); + + $response = Mockery::mock('Psr\Http\Message\ResponseInterface'); + $response->shouldReceive('getStatusCode')->andReturn(200); + $response->shouldReceive('getBody')->andReturn(json_encode(['companies' => [$this->company]])); + + $service->shouldReceive('request')->once()->andReturn($response); + + // When + $companies = $custify->companies(); + + // Then + $this->assertIsArray($companies); + $this->assertCount(1, $companies); + $this->assertInstanceOf(Company::class, $companies[0]); + $this->assertEquals($this->company['id'], $companies[0]->id); + $this->assertIsArray($companies[0]->toArray()); + } + + /** @test */ + public function it_should_throw_an_failed_action_exception_when_client_receives_bad_request_while_getting_a_list_of_companies() + { + // Given + $custify = new Client($this->token); + + $custify->setClient($service = Mockery::mock('\GuzzleHttp\Client')); + + $service->shouldReceive('request')->once()->andReturn($response = Mockery::mock('Psr\Http\Message\ResponseInterface')); + $response->shouldReceive('getStatusCode')->andReturn(400); + $response->shouldReceive('getBody')->andReturnNull(); + + $this->expectException(FailedActionException::class); + + // When + $custify->companies(); + } + + /** @test */ + public function it_should_throw_a_notfound_exception_when_client_receives_not_found_while_getting_a_list_of_companies() + { + // Given + $custify = new Client($this->token); + + $custify->setClient($service = Mockery::mock('\GuzzleHttp\Client')); + + $service->shouldReceive('request')->once()->andReturn($response = Mockery::mock('Psr\Http\Message\ResponseInterface')); + $response->shouldReceive('getStatusCode')->andReturn(404); + $response->shouldReceive('getBody')->andReturnNull(); + + $this->expectException(NotFoundException::class); + + // When + $custify->companies(); + } + + /** @test */ + public function it_should_throw_a_unauthorized_exception_when_client_lacks_authorization_for_getting_a_list_of_companies() + { + // Given + $custify = new Client($this->token); + + $custify->setClient($service = Mockery::mock('\GuzzleHttp\Client')); + + $service->shouldReceive('request')->once()->andReturn($response = Mockery::mock('Psr\Http\Message\ResponseInterface')); + $response->shouldReceive('getStatusCode')->andReturn(401); + $response->shouldReceive('getBody')->andReturnNull(); + + $this->expectException(UnauthorizedException::class); + + // When + $custify->companies(); + } + + /** @test */ + public function it_should_throw_a_validation_exception_when_client_provides_invalid_data_while_a_getting_list_of_companies() + { + // Given + $custify = new Client($this->token); + + $custify->setClient($service = Mockery::mock('\GuzzleHttp\Client')); + + $service->shouldReceive('request')->once()->andReturn($response = Mockery::mock('Psr\Http\Message\ResponseInterface')); + $response->shouldReceive('getStatusCode')->andReturn(422); + $response->shouldReceive('getBody')->andReturn(json_encode(['message' => 'invalid'])); + + $this->expectException(ValidationException::class); + + // When + $custify->companies(); + } + + /** @test */ + public function it_should_return_an_error_message_when_client_provides_invalid_data_while_a_getting_list_of_companies() + { + // Given + $custify = new Client($this->token); + + $custify->setClient($service = Mockery::mock('\GuzzleHttp\Client')); + + $service->shouldReceive('request')->once()->andReturn($response = Mockery::mock('Psr\Http\Message\ResponseInterface')); + $response->shouldReceive('getStatusCode')->andReturn(422); + $response->shouldReceive('getBody')->andReturn(json_encode(['errors' => ['invalid']])); + + // When + try { + $custify->companies(); + } catch (ValidationException $exception) { + + // Then + $this->assertIsArray($exception->errors()); + $this->assertEquals('invalid', $exception->errors()['errors'][0]); + } + } + + /** @test */ + public function it_should_throw_a_generic_exception_when_client_suddenly_becomes_a_teapot_while_a_getting_list_of_companies() + { + // Given + $custify = new Client($this->token); + + $custify->setClient($service = Mockery::mock('\GuzzleHttp\Client')); + + $service->shouldReceive('request')->once()->andReturn($response = Mockery::mock('Psr\Http\Message\ResponseInterface')); + $response->shouldReceive('getStatusCode')->andReturn(418); + $response->shouldReceive('getBody')->andReturn(json_encode(['rooibos' => 'anyone?'])); + + $this->expectException(Exception::class); + + // When + $custify->companies(); + } +} diff --git a/tests/PeopleTest.php b/tests/PeopleTest.php index dab3dc8..9bf5d6c 100644 --- a/tests/PeopleTest.php +++ b/tests/PeopleTest.php @@ -41,7 +41,7 @@ public function it_should_return_a_list_of_people() $response = Mockery::mock('Psr\Http\Message\ResponseInterface'); $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn(json_encode([$this->person])); + $response->shouldReceive('getBody')->andReturn(json_encode([['people' => [$this->person]]])); $service->shouldReceive('request')->once()->andReturn($response);