From cd40eb355b728dc3c351d69ae73c7bb050340207 Mon Sep 17 00:00:00 2001 From: Craig Potter Date: Wed, 14 Jun 2023 23:44:59 +0100 Subject: [PATCH] Initial Release --- .editorconfig | 15 ++ .env.example | 2 + .gitattributes | 7 + .github/CODE_OF_CONDUCT.md | 128 +++++++++++++ .github/CONTRIBUTING.md | 92 +++++++++ .github/README.md | 176 ++++++++++++++++++ .github/SECURITY.md | 3 + .github/workflows/php-cs-fixer.yml | 28 +++ .github/workflows/tests.yml | 50 +++++ .gitignore | 20 ++ .php-cs-fixer.dist.php | 44 +++++ LICENSE.md | 21 +++ composer.json | 44 +++++ phpunit.xml | 15 ++ src/DataObjects/Address.php | 65 +++++++ src/DataObjects/Firm.php | 35 ++++ src/DataObjects/Individual.php | 38 ++++ src/Fca.php | 95 ++++++++++ src/Requests/Firm/GetFirmAddresses.php | 57 ++++++ src/Requests/Firm/GetFirmDetails.php | 51 +++++ src/Requests/Firm/GetFirmIndividuals.php | 57 ++++++ src/Requests/GetEndpoint.php | 38 ++++ src/Resources/FirmResource.php | 71 +++++++ src/Resources/Resource.php | 19 ++ tests/Feature/EndpointTest.php | 23 +++ tests/Feature/FirmResourceTest.php | 140 ++++++++++++++ tests/Fixtures/Saloon/firm.address.json | 1 + tests/Fixtures/Saloon/firm.details.json | 1 + .../Saloon/firm.details.not-found.json | 1 + tests/Fixtures/Saloon/firm.individuals.1.json | 1 + tests/Fixtures/Saloon/firm.individuals.2.json | 1 + tests/Fixtures/Saloon/firm.individuals.3.json | 1 + tests/Pest.php | 45 +++++ 33 files changed, 1385 insertions(+) create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/README.md create mode 100644 .github/SECURITY.md create mode 100644 .github/workflows/php-cs-fixer.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 .php-cs-fixer.dist.php create mode 100644 LICENSE.md create mode 100644 composer.json create mode 100644 phpunit.xml create mode 100644 src/DataObjects/Address.php create mode 100644 src/DataObjects/Firm.php create mode 100644 src/DataObjects/Individual.php create mode 100644 src/Fca.php create mode 100644 src/Requests/Firm/GetFirmAddresses.php create mode 100644 src/Requests/Firm/GetFirmDetails.php create mode 100644 src/Requests/Firm/GetFirmIndividuals.php create mode 100644 src/Requests/GetEndpoint.php create mode 100644 src/Resources/FirmResource.php create mode 100644 src/Resources/Resource.php create mode 100644 tests/Feature/EndpointTest.php create mode 100644 tests/Feature/FirmResourceTest.php create mode 100644 tests/Fixtures/Saloon/firm.address.json create mode 100644 tests/Fixtures/Saloon/firm.details.json create mode 100644 tests/Fixtures/Saloon/firm.details.not-found.json create mode 100644 tests/Fixtures/Saloon/firm.individuals.1.json create mode 100644 tests/Fixtures/Saloon/firm.individuals.2.json create mode 100644 tests/Fixtures/Saloon/firm.individuals.3.json create mode 100644 tests/Pest.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6537ca4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..96b9991 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +AUTH_EMAIL= +AUTH_KEY= diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0a2ffba --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +/.github export-ignore +/tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +phpunit.xml export-ignore +.php-cs-fixer.dist.php export-ignore diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..b58f8d0 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +github@yeehaw.dev. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..2d8c908 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of one other developer, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at abuse@craigpotter.co.uk. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..bf82fab --- /dev/null +++ b/.github/README.md @@ -0,0 +1,176 @@ +# FCA API SDK for PHP +[![Latest Version on Packagist](https://img.shields.io/packagist/v/craigpotter/fca-php-sdk.svg?style=flat-square)](https://packagist.org/packages/craigpotter/fca-php-sdk) +[![Tests](https://img.shields.io/github/actions/workflow/status/craigpotter/fca-php-sdk/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/craigpotter/fca-php-sdk/actions/workflows/run-tests.yml) +[![Total Downloads](https://img.shields.io/packagist/dt/craigpotter/fca-php-sdk.svg?style=flat-square)](https://packagist.org/packages/craigpotter/fca-php-sdk) + +This is an unofficial PHP SDK for [FCA's API](https://register.fca.org.uk/Developer/s/) and is powered by [Saloon](https://github.com/Sammyjo20/Saloon). + +> **Note:** This SDK is still in development and is not yet ready for production use. +> Use at your own risk. + +## Installation + +You can install the package via composer: + +```bash +composer require craigpotter/fca-php-sdk +``` + +## Requirements + +You will need to have a valid API key from FCA to use this SDK. + +You can obtain one by [registering as a developer](https://register.fca.org.uk/Developer/s/). + +## Example Usage + +``` php +use CraigPotter\Fca\Fca; + +// Create a new FCA instance (This is required for all requests) +$fca = new Fca('my.email@dev.test', 'my-api-key'); + +// 12345 is the FCA firm reference number + +$firmFrnExists = $fa->firm(12345)->exists(); // Returns a boolean + +$response = $fca->firm(12345)->get(); + +$firm = $response->dto(); // Returns a Firm DTO +$json = $response->json(); // Returns the raw JSON response +``` + +## Documentation + +> **Warning:** This documentation is still a work in progress. + +Some responses have the option of a DTO (Data Transfer Object) which can be used to access the response data. + +This does not work for all responses, but is documented where it is available. +For all other responses, you can access the raw JSON response using `$response->json()`. + +### Firms + +#### Check an FRN exists +```php +use CraigPotter\Fca\Fca; + +// Create a new FCA instance (This is required for all requests) +$fca = new Fca('my.email@dev.test', 'my-api-key'); + +// 12345 is the FCA firm reference number + +$firmFrnExists = $fa->firm(12345)->exists(); // Returns a boolean +``` + +#### Get a firm by FRN +```php +use CraigPotter\Fca\Fca; + +// Create a new FCA instance (This is required for all requests) +$fca = new Fca('my.email@dev.test', 'my-api-key'); + +// 12345 is the FCA firm reference number + +$response = $fa->firm(12345)->get(); // Returns a Saloon response object + +$firm = $response->dto(); // Returns a Firm DTO +$firm->frn; // 12345 +$firm->name; // "My Firm" +$firm->status; // "Active" +$firm->statusDate; // "2021-01-01" +$firm->statusReason; // "Active" +$firm->companiesHouseNumber; // "12345678" + +$json = $response->json(); // Returns the raw JSON response +``` + +#### Get the individuals associated with a firm by FRN +This endpoint is paginated and will return a maximum of 20 results per page. +You should loop through the pages to get all results. +```php +use CraigPotter\Fca\Fca; + +// Create a new FCA instance (This is required for all requests) +use CraigPotter\Fca\Fca; + +// Create a new FCA instance (This is required for all requests) +$fca = new Fca('my.email@dev.test', 'my-api-key'); + +// 12345 is the FCA firm reference number + +$paginatedData = $fa->firm(12345)->individuals(); // Returns a Saloon response object + +$paginatedData->totalPages(); // Returns the total number of pages +$paginatedData->totalResults(); // Returns the total number of results +foreach ($paginatedData as $page) { + $paginatedData->getCurrentPage(); // Returns the current page number + $individuals = $page->dto(); // Returns an array of Individual DTOs for this page + $json = $page->json(); // Returns the raw JSON response for this page + + + foreach ($individuals as $individual) { + $individual->irn; // "JOHNSMITH12345" + $individual->name; // "John Smith" + $individual->status; // "Approved by regulator" + } +} +``` + +#### Get the addresses associated with a firm by FRN +This endpoint is paginated and will return a maximum of 20 results per page. +You should loop through the pages to get all results. +```php +use CraigPotter\Fca\Fca; + +// Create a new FCA instance (This is required for all requests) +use CraigPotter\Fca\Fca; + +// Create a new FCA instance (This is required for all requests) +$fca = new Fca('my.email@dev.test', 'my-api-key'); + +// 12345 is the FCA firm reference number + +$paginatedData = $fa->firm(12345)->addresses(); // Returns a Saloon response object + +$paginatedData->totalPages(); // Returns the total number of pages +$paginatedData->totalResults(); // Returns the total number of results +foreach ($paginatedData as $page) { + $paginatedData->getCurrentPage(); // Returns the current page number + $addresses = $page->dto(); // Returns an array of Address DTOs for this page + $json = $page->json(); // Returns the raw JSON response for this page + + + foreach ($addresses as $address) { + $address->website; // "www.example.org" + $address->phoneNumber; // "44123456778" + $address->type; // "Principal Place of Business" + $address->contactName; // "John Smith" + $address->addressLine1; // "1 Example Street" + $address->addressLine2; // "Aberdeen" + $address->addressLine3; // "Aberdeen" + $address->addressLine4; // "Aberdeen" + $address->town; // "Aberdeen" + $address->county; // "Aberdeenshire" + $address->country; // "United Kingdom" + $address->postcode; // "AB1 2CD" + } +} +``` + +## Testing + +Copy `.env.example` to `.env` and update accordingly. +```bash +vendor/bin/pest +``` + +## Credits + +- [Craig Potter](https://github.com/craigpotter) +- [All Contributors](../../../contributors) + +## License + +The MIT License (MIT). Please see [License File](../LICENSE.md) for more information. + diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..2d69cce --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email security@craigpotter.co.uk instead of using the issue tracker. diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml new file mode 100644 index 0000000..634914b --- /dev/null +++ b/.github/workflows/php-cs-fixer.yml @@ -0,0 +1,28 @@ +name: Code Style + +on: + push: + branches: + - 'main' + pull_request: + branches: + - '*' + +permissions: + contents: write + +jobs: + php-cs-fixer: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Run PHP CS Fixer + uses: docker://oskarstark/php-cs-fixer-ga + with: + args: --config=.php-cs-fixer.dist.php --allow-risky=yes + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: 🪄 Code Style Fixes diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..70481ad --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,50 @@ +name: tests + +on: + push: + branches: + - 'main' + pull_request: + branches: + - '*' + +permissions: + contents: read + +jobs: + tests: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ ubuntu-latest, windows-latest ] + php: [ 8.1, 8.2 ] + stability: [ prefer-lowest, prefer-stable ] + + name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + + - name: Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install dependencies + run: | + composer update --${{ matrix.stability }} --prefer-dist --no-interaction + + - name: Create env file + run: | + echo "${{ secrets.ENV_FILE }}" > .env + - name: Execute tests + run: vendor/bin/pest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1995a6a --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Operating system files and directories +.DS_Store +._.DS_Store + +# Editors and IDEs +/.fleet +/.idea +/.vscode + +# Project dependencies +/vendor +/composer.lock + +# Caches, externally stored stuff, etc +.php-cs-fixer.cache +.phpunit.result.cache + +# environments/configs +phpstan.neon +.env diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..3938001 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,44 @@ +in([ + __DIR__, + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true) + ->exclude(['vendor', 'node_modules']); + +$config = new PhpCsFixer\Config(); + +// Rules from: https://cs.symfony.com/doc/rules/index.html + +return $config->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'length'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => true, + 'single_quote' => ['strings_containing_single_quote_chars' => true], + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ], + 'return_type_declaration' => [ + 'space_before' => 'none' + ], + 'declare_strict_types' => true, + 'blank_line_after_opening_tag' => true, + 'single_import_per_statement' => true, + 'mb_str_functions' => true, +])->setFinder($finder); diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..e60a1de --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Craig Potter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7eb2870 --- /dev/null +++ b/composer.json @@ -0,0 +1,44 @@ +{ + "name": "craigpotter/fca-php-sdk", + "description": "An unofficial PHP SDK consuming the FCA API", + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Craig Potter", + "email": "me@craigpotter.co.uk", + "role": "Developer" + } + ], + "homepage": "https://github.com/craigpotter", + "require": { + "php": "^8.1", + "sammyjo20/saloon": "^2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.5", + "pestphp/pest": "^2.0", + "spatie/ray": "^1.33", + "vlucas/phpdotenv": "^5.5" + }, + "minimum-stability": "stable", + "autoload": { + "psr-4": { + "CraigPotter\\Fca\\": "src/", + "CraigPotter\\Fca\\Tests\\": "tests/" + } + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "scripts": { + "fix-code": [ + "./vendor/bin/php-cs-fixer fix --allow-risky=yes" + ], + "test": [ + "./vendor/bin/pest" + ] + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..60cf75f --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,15 @@ + + + + + ./tests + + + + + + ./app + ./src + + + diff --git a/src/DataObjects/Address.php b/src/DataObjects/Address.php new file mode 100644 index 0000000..958a5cb --- /dev/null +++ b/src/DataObjects/Address.php @@ -0,0 +1,65 @@ +json('Data')[0]; + + return new static( + (int) $data['FRN'], + $data['Organisation Name'], + $data['Business Type'], + $data['Status'], + $data['Status Effective Date'], + $data['Companies House Number'] ?? null, + ); + } +} diff --git a/src/DataObjects/Individual.php b/src/DataObjects/Individual.php new file mode 100644 index 0000000..aedbaa2 --- /dev/null +++ b/src/DataObjects/Individual.php @@ -0,0 +1,38 @@ + 'application/json', + 'Content-Type' => 'application/json', + 'X-Auth-Email' => $this->authEmail, + 'X-Auth-Key' => $this->authKey, + ]; + } + + /** + * Resolve the base URL of the service. + * + * @return string + */ + public function resolveBaseUrl(): string + { + return 'https://register.fca.org.uk/services/V0.1'; + } + + /** + * Determine if the request has failed + * + * @param Response $response + * @return bool|null + */ + public function hasRequestFailed(Response $response): ?bool + { + return ! str_ends_with($response->json('Status', 'FAILED'), '-00'); + } + + /** + * Get the firm resource + * + * @param int $frn + * @return FirmResource + */ + public function firm(int $frn): FirmResource + { + return new FirmResource($this, $frn); + } + + public function getEndpoint(string $endpoint): Paginator + { + return $this->paginate(new GetEndpoint($endpoint)); + } + + public function paginate(Request $request, ...$additionalArguments): Paginator + { + /** @var PagedPaginator $paginator */ + $paginator = new PagedPaginator($this, $request, 20, ...$additionalArguments); + + $paginator->setTotalKeyName('ResultInfo.total_count'); + $paginator->setPageKeyName('pgnp'); + $paginator->setLimit(20); + $paginator->setNextPageKeyName('ResultInfo.Next'); + $paginator->setCurrentPage(1); + + return $paginator; + } +} diff --git a/src/Requests/Firm/GetFirmAddresses.php b/src/Requests/Firm/GetFirmAddresses.php new file mode 100644 index 0000000..8f3b971 --- /dev/null +++ b/src/Requests/Firm/GetFirmAddresses.php @@ -0,0 +1,57 @@ +frn . '/Address'; + } + + /** + * Create a DTO from the response + * + * @param Response $response + * @return mixed + */ + public function createDtoFromResponse(Response $response): array + { + $data = $response->json('Data'); + $dtos = []; + foreach ($data as $address) { + $dtos[] = Address::fromArray($address); + } + + return $dtos; + } +} diff --git a/src/Requests/Firm/GetFirmDetails.php b/src/Requests/Firm/GetFirmDetails.php new file mode 100644 index 0000000..1945a54 --- /dev/null +++ b/src/Requests/Firm/GetFirmDetails.php @@ -0,0 +1,51 @@ +frn; + } + + /** + * Create a DTO from the response + * + * @param Response $response + * @return mixed + */ + public function createDtoFromResponse(Response $response): mixed + { + return Firm::fromResponse($response); + } +} diff --git a/src/Requests/Firm/GetFirmIndividuals.php b/src/Requests/Firm/GetFirmIndividuals.php new file mode 100644 index 0000000..d50ce2e --- /dev/null +++ b/src/Requests/Firm/GetFirmIndividuals.php @@ -0,0 +1,57 @@ +frn . '/Individuals'; + } + + /** + * Create a DTO from the response + * + * @param Response $response + * @return mixed + */ + public function createDtoFromResponse(Response $response): mixed + { + $data = $response->json('Data'); + $dtos = []; + foreach ($data as $individual) { + $dtos[] = Individual::fromArray($individual); + } + + return $dtos; + } +} diff --git a/src/Requests/GetEndpoint.php b/src/Requests/GetEndpoint.php new file mode 100644 index 0000000..1822d87 --- /dev/null +++ b/src/Requests/GetEndpoint.php @@ -0,0 +1,38 @@ +endpoint; + } +} diff --git a/src/Resources/FirmResource.php b/src/Resources/FirmResource.php new file mode 100644 index 0000000..14a4ca2 --- /dev/null +++ b/src/Resources/FirmResource.php @@ -0,0 +1,71 @@ +connector->send(new GetFirmDetails($this->frn)); + } catch (RequestException $e) { + if(str_contains($e->getMessage(), 'Firm not found')) { + return false; + } + + throw $e; + } + + return ! $response->failed(); + } + + /** + * Get the details of a firm + * + * @return Response + */ + public function get(): Response + { + return $this->connector->send(new GetFirmDetails($this->frn)); + } + + /** + * Get the individuals of a firm + * + * @return PagedPaginator + */ + public function individuals(): PagedPaginator + { + return $this->connector->paginate(new GetFirmIndividuals($this->frn)); + } + + /** + * Get the addresses of a firm + * + * @return PagedPaginator + */ + public function addresses(): PagedPaginator + { + return $this->connector->paginate(new GetFirmAddresses($this->frn)); + } +} diff --git a/src/Resources/Resource.php b/src/Resources/Resource.php new file mode 100644 index 0000000..f677c32 --- /dev/null +++ b/src/Resources/Resource.php @@ -0,0 +1,19 @@ + MockResponse::make(['Status' => 'FSR-API-02-03-00'], 200), + + ]); + + $fca = new Fca($_ENV['AUTH_EMAIL'], $_ENV['AUTH_KEY']); + $fca->withMockClient($mockClient); + /** @var \Saloon\Http\Paginators\PagedPaginator $response */ + $response = $fca->getEndpoint('/Firm/439536'); + + foreach ($response as $page) { + expect($page->status())->toBe(200); + } +}); diff --git a/tests/Feature/FirmResourceTest.php b/tests/Feature/FirmResourceTest.php new file mode 100644 index 0000000..9229c1a --- /dev/null +++ b/tests/Feature/FirmResourceTest.php @@ -0,0 +1,140 @@ + MockResponse::fixture('firm.details'), + ]); + + $fca = new Fca($_ENV['AUTH_EMAIL'], $_ENV['AUTH_KEY']); + $fca->withMockClient($mockClient); + + $response = $fca->firm(439536)->exists(); + + expect($response)->toBeTrue(); +}); + +it('can get a check a firms fca number does not exists', function () { + $mockClient = new MockClient([ + GetFirmDetails::class => MockResponse::fixture('firm.details.not-found'), + ]); + + $fca = new Fca($_ENV['AUTH_EMAIL'], $_ENV['AUTH_KEY']); + $fca->withMockClient($mockClient); + + $response = $fca->firm(00000)->exists(); + + expect($response)->toBeFalse(); +}); + +it('can get a firms details', function () { + $mockClient = new MockClient([ + GetFirmDetails::class => MockResponse::fixture('firm.details'), + ]); + + $fca = new Fca($_ENV['AUTH_EMAIL'], $_ENV['AUTH_KEY']); + $fca->withMockClient($mockClient); + + $response = $fca->firm(439536)->get(); + + expect($response->status())->toBe(200); + $dto = $response->dto(); + + expect($dto) + ->toBeInstanceOf(CraigPotter\Fca\DataObjects\Firm::class) + ->and($dto->frn) + ->toBe(439536); +}); + +it('throws an exception when the firm is not found', function () { + $mockClient = new MockClient([ + GetFirmDetails::class => MockResponse::fixture('firm.details.not-found'), + ]); + + $fca = new Fca($_ENV['AUTH_EMAIL'], $_ENV['AUTH_KEY']); + $fca->withMockClient($mockClient); + + expect(fn () => $fca->firm(00000)->get()) + ->toThrow(RequestException::class, 'Firm not found'); +}); + +it('can get a firms individuals via pagination', function () { + $mockClient = new MockClient([ + GetFirmIndividuals::class => function (PendingRequest $pendingRequest) { + return match ($pendingRequest->query()->get('pgnp')) { + 1 => MockResponse::fixture('firm.individuals.1'), + 2 => MockResponse::fixture('firm.individuals.2'), + 3 => MockResponse::fixture('firm.individuals.3'), + }; + }, + ]); + + $fca = new Fca($_ENV['AUTH_EMAIL'], $_ENV['AUTH_KEY']); + $fca->withMockClient($mockClient); + + $paginated = $fca->firm(225566)->individuals(); + + $total = []; + foreach ($paginated as $response) { + $data = $response->dto(); + $total = array_merge($total, $data); + + foreach ($data as $individual) { + expect($individual) + ->toBeInstanceOf(Individual::class) + ->toHaveProperties(['name', 'status', 'irn']); + } + } + expect($total)->toHaveCount(47); + $mockClient->assertSentCount(3); +}); + +it('can get a firm addresses', function () { + $mockClient = new MockClient([ + GetFirmAddresses::class => MockResponse::fixture('firm.address'), + ]); + + $fca = new Fca($_ENV['AUTH_EMAIL'], $_ENV['AUTH_KEY']); + $fca->withMockClient($mockClient); + + $paginated = $fca->firm(225566)->addresses(); + + $total = []; + foreach ($paginated as $response) { + $data = $response->dto(); + $total = array_merge($total, $data); + + foreach ($data as $address) { + expect($address) + ->toBeInstanceOf(Address::class) + ->toHaveProperties([ + 'website', + 'contactName', + 'type', + 'addressLine1', + 'addressLine2', + 'addressLine3', + 'addressLine4', + 'postcode', + 'town', + 'county', + 'country', + 'phoneNumber', + ]); + } + } + expect($total)->toHaveCount(2); + $mockClient->assertSentCount(1); +}); diff --git a/tests/Fixtures/Saloon/firm.address.json b/tests/Fixtures/Saloon/firm.address.json new file mode 100644 index 0000000..b8c572c --- /dev/null +++ b/tests/Fixtures/Saloon/firm.address.json @@ -0,0 +1 @@ +{"statusCode":200,"headers":{"Date":"Wed, 14 Jun 2023 22:24:22 GMT","Content-Type":"application\/json;charset=UTF-8","Transfer-Encoding":"chunked","Connection":"keep-alive","CF-Ray":"7d75fba53c872dd5-MAN","CF-Cache-Status":"DYNAMIC","Cache-Control":"no-cache,must-revalidate,max-age=0,no-store,private","Set-Cookie":["CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:21 GMT; Max-Age=31536000","LSKey-c$CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:21 GMT; Max-Age=31536000"],"Strict-Transport-Security":"max-age=63072000; includeSubDomains","Vary":"Accept-Encoding","X-Content-Type-Options":"nosniff","X-XSS-Protection":"1; mode=block","Server":"cloudflare"},"data":"{\"Status\":\"FSR-API-02-02-00\",\"ResultInfo\":{\"page\":\"1\",\"per_page\":\"2\",\"total_count\":\"2\"},\"Message\":\"Ok. Firm Address Found\",\"Data\":[{\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/225566\/Address?Type=PPOB\",\"Website Address\":\"www.abg.net\",\"Phone Number\":\"441513465460\",\"Country\":\"UNITED KINGDOM\",\"Postcode\":\"CH65 3EY\",\"County\":\"Merseyside\",\"Town\":\"Ellesmere Port\",\"Address Line 4\":\"\",\"Address LIne 3\":\"\",\"Address Line 2\":\"Rossmore Business Village, Inward Way\",\"Address Line 1\":\"Unit 14\/16\",\"Address Type\":\"Principal Place of Business\"},{\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/225566\/Address?Type=Complaint\",\"Website Address\":\"\",\"Phone Number\":\"441513465460\",\"Country\":\"UNITED KINGDOM\",\"Postcode\":\"CH65 3EY\",\"County\":\"Merseyside\",\"Town\":\"Ellesmere Port\",\"Address Line 4\":\"\",\"Address LIne 3\":\"\",\"Address Line 2\":\"Rossmore Business Village, Inward Way\",\"Address Line 1\":\"Unit 14\/16\",\"Individual\":\"\",\"Address Type\":\"Complaints Contact\"}]}"} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/firm.details.json b/tests/Fixtures/Saloon/firm.details.json new file mode 100644 index 0000000..e7be71e --- /dev/null +++ b/tests/Fixtures/Saloon/firm.details.json @@ -0,0 +1 @@ +{"statusCode":200,"headers":{"Date":"Wed, 14 Jun 2023 22:24:16 GMT","Content-Type":"application\/json;charset=UTF-8","Transfer-Encoding":"chunked","Connection":"keep-alive","CF-Ray":"7d75fb81e98f0749-MAN","CF-Cache-Status":"DYNAMIC","Cache-Control":"no-cache,must-revalidate,max-age=0,no-store,private","Set-Cookie":["CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:15 GMT; Max-Age=31536000","LSKey-c$CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:15 GMT; Max-Age=31536000"],"Strict-Transport-Security":"max-age=63072000; includeSubDomains","Vary":"Accept-Encoding","X-Content-Type-Options":"nosniff","X-XSS-Protection":"1; mode=block","Server":"cloudflare"},"data":"{\"Status\":\"FSR-API-02-01-00\",\"ResultInfo\":{\"page\":\"1\",\"per_page\":\"1\",\"total_count\":\"1\"},\"Message\":\"Ok. Firm Found\",\"Data\":[{\"Name\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Names\",\"Individuals\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Individuals\",\"Requirements\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Requirements\",\"Permission\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Permissions\",\"Passport\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Passports\",\"Regulators\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Regulators\",\"Appointed Representative\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/AR\",\"Address\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Address\",\"Waivers\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Waivers\",\"Exclusions\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/Exclusions\",\"DisciplinaryHistory\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/439536\/DisciplinaryHistory\",\"System Timestamp\":\"14\/06\/2023 22:24\",\"Exceptional Info Title\":\"\",\"Exceptional Info Body\":\"\",\"Status Effective Date\":\"19\/09\/2005\",\"E-Money Agent Status\":\"\",\"PSD \/ EMD Effective Date\":\"\",\"Client Money Permission\":\"\",\"Sub Status Effective from\":\"\",\"Sub-Status\":\"\",\"Mutual Society Number\":\"\",\"Companies House Number\":\"\",\"MLRs Status Effective Date\":\"\",\"MLRs Status\":\"\",\"E-Money Agent Effective Date\":\"\",\"PSD Agent Effective date\":\"\",\"PSD Agent Status\":\"\",\"PSD \/ EMD Status\":\"\",\"Status\":\"Appointed representative\",\"Business Type\":\"Appointed Representative\",\"Organisation Name\":\"Mortgages 4 Plymouth\",\"FRN\":\"439536\"}]}"} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/firm.details.not-found.json b/tests/Fixtures/Saloon/firm.details.not-found.json new file mode 100644 index 0000000..660d411 --- /dev/null +++ b/tests/Fixtures/Saloon/firm.details.not-found.json @@ -0,0 +1 @@ +{"statusCode":200,"headers":{"Date":"Wed, 14 Jun 2023 22:24:17 GMT","Content-Type":"application\/json;charset=UTF-8","Transfer-Encoding":"chunked","Connection":"keep-alive","CF-Ray":"7d75fb87cae3222a-MAN","CF-Cache-Status":"DYNAMIC","Cache-Control":"no-cache,must-revalidate,max-age=0,no-store,private","Set-Cookie":["CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:16 GMT; Max-Age=31536000","LSKey-c$CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:16 GMT; Max-Age=31536000"],"Strict-Transport-Security":"max-age=63072000; includeSubDomains","Vary":"Accept-Encoding","X-Content-Type-Options":"nosniff","X-XSS-Protection":"1; mode=block","Server":"cloudflare"},"data":"{\"Status\":\"FSR-API-02-01-11\",\"ResultInfo\":null,\"Message\":\"Firm not found\",\"Data\":null}"} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/firm.individuals.1.json b/tests/Fixtures/Saloon/firm.individuals.1.json new file mode 100644 index 0000000..6d24d7e --- /dev/null +++ b/tests/Fixtures/Saloon/firm.individuals.1.json @@ -0,0 +1 @@ +{"statusCode":200,"headers":{"Date":"Wed, 14 Jun 2023 22:24:18 GMT","Content-Type":"application\/json;charset=UTF-8","Transfer-Encoding":"chunked","Connection":"keep-alive","CF-Ray":"7d75fb8c0bbd220e-MAN","CF-Cache-Status":"DYNAMIC","Cache-Control":"no-cache,must-revalidate,max-age=0,no-store,private","Set-Cookie":["CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:17 GMT; Max-Age=31536000","LSKey-c$CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:17 GMT; Max-Age=31536000"],"Strict-Transport-Security":"max-age=63072000; includeSubDomains","Vary":"Accept-Encoding","X-Content-Type-Options":"nosniff","X-XSS-Protection":"1; mode=block","Server":"cloudflare"},"data":"{\"Status\":\"FSR-API-02-05-00\",\"ResultInfo\":{\"Next\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/225566\/Individuals?pgnp=2\",\"page\":\"1\",\"per_page\":\"20\",\"total_count\":\"47\"},\"Message\":\"Ok. Firm Individuals found\",\"Data\":[{\"Status\":\"Approved by regulator\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/AXB00979\",\"IRN\":\"AXB00979\",\"Name\":\"Anita Brennan\"},{\"Status\":\"Approved by regulator\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/EXE00056\",\"IRN\":\"EXE00056\",\"Name\":\"Elizabeth Evans\"},{\"Status\":\"Approved by regulator\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/KXP01224\",\"IRN\":\"KXP01224\",\"Name\":\"Kate Parfitt\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/SMR01191\",\"IRN\":\"SMR01191\",\"Name\":\"Sharon Mary Rogers\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/GXV01110\",\"IRN\":\"GXV01110\",\"Name\":\"Gilles Velcin\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/JWC00013\",\"IRN\":\"JWC00013\",\"Name\":\"John Winston Croasdale\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/MXR00573\",\"IRN\":\"MXR00573\",\"Name\":\"Maureen Vanessa Rathore\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/CRN00001\",\"IRN\":\"CRN00001\",\"Name\":\"Colin Robert Nicol\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/AXW00459\",\"IRN\":\"AXW00459\",\"Name\":\"Alistair West\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/JMK01100\",\"IRN\":\"JMK01100\",\"Name\":\"Joseph Michael Kennedy\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/AJM00173\",\"IRN\":\"AJM00173\",\"Name\":\"Andrew James MacIntyre Moore\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/CXJ91691\",\"IRN\":\"CXJ91691\",\"Name\":\"Christina Anne James\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/GXB00005\",\"IRN\":\"GXB00005\",\"Name\":\"Glynn William Bartley\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/EXH20645\",\"IRN\":\"EXH20645\",\"Name\":\"Edward Richard Hill\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/MGS00023\",\"IRN\":\"MGS00023\",\"Name\":\"Michael Shakespeare\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/MAJ01163\",\"IRN\":\"MAJ01163\",\"Name\":\"Mark Anthony James\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/MES00021\",\"IRN\":\"MES00021\",\"Name\":\"Martin Edward Shaw\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/SFS01047\",\"IRN\":\"SFS01047\",\"Name\":\"Steven Frank Stockwell\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/NAB00023\",\"IRN\":\"NAB00023\",\"Name\":\"Nigel Andrew Benn\"},{\"Status\":\"Approved by regulator\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/PDB00010\",\"IRN\":\"PDB00010\",\"Name\":\"Paul Douglas Beard\"}]}"} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/firm.individuals.2.json b/tests/Fixtures/Saloon/firm.individuals.2.json new file mode 100644 index 0000000..5ac0ceb --- /dev/null +++ b/tests/Fixtures/Saloon/firm.individuals.2.json @@ -0,0 +1 @@ +{"statusCode":200,"headers":{"Date":"Wed, 14 Jun 2023 22:24:20 GMT","Content-Type":"application\/json;charset=UTF-8","Transfer-Encoding":"chunked","Connection":"keep-alive","CF-Ray":"7d75fb95fbef220e-MAN","CF-Cache-Status":"DYNAMIC","Cache-Control":"no-cache,must-revalidate,max-age=0,no-store,private","Set-Cookie":["CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:18 GMT; Max-Age=31536000","LSKey-c$CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:18 GMT; Max-Age=31536000"],"Strict-Transport-Security":"max-age=63072000; includeSubDomains","Vary":"Accept-Encoding","X-Content-Type-Options":"nosniff","X-XSS-Protection":"1; mode=block","Server":"cloudflare"},"data":"{\"Status\":\"FSR-API-02-05-00\",\"ResultInfo\":{\"Previous\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/225566\/Individuals?pgnp=1\",\"Next\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/225566\/Individuals?pgnp=3\",\"page\":\"2\",\"per_page\":\"20\",\"total_count\":\"47\"},\"Message\":\"Ok. Firm Individuals found\",\"Data\":[{\"Status\":\"Approved by regulator\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/KXP01224\",\"IRN\":\"KXP01224\",\"Name\":\"Kate Parfitt\"},{\"Status\":\"Approved by regulator\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/RCL00009\",\"IRN\":\"RCL00009\",\"Name\":\"Raymond Charles Louis Lepretre\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/GXB00005\",\"IRN\":\"GXB00005\",\"Name\":\"Glynn William Bartley\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/GXV01110\",\"IRN\":\"GXV01110\",\"Name\":\"Gilles Velcin\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/JWC00013\",\"IRN\":\"JWC00013\",\"Name\":\"John Winston Croasdale\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/AJM00173\",\"IRN\":\"AJM00173\",\"Name\":\"Andrew James MacIntyre Moore\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/JXV00005\",\"IRN\":\"JXV00005\",\"Name\":\"Jean-Marc Vouillemin\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/NAB00023\",\"IRN\":\"NAB00023\",\"Name\":\"Nigel Andrew Benn\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/PRT01055\",\"IRN\":\"PRT01055\",\"Name\":\"Philip Richard Owen Teague\"},{\"Status\":\"Approved by regulator;Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/GRH00010\",\"IRN\":\"GRH00010\",\"Name\":\"Guy Redwood Hammett\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/DSM00004\",\"IRN\":\"DSM00004\",\"Name\":\"David Somerville McGowan\"},{\"Status\":\"Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/MXY01380\",\"IRN\":\"MXY01380\",\"Name\":\"Matthew Young\"},{\"Status\":\"Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/PES00029\",\"IRN\":\"PES00029\",\"Name\":\"Peter Edwin James Smith\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/PCJ01030\",\"IRN\":\"PCJ01030\",\"Name\":\"Paula Christine Jack\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/SXJ00190\",\"IRN\":\"SXJ00190\",\"Name\":\"Senga Jones\"},{\"Status\":\"Certified \/ assessed by firm\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/IAW01025\",\"IRN\":\"IAW01025\",\"Name\":\"Ian Alistair Watson\"},{\"Status\":\"Approved by regulator\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/PDB00010\",\"IRN\":\"PDB00010\",\"Name\":\"Paul Douglas Beard\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/IDB00010\",\"IRN\":\"IDB00010\",\"Name\":\"Ian David Brook\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/EHW01015\",\"IRN\":\"EHW01015\",\"Name\":\"Elizabeth Helen Wilkinson\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/MXD00105\",\"IRN\":\"MXD00105\",\"Name\":\"Michael Doyle\"}]}"} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/firm.individuals.3.json b/tests/Fixtures/Saloon/firm.individuals.3.json new file mode 100644 index 0000000..12f6908 --- /dev/null +++ b/tests/Fixtures/Saloon/firm.individuals.3.json @@ -0,0 +1 @@ +{"statusCode":200,"headers":{"Date":"Wed, 14 Jun 2023 22:24:21 GMT","Content-Type":"application\/json;charset=UTF-8","Transfer-Encoding":"chunked","Connection":"keep-alive","CF-Ray":"7d75fb9eea53220e-MAN","CF-Cache-Status":"DYNAMIC","Cache-Control":"no-cache,must-revalidate,max-age=0,no-store,private","Set-Cookie":["CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:20 GMT; Max-Age=31536000","LSKey-c$CookieConsentPolicy=0:1; path=\/; expires=Thu, 13-Jun-2024 22:24:20 GMT; Max-Age=31536000"],"Strict-Transport-Security":"max-age=63072000; includeSubDomains","Vary":"Accept-Encoding","X-Content-Type-Options":"nosniff","X-XSS-Protection":"1; mode=block","Server":"cloudflare"},"data":"{\"Status\":\"FSR-API-02-05-00\",\"ResultInfo\":{\"Previous\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Firm\/225566\/Individuals?pgnp=2\",\"page\":\"3\",\"per_page\":\"20\",\"total_count\":\"47\"},\"Message\":\"Ok. Firm Individuals found\",\"Data\":[{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/KER01030\",\"IRN\":\"KER01030\",\"Name\":\"Katherine Butler\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/CDH01072\",\"IRN\":\"CDH01072\",\"Name\":\"Christine Dora Haslam\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/MED01046\",\"IRN\":\"MED01046\",\"Name\":\"Mark Evan Dutton\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/RGK00007\",\"IRN\":\"RGK00007\",\"Name\":\"Roger Graham Knapton\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/AXT00018\",\"IRN\":\"AXT00018\",\"Name\":\"Alan Taylor\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/CJG00052\",\"IRN\":\"CJG00052\",\"Name\":\"Christopher John Grenan\"},{\"Status\":\"Regulatory approval no longer required\",\"URL\":\"https:\/\/register.fca.org.uk\/services\/V0.1\/Individuals\/OPG01005\",\"IRN\":\"OPG01005\",\"Name\":\"Olivier Paul Joseph Grau\"}]}"} \ No newline at end of file diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..fdc3de0 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,45 @@ +load(); + +/* +|-------------------------------------------------------------------------- +| Test Case +|-------------------------------------------------------------------------- +| +| The closure you provide to your test functions is always bound to a specific PHPUnit test +| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may +| need to change it using the "uses()" function to bind a different classes or traits. +| +*/ + +// uses(Tests\TestCase::class)->in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/