From baf2c91b9c8e7fb7cb1c4c08d0c27d89c3a07076 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Sat, 29 Feb 2020 21:36:57 +0000 Subject: [PATCH 01/17] SDK-1472: Move sandbox into standalone repository --- .dependabot/config.yml | 12 + .gitignore | 106 +----- .php_cs.dist | 24 ++ .phpcs.xml.dist | 16 + .travis.yml | 42 +++ CONTRIBUTING.md | 48 +++ README.md | 55 ++- composer.json | 54 +++ phpstan.neon.dist | 4 + phpunit.xml | 21 ++ sonar-project.properties | 12 + src/Exception/ResponseException.php | 9 + .../Attribute/SandboxAgeVerification.php | 39 ++ .../Request/Attribute/SandboxAnchor.php | 51 +++ .../Request/Attribute/SandboxAttribute.php | 62 ++++ .../Attribute/SandboxDocumentDetails.php | 21 ++ src/Profile/Request/TokenRequest.php | 53 +++ src/Profile/Request/TokenRequestBuilder.php | 336 ++++++++++++++++++ src/Profile/Response/TokenResponse.php | 71 ++++ src/Profile/Service.php | 82 +++++ src/SandboxClient.php | 49 +++ .../Attribute/SandboxAgeVerificationTest.php | 90 +++++ .../Request/Attribute/SandboxAnchorTest.php | 60 ++++ .../Attribute/SandboxAttributeTest.php | 49 +++ .../Attribute/SandboxDocumentDetailsTest.php | 42 +++ .../Request/TokenRequestBuilderTest.php | 298 ++++++++++++++++ tests/Profile/Request/TokenRequestTest.php | 76 ++++ tests/Profile/Response/TokenResponseTest.php | 83 +++++ tests/Profile/ServiceTest.php | 60 ++++ tests/SandboxClientTest.php | 57 +++ tests/TestCase.php | 11 + tests/TestData.php | 12 + tests/sample-data/connect-token.txt | 1 + tests/sample-data/pem-auth-key.txt | 1 + tests/sample-data/test.pem | 27 ++ 35 files changed, 1935 insertions(+), 99 deletions(-) create mode 100644 .dependabot/config.yml create mode 100644 .php_cs.dist create mode 100644 .phpcs.xml.dist create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100755 composer.json create mode 100644 phpstan.neon.dist create mode 100755 phpunit.xml create mode 100644 sonar-project.properties create mode 100644 src/Exception/ResponseException.php create mode 100644 src/Profile/Request/Attribute/SandboxAgeVerification.php create mode 100644 src/Profile/Request/Attribute/SandboxAnchor.php create mode 100644 src/Profile/Request/Attribute/SandboxAttribute.php create mode 100644 src/Profile/Request/Attribute/SandboxDocumentDetails.php create mode 100644 src/Profile/Request/TokenRequest.php create mode 100644 src/Profile/Request/TokenRequestBuilder.php create mode 100644 src/Profile/Response/TokenResponse.php create mode 100644 src/Profile/Service.php create mode 100644 src/SandboxClient.php create mode 100644 tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php create mode 100644 tests/Profile/Request/Attribute/SandboxAnchorTest.php create mode 100644 tests/Profile/Request/Attribute/SandboxAttributeTest.php create mode 100644 tests/Profile/Request/Attribute/SandboxDocumentDetailsTest.php create mode 100644 tests/Profile/Request/TokenRequestBuilderTest.php create mode 100644 tests/Profile/Request/TokenRequestTest.php create mode 100644 tests/Profile/Response/TokenResponseTest.php create mode 100644 tests/Profile/ServiceTest.php create mode 100644 tests/SandboxClientTest.php create mode 100644 tests/TestCase.php create mode 100644 tests/TestData.php create mode 100755 tests/sample-data/connect-token.txt create mode 100644 tests/sample-data/pem-auth-key.txt create mode 100644 tests/sample-data/test.pem diff --git a/.dependabot/config.yml b/.dependabot/config.yml new file mode 100644 index 0000000..dd7a819 --- /dev/null +++ b/.dependabot/config.yml @@ -0,0 +1,12 @@ +version: 1 +update_configs: + - package_manager: "php:composer" + directory: "/" + update_schedule: "live" + target_branch: "development" + + default_reviewers: + - "echarrod" + - "davidgrayston" + - "emmas-yoti" + - "MrBurtyyy" diff --git a/.gitignore b/.gitignore index 6704566..f2cca8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,104 +1,16 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* +.idea/ +composer.lock +/vendor -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +.phpcs.xml +phpcs.xml -# Runtime data -pids -*.pid -*.seed -*.pid.lock +.phpunit.result.cache -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +.php_cs.cache -# Coverage directory used by tools like istanbul coverage -*.lcov -# nyc test coverage -.nyc_output +.scannerwork -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port +.DS_Store diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000..af0a1cb --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,24 @@ +exclude('Protobuf') + ->in(__DIR__ . '/src') + ->in(__DIR__ . '/tests') +; + +return PhpCsFixer\Config::create() + ->setRules([ + 'array_syntax' => ['syntax' => 'short'], + 'no_unused_imports' => true, + 'ordered_imports' => [ + 'imports_order' => [ + 'const', + 'class', + 'function', + ], + ], + 'php_unit_fqcn_annotation' => true, + 'phpdoc_return_self_reference' => true, + 'phpdoc_scalar' => true, + ]) + ->setFinder($finder) +; diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist new file mode 100644 index 0000000..51d7c69 --- /dev/null +++ b/.phpcs.xml.dist @@ -0,0 +1,16 @@ + + + + + + src + tests + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8b937bd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,42 @@ +language: php + +git: + depth: 1 + +jobs: + include: + - &test + stage: Test + php: "7.4" + os: linux + install: + - travis_retry composer self-update + - travis_retry composer install --no-interaction --prefer-source --dev + script: + - composer test + - composer lint + - &compatibility + <<: *test + stage: Compatibility + php: "7.1" + - <<: *compatibility + php: "7.2" + - <<: *compatibility + php: "7.3" + - <<: *test + stage: Coverage + name: Coveralls + if: type = pull_request OR branch = master + script: + - composer coveralls + - <<: *test + stage: Analyze + name: Sonarcloud + dist: trusty + addons: + sonarcloud: + organization: "getyoti" + if: type == pull_request OR branch = master + script: + - composer coverage-clover + - sonar-scanner diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a312a07 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing + +After cloning the repository, run `composer install` to install dependencies. + +> See for Composer installation instructions. + +## Testing + +Running the tests: + +```shell +composer test +``` + +See [.travis.yml](.travis.yml) for PHP versions we currently support. + +Generate and view coverage report by running: + +```shell +composer coverage-html +open ./coverage/report/index.html +``` + +To generate clover report run: + +```shell +composer coverage-clover +``` + +## Style guide + +We follow the [PSR-12 Style Guide](https://www.php-fig.org/psr/psr-12/), which +is configured in [.phpcs.xml.dist](.phpcs.xml.dist). + +Additional checks are configured in [.php_cs.dist](.php_cs.dist) - See + for further information. + +Coding style can be verified by running: + +```shell +composer lint +``` + +> Note: Windows users that have enabled `core.autocrlf` in their git + configuration can disable the `Generic.Files.LineEndings` rule by + copying [.phpcs.xml.dist](.phpcs.xml.dist) file to `.phpcs.xml` + and adding an exclusion. This sniff will always be included on + Travis CI. diff --git a/README.md b/README.md index 7e0ec04..078a209 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,53 @@ -# yoti-php-sdk-sandbox -Yoti PHP SDK Sandbox +# Yoti PHP Sandbox SDK + +This repo contains the tools you need to test your Yoti integration. + +## Enabling the SDK + +Add the Yoti SDK dependency: + +```console +$ composer require yoti/yoti-php-sdk-sandbox +``` + +## Client Initialisation + +The YotiClient is the SDK entry point. To initialise it you need to include the following snippet inside your endpoint initialisation section: + +```php +setGivenNames() + ->build(); + + $token = $client->setupSharingProfile($tokenRequest)->getToken(); +} catch(Exception $e) { + // Handle unhappy path +} +``` diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..319ab9b --- /dev/null +++ b/composer.json @@ -0,0 +1,54 @@ +{ + "name": "yoti/yoti-php-sdk-sandbox", + "description": "Yoti PHP SDK Sandbox", + "keywords": [ + "yoti", + "sdk" + ], + "homepage": "https://yoti.com", + "license": "MIT", + "require": { + "yoti/yoti-php-sdk": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5", + "squizlabs/php_codesniffer": "^3.4", + "friendsofphp/php-cs-fixer": "^2.15", + "brainmaestro/composer-git-hooks": "^2.8", + "php-coveralls/php-coveralls": "^2.1", + "phpstan/phpstan-strict-rules": "^0.12.1", + "phpstan/extension-installer": "^1.0" + }, + "autoload": { + "psr-4": { + "Yoti\\Sandbox\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Yoti\\Sandbox\\Test\\": "tests" + } + }, + "scripts": { + "cghooks": "cghooks", + "test": "phpunit", + "coverage-clover": "phpunit --coverage-clover ./coverage/coverage.xml", + "coverage-html": "phpunit --coverage-html ./coverage/report", + "coveralls": [ + "@coverage-clover", + "php-coveralls --coverage_clover ./coverage/coverage.xml --json_path ./coverage/coveralls-upload.json" + ], + "lint": [ + "phpcs", + "php-cs-fixer fix --config=.php_cs.dist -v --dry-run --using-cache=no --diff-format=udiff --ansi", + "phpstan analyse" + ], + "post-install-cmd": "cghooks add --ignore-lock", + "post-update-cmd": "cghooks update" + }, + "extra": { + "hooks": { + "pre-commit": "composer test && composer lint" + } + } +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..776ccd8 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + level: max + paths: + - src diff --git a/phpunit.xml b/phpunit.xml new file mode 100755 index 0000000..c8f69e7 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,21 @@ + + + + + tests/ + + + + + src + + + + + + diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..f90f820 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,12 @@ +sonar.host.url = https://sonarcloud.io +sonar.organization = getyoti + +sonar.projectKey = getyoti:php-sandbox +sonar.projectName = PHP SDK Sandbox +sonar.projectVersion = 3.1.0 + +sonar.language = php +sonar.sources=src +sonar.tests=tests +sonar.php.coverage.reportPaths=coverage/coverage.xml +sonar.verbose = true diff --git a/src/Exception/ResponseException.php b/src/Exception/ResponseException.php new file mode 100644 index 0000000..4e89570 --- /dev/null +++ b/src/Exception/ResponseException.php @@ -0,0 +1,9 @@ +format('Y-m-d'), + $derivation, + true, + $anchors + ); + } + + public function setAgeOver(int $age): void + { + $this->derivation = sprintf(self::AGE_OVER_FORMAT, $age); + } + + public function setAgeUnder(int $age): void + { + $this->derivation = sprintf(self::AGE_UNDER_FORMAT, $age); + } +} diff --git a/src/Profile/Request/Attribute/SandboxAnchor.php b/src/Profile/Request/Attribute/SandboxAnchor.php new file mode 100644 index 0000000..b920474 --- /dev/null +++ b/src/Profile/Request/Attribute/SandboxAnchor.php @@ -0,0 +1,51 @@ +type = $type; + $this->value = $value; + $this->subtype = $subType; + $this->timestamp = $timestamp; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'type' => $this->type, + 'value' => $this->value, + 'sub_type' => $this->subtype, + 'timestamp' => DateTime::dateTimeToTimestamp($this->timestamp), + ]; + } +} diff --git a/src/Profile/Request/Attribute/SandboxAttribute.php b/src/Profile/Request/Attribute/SandboxAttribute.php new file mode 100644 index 0000000..e2dfbb4 --- /dev/null +++ b/src/Profile/Request/Attribute/SandboxAttribute.php @@ -0,0 +1,62 @@ +name = $name; + $this->value = $value; + $this->derivation = $derivation; + $this->optional = $optional; + + Validation::isArrayOfType($anchors, [\Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor::class], 'anchors'); + $this->anchors = $anchors; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'name' => $this->name, + 'value' => $this->value, + 'derivation' => $this->derivation, + 'optional' => $this->optional, + 'anchors' => $this->anchors, + ]; + } +} diff --git a/src/Profile/Request/Attribute/SandboxDocumentDetails.php b/src/Profile/Request/Attribute/SandboxDocumentDetails.php new file mode 100644 index 0000000..047c35b --- /dev/null +++ b/src/Profile/Request/Attribute/SandboxDocumentDetails.php @@ -0,0 +1,21 @@ +getType() . ' ' . $this->getIssuingCountry() . ' ' . $this->getDocumentNumber() . ' '; + + $expirationDate = $this->getExpirationDate(); + $value .= (null !== $expirationDate) ? $expirationDate->format('Y-m-d') : '-'; + $value .= (null !== $this->getIssuingAuthority()) ? ' ' . $this->getIssuingAuthority() : ''; + + return $value; + } +} diff --git a/src/Profile/Request/TokenRequest.php b/src/Profile/Request/TokenRequest.php new file mode 100644 index 0000000..4d7c6c1 --- /dev/null +++ b/src/Profile/Request/TokenRequest.php @@ -0,0 +1,53 @@ +rememberMeId = $rememberMeId; + + Validation::isArrayOfType($sandboxAttributes, [ SandboxAttribute::class ], 'sandboxAttributes'); + $this->sandboxAttributes = $sandboxAttributes; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'remember_me_id' => $this->rememberMeId, + 'profile_attributes' => $this->sandboxAttributes, + ]; + } + + /** + * @return Payload + */ + public function getPayload(): Payload + { + return Payload::fromJsonData($this); + } +} diff --git a/src/Profile/Request/TokenRequestBuilder.php b/src/Profile/Request/TokenRequestBuilder.php new file mode 100644 index 0000000..e6f8394 --- /dev/null +++ b/src/Profile/Request/TokenRequestBuilder.php @@ -0,0 +1,336 @@ +rememberMeId = $value; + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setFullName(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_FULL_NAME, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setFamilyName(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_FAMILY_NAME, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setGivenNames(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_GIVEN_NAMES, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param \DateTime $dateTime + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setDateOfBirth(\DateTime $dateTime, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_DATE_OF_BIRTH, + $dateTime->format('Y-m-d'), + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setGender(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_GENDER, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setNationality(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_NATIONALITY, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setPhoneNumber(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_PHONE_NUMBER, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setSelfie(string $value, bool $optional = false, array $anchors = []): self + { + $base64Selfie = base64_encode($value); + return $this->setBase64Selfie($base64Selfie, $optional, $anchors); + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setBase64Selfie(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_SELFIE, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setEmailAddress(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_EMAIL_ADDRESS, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setPostalAddress(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_POSTAL_ADDRESS, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setStructuredPostalAddress(string $value, bool $optional = false, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_STRUCTURED_POSTAL_ADDRESS, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxDocumentDetails $documentDetails + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setDocumentDetails( + SandboxDocumentDetails $documentDetails, + bool $optional = true, + array $anchors = [] + ): self { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_DOCUMENT_DETAILS, + $documentDetails->getValue(), + '', + $optional, + $anchors + )); + return $this; + } + + /** + * @param string $value + * @param bool $optional + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return $this + */ + public function setDocumentDetailsWithString(string $value, bool $optional = true, array $anchors = []): self + { + $this->addAttribute($this->createAttribute( + UserProfile::ATTR_DOCUMENT_DETAILS, + $value, + '', + $optional, + $anchors + )); + return $this; + } + + public function setAgeVerification(SandboxAgeVerification $ageVerification): self + { + $this->addAttribute($ageVerification); + return $this; + } + + public function addAttribute(SandboxAttribute $attribute): self + { + $this->sandboxAttributes[] = $attribute; + return $this; + } + + /** + * @param string $name + * @param string $value + * @param string $derivation + * Empty value means there is no derivation for this attribute + * @param bool $optional + * false value means this attribute is required + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + * + * @return SandboxAttribute + */ + private function createAttribute( + string $name, + string $value, + string $derivation, + bool $optional, + array $anchors + ): SandboxAttribute { + return new SandboxAttribute($name, $value, $derivation, $optional, $anchors); + } + + /** + * @return \Yoti\Sandbox\Profile\Request\TokenRequest + */ + public function build(): TokenRequest + { + return new TokenRequest($this->rememberMeId, $this->sandboxAttributes); + } +} diff --git a/src/Profile/Response/TokenResponse.php b/src/Profile/Response/TokenResponse.php new file mode 100644 index 0000000..5f3d9e1 --- /dev/null +++ b/src/Profile/Response/TokenResponse.php @@ -0,0 +1,71 @@ +processData($response); + $this->token = $responseArr['token']; + } + + /** + * @return string + */ + public function getToken(): string + { + return $this->token; + } + + /** + * @param \Psr\Http\Message\ResponseInterface $response + * + * @return array + * + * @throws ResponseException + */ + private function processData(ResponseInterface $response): array + { + $this->checkResponseStatus($response->getStatusCode()); + + // Get decoded response data. + $responseArr = Json::decode((string) $response->getBody(), true); + + if (!isset($responseArr['token'])) { + throw new ResponseException('Token key is missing', 404); + } + + return $responseArr; + } + + /** + * @param int $httpCode + * + * @throws ResponseException + */ + private function checkResponseStatus(int $httpCode): void + { + if ($httpCode !== 201) { + throw new ResponseException("Server responded with {$httpCode}", $httpCode); + } + } +} diff --git a/src/Profile/Service.php b/src/Profile/Service.php new file mode 100644 index 0000000..4652369 --- /dev/null +++ b/src/Profile/Service.php @@ -0,0 +1,82 @@ +sdkId = $sdkId; + $this->pemFile = $pemFile; + $this->config = $config; + } + + /** + * @param \Yoti\Sandbox\Profile\Request\TokenRequest $tokenRequest + * + * @return string + */ + public function setupSharingProfile(TokenRequest $tokenRequest): string + { + // Request endpoint + $endpoint = sprintf(self::TOKEN_REQUEST_ENDPOINT_FORMAT, $this->sdkId); + $response = (new RequestBuilder($this->config)) + ->withBaseUrl($this->config->getApiUrl() ?? self::SANDBOX_URL) + ->withEndpoint($endpoint) + ->withPost() + ->withPemFile($this->pemFile) + ->withPayload($tokenRequest->getPayload()) + ->withQueryParam('appId', $this->sdkId) + ->build() + ->execute(); + + return (new TokenResponse($response))->getToken(); + } +} diff --git a/src/SandboxClient.php b/src/SandboxClient.php new file mode 100644 index 0000000..5846ce3 --- /dev/null +++ b/src/SandboxClient.php @@ -0,0 +1,49 @@ + $options (optional) + * SDK configuration options - {@see \Yoti\Util\Config} for available options. + */ + public function __construct( + string $sdkId, + string $pem, + array $options = [] + ) { + $pemFile = Pemfile::resolveFromString($pem); + $config = new Config($options); + + $this->profileService = new ProfileService($sdkId, $pemFile, $config); + } + + /** + * @param \Yoti\Sandbox\Profile\Request\TokenRequest $tokenRequest + * + * @return string + */ + public function setupSharingProfile(TokenRequest $tokenRequest): string + { + return $this->profileService->setupSharingProfile($tokenRequest); + } +} diff --git a/tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php b/tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php new file mode 100644 index 0000000..366a206 --- /dev/null +++ b/tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php @@ -0,0 +1,90 @@ +ageVerification = new SandboxAgeVerification( + (new \DateTime())->setTimestamp(self::SOME_TIMESTAMP), + self::SOME_DERIVATION + ); + } + + /** + * @covers ::jsonSerialize + * @covers ::__construct + */ + public function testJsonSerialize() + { + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'name' => UserProfile::ATTR_DATE_OF_BIRTH, + 'value' => self::SOME_TIMESTAMP_DATE_STRING, + 'derivation' => self::SOME_DERIVATION, + 'optional' => true, + 'anchors' => [], + ]), + json_encode($this->ageVerification) + ); + } + + /** + * @covers ::setAgeOver + * @covers ::__construct + */ + public function testGetAgeOver() + { + $this->ageVerification->setAgeOver(20); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'name' => UserProfile::ATTR_DATE_OF_BIRTH, + 'value' => self::SOME_TIMESTAMP_DATE_STRING, + 'derivation' => 'age_over:20', + 'optional' => true, + 'anchors' => [], + ]), + json_encode($this->ageVerification) + ); + } + + /** + * @covers ::setAgeUnder + * @covers ::__construct + */ + public function testAgeUnder() + { + $this->ageVerification->setAgeUnder(30); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'name' => UserProfile::ATTR_DATE_OF_BIRTH, + 'value' => self::SOME_TIMESTAMP_DATE_STRING, + 'derivation' => 'age_under:30', + 'optional' => true, + 'anchors' => [], + ]), + json_encode($this->ageVerification) + ); + } +} diff --git a/tests/Profile/Request/Attribute/SandboxAnchorTest.php b/tests/Profile/Request/Attribute/SandboxAnchorTest.php new file mode 100644 index 0000000..edfde98 --- /dev/null +++ b/tests/Profile/Request/Attribute/SandboxAnchorTest.php @@ -0,0 +1,60 @@ +assertJsonStringEqualsJsonString( + json_encode([ + 'type' => self::SOME_TYPE, + 'value' => self::SOME_VALUE, + 'sub_type' => self::SOME_SUB_TYPE, + 'timestamp' => $timestamp, + ]), + json_encode($anchor) + ); + } + + /** + * Provides valid microsecond timestamps and their expected RFC3339 representation with microseconds. + */ + public function validTimestampProvider() + { + return [ + [ 1523538872835537, '2018-04-12T13:14:32.835537+00:00' ], + [ -1571630945999999, '1920-03-13T19:50:54.000001+00:00' ], + [ 1571630945999999, '2019-10-21T04:09:05.999999+00:00' ], + [ 1571630945000000, '2019-10-21T04:09:05+00:00' ], + [ 1571630945000000, '2019-10-21T04:09:05' ], + [ 1571616000000000, '2019-10-21' ], + [ -1571702400000000, '1920-03-13' ], + ]; + } +} diff --git a/tests/Profile/Request/Attribute/SandboxAttributeTest.php b/tests/Profile/Request/Attribute/SandboxAttributeTest.php new file mode 100644 index 0000000..a686d55 --- /dev/null +++ b/tests/Profile/Request/Attribute/SandboxAttributeTest.php @@ -0,0 +1,49 @@ +attribute = new SandboxAttribute( + self::SOME_NAME, + self::SOME_VALUE, + '' + ); + } + + /** + * @covers ::jsonSerialize + * @covers ::__construct + */ + public function testJsonSerialize() + { + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'name' => self::SOME_NAME, + 'value' => self::SOME_VALUE, + 'derivation' => '', + 'optional' => false, + 'anchors' => [], + ]), + json_encode($this->attribute) + ); + } +} diff --git a/tests/Profile/Request/Attribute/SandboxDocumentDetailsTest.php b/tests/Profile/Request/Attribute/SandboxDocumentDetailsTest.php new file mode 100644 index 0000000..82063fc --- /dev/null +++ b/tests/Profile/Request/Attribute/SandboxDocumentDetailsTest.php @@ -0,0 +1,42 @@ +assertEquals( + $someDocumentDetailsString, + $sandboxDocumentDetails->getValue() + ); + } + + /** + * Provides valid document details string values. + */ + public function documentDetailsDataProvider() + { + return [ + ['PASSPORT GBR 01234567 2020-01-01 some_authority'], + ['PASSPORT GBR 01234567 2020-01-01'], + ['PASSPORT GBR 01234567 - some_authority'], + ['PASSPORT GBR 01234567 -'], + ]; + } +} diff --git a/tests/Profile/Request/TokenRequestBuilderTest.php b/tests/Profile/Request/TokenRequestBuilderTest.php new file mode 100644 index 0000000..593dfb4 --- /dev/null +++ b/tests/Profile/Request/TokenRequestBuilderTest.php @@ -0,0 +1,298 @@ + 'some type', + 'sub_type' => 'some sub type', + 'value' => 'some anchor value', + 'timestamp' => 1575998454, + ]; + + /** + * @var \Yoti\Sandbox\Profile\RequestBuilders + */ + private $requestBuilder; + + public function setup(): void + { + $this->requestBuilder = new TokenRequestBuilder(); + } + + /** + * @covers ::build + */ + public function testBuild() + { + $tokenRequest = $this->requestBuilder->build(); + + $this->assertInstanceOf(TokenRequest::class, $tokenRequest); + } + + /** + * @covers ::setRememberMeId + */ + public function testSetRememberMeId() + { + $this->requestBuilder->setRememberMeId(self::SOME_REMEMBER_ME_ID); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => self::SOME_REMEMBER_ME_ID, + 'profile_attributes' => [] + ]), + json_encode($tokenRequest) + ); + } + + /** + * @covers ::setFullName + * @covers ::setFamilyName + * @covers ::setGivenNames + * @covers ::setGender + * @covers ::setNationality + * @covers ::setPhoneNumber + * @covers ::setBase64Selfie + * @covers ::setEmailAddress + * @covers ::setPostalAddress + * @covers ::setStructuredPostalAddress + * @covers ::createAttribute + * @covers ::addAttribute + * + * @dataProvider stringAttributeSettersDataProvider + */ + public function testStringAttributeSetters($setterMethod, $name) + { + $this->requestBuilder->{$setterMethod}(self::SOME_STRING_VALUE); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => null, + 'profile_attributes' => [ + [ + 'name' => $name, + 'value' => self::SOME_STRING_VALUE, + 'derivation' => '', + 'optional' => false, + 'anchors' => [], + ] + ] + ]), + json_encode($tokenRequest) + ); + } + + /** + * @covers ::setFullName + * @covers ::setFamilyName + * @covers ::setGivenNames + * @covers ::setGender + * @covers ::setNationality + * @covers ::setPhoneNumber + * @covers ::setBase64Selfie + * @covers ::setEmailAddress + * @covers ::setPostalAddress + * @covers ::setStructuredPostalAddress + * @covers ::createAttribute + * @covers ::addAttribute + * + * @dataProvider stringAttributeSettersDataProvider + */ + public function testStringAttributeSettersWithAnchor($setterMethod, $name) + { + $someAnchor = $this->createMock(SandboxAnchor::class); + $someAnchor->method('jsonSerialize')->willReturn(self::SOME_ANCHOR_JSON_DATA); + + $this->requestBuilder->{$setterMethod}(self::SOME_STRING_VALUE, true, [ $someAnchor ]); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => null, + 'profile_attributes' => [ + [ + 'name' => $name, + 'value' => self::SOME_STRING_VALUE, + 'derivation' => '', + 'optional' => true, + 'anchors' => [ self::SOME_ANCHOR_JSON_DATA ], + ] + ] + ]), + json_encode($tokenRequest) + ); + } + + /** + * Provides test data for attribute setters. + * + * @return array + */ + public function stringAttributeSettersDataProvider() + { + return [ + ['setFullName', 'full_name'], + ['setFamilyName', 'family_name'], + ['setGivenNames', 'given_names'], + ['setGender', 'gender'], + ['setNationality', 'nationality'], + ['setPhoneNumber', 'phone_number'], + ['setBase64Selfie', 'selfie'], + ['setEmailAddress', 'email_address'], + ['setPostalAddress', 'postal_address'], + ['setStructuredPostalAddress', 'structured_postal_address'], + ]; + } + + /** + * @covers ::setDateOfBirth + */ + public function testSetDateOfBirth() + { + $someDOB = new \DateTime(); + $this->requestBuilder->setDateOfBirth($someDOB); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => null, + 'profile_attributes' => [ + [ + 'name' => 'date_of_birth', + 'value' => $someDOB->format('Y-m-d'), + 'derivation' => '', + 'optional' => false, + 'anchors' => [], + ] + ] + ]), + json_encode($tokenRequest) + ); + } + + /** + * @covers ::setSelfie + */ + public function testSetSelfie() + { + $this->requestBuilder->setSelfie(self::SOME_STRING_VALUE); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => null, + 'profile_attributes' => [ + [ + 'name' => 'selfie', + 'value' => base64_encode(self::SOME_STRING_VALUE), + 'derivation' => '', + 'optional' => false, + 'anchors' => [], + ] + ] + ]), + json_encode($tokenRequest) + ); + } + + /** + * @covers ::setDocumentDetails + */ + public function testSetDocumentDetails() + { + $someDocumentDetails = $this->createMock(SandboxDocumentDetails::class); + $someDocumentDetails->method('getValue')->willReturn(self::SOME_STRING_VALUE); + + $this->requestBuilder->setDocumentDetails($someDocumentDetails); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => null, + 'profile_attributes' => [ + [ + 'name' => 'document_details', + 'value' => self::SOME_STRING_VALUE, + 'derivation' => '', + 'optional' => true, + 'anchors' => [], + ] + ] + ]), + json_encode($tokenRequest) + ); + } + + + /** + * @covers ::setDocumentDetailsWithString + */ + public function testSetDocumentDetailsWithString() + { + $this->requestBuilder->setDocumentDetailsWithString(self::SOME_STRING_VALUE); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => null, + 'profile_attributes' => [ + [ + 'name' => 'document_details', + 'value' => self::SOME_STRING_VALUE, + 'derivation' => '', + 'optional' => true, + 'anchors' => [], + ] + ] + ]), + json_encode($tokenRequest) + ); + } + + /** + * @covers ::setAgeVerification + */ + public function testSetAgeVerification() + { + $someAgeVerification = $this->createMock(SandboxAgeVerification::class); + $someAgeVerification->method('jsonSerialize')->willReturn([ + 'name' => self::SOME_NAME, + 'value' => self::SOME_STRING_VALUE, + ]); + + $this->requestBuilder->setAgeVerification($someAgeVerification); + $tokenRequest = $this->requestBuilder->build(); + + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => null, + 'profile_attributes' => [ + [ + 'name' => self::SOME_NAME, + 'value' => self::SOME_STRING_VALUE, + ] + ] + ]), + json_encode($tokenRequest) + ); + } +} diff --git a/tests/Profile/Request/TokenRequestTest.php b/tests/Profile/Request/TokenRequestTest.php new file mode 100644 index 0000000..fd921e0 --- /dev/null +++ b/tests/Profile/Request/TokenRequestTest.php @@ -0,0 +1,76 @@ + 'some-name', + 'value' => 'some-value', + 'derivation' => '', + 'optional' => false, + 'anchors' => [], + ]; + + /** + * @var TokenRequest + */ + private $tokenRequest; + + /** + * Setup TokenRequest + */ + public function setup(): void + { + $someAttribute = $this->createMock(SandboxAttribute::class); + $someAttribute + ->method('jsonSerialize') + ->willReturn(self::SOME_ATTRIBUTE_JSON_DATA); + + $this->tokenRequest = new TokenRequest( + self::SOME_REMEMBER_ME_ID, + [ $someAttribute ] + ); + } + + /** + * @covers ::jsonSerialize + * @covers ::__construct + */ + public function testJsonSerialize() + { + $this->assertJsonStringEqualsJsonString( + json_encode([ + 'remember_me_id' => self::SOME_REMEMBER_ME_ID, + 'profile_attributes' => [ self::SOME_ATTRIBUTE_JSON_DATA ], + ]), + json_encode($this->tokenRequest) + ); + } + + /** + * @covers ::getPayload + * @covers ::__construct + */ + public function testGetPayload() + { + $this->assertEquals( + (string) Payload::fromJsonData([ + 'remember_me_id' => self::SOME_REMEMBER_ME_ID, + 'profile_attributes' => [ self::SOME_ATTRIBUTE_JSON_DATA ], + ]), + (string) $this->tokenRequest->getPayload() + ); + } +} diff --git a/tests/Profile/Response/TokenResponseTest.php b/tests/Profile/Response/TokenResponseTest.php new file mode 100644 index 0000000..4d9aa2f --- /dev/null +++ b/tests/Profile/Response/TokenResponseTest.php @@ -0,0 +1,83 @@ +createMock(ResponseInterface::class); + $someResponse->method('getStatusCode')->willReturn(201); + $someResponse->method('getBody')->willReturn(json_encode([ + 'token' => $someToken, + ])); + + $response = new TokenResponse($someResponse); + + $this->assertEquals( + $someToken, + $response->getToken() + ); + } + + /** + * @covers ::getToken + * @covers ::__construct + * @covers ::processData + */ + public function testGetTokenEmpty() + { + $this->expectException(\Yoti\Sandbox\Exception\ResponseException::class, 'Token key is missing'); + + $someResponse = $this->createMock(ResponseInterface::class); + $someResponse->method('getStatusCode')->willReturn(201); + $someResponse->method('getBody')->willReturn('{}'); + + (new TokenResponse($someResponse))->getToken(); + } + + /** + * @covers ::checkResponseStatus + */ + public function testBadResponseStatusCode() + { + $this->expectException(\Yoti\Sandbox\Exception\ResponseException::class, 'Server responded with 500'); + + $someResponse = $this->createMock(ResponseInterface::class); + $someResponse->method('getStatusCode')->willReturn(500); + $someResponse->method('getBody')->willReturn('{}'); + + (new TokenResponse($someResponse))->getToken(); + } + + /** + * @covers ::processData + */ + public function testInvalidJson() + { + $this->expectException(\Yoti\Exception\JsonException::class, 'Syntax error'); + + $someResponse = $this->createMock(ResponseInterface::class); + $someResponse->method('getStatusCode')->willReturn(201); + $someResponse->method('getBody')->willReturn('invalid json'); + + (new TokenResponse($someResponse))->getToken(); + } +} diff --git a/tests/Profile/ServiceTest.php b/tests/Profile/ServiceTest.php new file mode 100644 index 0000000..be9fd5b --- /dev/null +++ b/tests/Profile/ServiceTest.php @@ -0,0 +1,60 @@ +createMock(ResponseInterface::class); + $mockResponse + ->method('getBody') + ->willReturn(json_encode([ + 'token' => $expectedConnectToken + ])); + $mockResponse + ->method('getStatusCode') + ->willReturn(201); + + $mockHttpClient = $this->createMock(ClientInterface::class); + $mockHttpClient->method('sendRequest')->willReturn($mockResponse); + + $service = new Service( + TestData::SDK_ID, + PemFile::fromFilePath(TestData::PEM_FILE), + new Config([ + Config::HTTP_CLIENT => $mockHttpClient, + ]) + ); + + $mockTokenRequest = $this->createMock(TokenRequest::class); + $mockTokenRequest + ->method('getPayload') + ->willReturn($this->createMock(Payload::class)); + + $token = $service->setupSharingProfile($mockTokenRequest); + + $this->assertEquals($expectedConnectToken, $token); + } +} diff --git a/tests/SandboxClientTest.php b/tests/SandboxClientTest.php new file mode 100644 index 0000000..66eec55 --- /dev/null +++ b/tests/SandboxClientTest.php @@ -0,0 +1,57 @@ +createMock(ResponseInterface::class); + $mockResponse + ->method('getBody') + ->willReturn(json_encode([ + 'token' => $expectedConnectToken + ])); + $mockResponse + ->method('getStatusCode') + ->willReturn(201); + + $mockHttpClient = $this->createMock(ClientInterface::class); + $mockHttpClient->method('sendRequest')->willReturn($mockResponse); + + $sandboxClient = new SandboxClient( + TestData::SDK_ID, + TestData::PEM_FILE, + [ + Config::HTTP_CLIENT => $mockHttpClient, + ] + ); + + $mockTokenRequest = $this->createMock(TokenRequest::class); + $mockTokenRequest + ->method('getPayload') + ->willReturn($this->createMock(Payload::class)); + + $token = $sandboxClient->setupSharingProfile($mockTokenRequest); + + $this->assertEquals($expectedConnectToken, $token); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..de0c71d --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,11 @@ + Date: Thu, 12 Mar 2020 14:12:24 +0000 Subject: [PATCH 02/17] SDK-1472: Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 078a209..140d448 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repo contains the tools you need to test your Yoti integration. -## Enabling the SDK +## Installing the Sandbox Add the Yoti SDK dependency: @@ -12,7 +12,7 @@ $ composer require yoti/yoti-php-sdk-sandbox ## Client Initialisation -The YotiClient is the SDK entry point. To initialise it you need to include the following snippet inside your endpoint initialisation section: +The SandboxClient is the sandbox entry point. To initialise it you need to include the following snippet inside your endpoint initialisation section: ```php Date: Tue, 17 Mar 2020 17:08:22 +0000 Subject: [PATCH 03/17] SDK-1472: Update required SDK version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 319ab9b..f5b848c 100755 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "homepage": "https://yoti.com", "license": "MIT", "require": { - "yoti/yoti-php-sdk": "^3.0" + "yoti/yoti-php-sdk": "^3.1" }, "require-dev": { "phpunit/phpunit": "^8.5", From 0770898abd6af3a2b1394770d96565cc2920111b Mon Sep 17 00:00:00 2001 From: David Grayston Date: Tue, 17 Mar 2020 17:18:41 +0000 Subject: [PATCH 04/17] SDK-1472: Move profile sandbox client into Yoti\Sandbox\Profile namespace --- .gitignore | 2 ++ src/{ => Profile}/SandboxClient.php | 2 +- tests/{ => Profile}/SandboxClientTest.php | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) rename src/{ => Profile}/SandboxClient.php (97%) rename tests/{ => Profile}/SandboxClientTest.php (93%) diff --git a/.gitignore b/.gitignore index f2cca8f..6657b64 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ coverage .scannerwork .DS_Store +cghooks.lock + diff --git a/src/SandboxClient.php b/src/Profile/SandboxClient.php similarity index 97% rename from src/SandboxClient.php rename to src/Profile/SandboxClient.php index 5846ce3..df31f7b 100644 --- a/src/SandboxClient.php +++ b/src/Profile/SandboxClient.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yoti\Sandbox; +namespace Yoti\Sandbox\Profile; use Yoti\Sandbox\Profile\Request\TokenRequest; use Yoti\Sandbox\Profile\Service as ProfileService; diff --git a/tests/SandboxClientTest.php b/tests/Profile/SandboxClientTest.php similarity index 93% rename from tests/SandboxClientTest.php rename to tests/Profile/SandboxClientTest.php index 66eec55..0bf15f5 100644 --- a/tests/SandboxClientTest.php +++ b/tests/Profile/SandboxClientTest.php @@ -8,11 +8,11 @@ use Psr\Http\Message\ResponseInterface; use Yoti\Http\Payload; use Yoti\Sandbox\Profile\Request\TokenRequest; -use Yoti\Sandbox\SandboxClient; +use Yoti\Sandbox\Profile\SandboxClient; use Yoti\Util\Config; /** - * @coversDefaultClass \Yoti\Sandbox\SandboxClient + * @coversDefaultClass \Yoti\Sandbox\Profile\SandboxClient */ class SandboxClientTest extends TestCase { From c89ac8053ba0dfbded45441eca5385aa56bda8c8 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 18 Mar 2020 10:47:41 +0000 Subject: [PATCH 05/17] SDK-1472: Add version to composer.json --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index f5b848c..e39b611 100755 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "name": "yoti/yoti-php-sdk-sandbox", "description": "Yoti PHP SDK Sandbox", + "version": "1.0.0", "keywords": [ "yoti", "sdk" From 686aacd7c3c7db0f0ab877a0a7bfe68e0b163104 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 18 Mar 2020 12:25:16 +0000 Subject: [PATCH 06/17] SDK-1472: Update README example --- README.md | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 140d448..124ce91 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Yoti PHP Sandbox SDK -This repo contains the tools you need to test your Yoti integration. +This repository contains the tools you need to test your Yoti integration. ## Installing the Sandbox @@ -10,40 +10,47 @@ Add the Yoti SDK dependency: $ composer require yoti/yoti-php-sdk-sandbox ``` -## Client Initialisation +## Configuration -The SandboxClient is the sandbox entry point. To initialise it you need to include the following snippet inside your endpoint initialisation section: +* `CLIENT_SDK_ID` is the SDK identifier generated by Yoti Hub in the Key tab when you create your app. -```php -setGivenNames() + ->setRememberMeId('some remember me ID') + ->setGivenNames('some given names') + ->setFamilyName('some family name') + ->setFullName('some full name') + ->setDateOfBirth(new \DateTime('1980-01-01')) + ->setAgeVerification($ageVerification) + ->setGender('some gender') + ->setPhoneNumber('some phone number') + ->setNationality('some nationality') + ->setStructuredPostalAddress(json_encode([ + 'building_number' => 1, + 'address_line1' => 'some address', + ])) + ->setBase64Selfie('some base64 encoded selfie') + ->setEmailAddress('some@email') + ->setDocumentDetailsWithString('PASSPORT USA 1234abc') ->build(); $token = $client->setupSharingProfile($tokenRequest)->getToken(); From bbb16e1123d9d578230bc56f4b12fde04f84da44 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 18 Mar 2020 12:43:32 +0000 Subject: [PATCH 07/17] SDK-1472: Add token usage to README example --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 124ce91..c906661 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ use Yoti\Sandbox\Profile\Request\TokenRequestBuilder; use Yoti\Sandbox\Profile\Request\Attribute\SandboxAgeVerification; try { - $client = new SandboxClient('CLIENT_SDK_ID', '/path/to/your-pem-file.pem'); + $sandboxClient = new SandboxClient('CLIENT_SDK_ID', '/path/to/your-pem-file.pem'); $ageVerification = new SandboxAgeVerification( new \DateTime('1980-01-01'), @@ -53,7 +53,11 @@ try { ->setDocumentDetailsWithString('PASSPORT USA 1234abc') ->build(); - $token = $client->setupSharingProfile($tokenRequest)->getToken(); + $token = $sandboxClient->setupSharingProfile($tokenRequest)->getToken(); + + $client = new \Yoti\YotiClient('CLIENT_SDK_ID', '/path/to/your-pem-file.pem'); + $activityDetails = $client->getActivityDetails($token); + } catch(Exception $e) { // Handle unhappy path } From c0c7654260aee0c4f7dfe2b72cd6b5f36b656259 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 18 Mar 2020 14:51:12 +0000 Subject: [PATCH 08/17] SDK-1472: Change setters to statuc factory methods --- README.md | 6 ++--- .../Attribute/SandboxAgeVerification.php | 24 +++++++++++++------ .../Attribute/SandboxAgeVerificationTest.php | 22 ++++++++++------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c906661..19d26cb 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ use Yoti\Sandbox\Profile\Request\Attribute\SandboxAgeVerification; try { $sandboxClient = new SandboxClient('CLIENT_SDK_ID', '/path/to/your-pem-file.pem'); - $ageVerification = new SandboxAgeVerification( - new \DateTime('1980-01-01'), - 'age_under:18' + $ageVerification = SandboxAgeVerification::forAgeOver( + 18, + new \DateTime('1980-01-01') ); $tokenRequest = (new TokenRequestBuilder()) diff --git a/src/Profile/Request/Attribute/SandboxAgeVerification.php b/src/Profile/Request/Attribute/SandboxAgeVerification.php index 58ef4c6..5962af6 100644 --- a/src/Profile/Request/Attribute/SandboxAgeVerification.php +++ b/src/Profile/Request/Attribute/SandboxAgeVerification.php @@ -12,28 +12,38 @@ class SandboxAgeVerification extends SandboxAttribute private const AGE_UNDER_FORMAT = 'age_under:%d'; /** - * @param \DateTime $dateObj + * @param \DateTime $date * @param string $derivation * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors */ - public function __construct(\DateTime $dateObj, string $derivation = '', array $anchors = []) + final public function __construct(\DateTime $date, string $derivation = '', array $anchors = []) { parent::__construct( UserProfile::ATTR_DATE_OF_BIRTH, - $dateObj->format('Y-m-d'), + $date->format('Y-m-d'), $derivation, true, $anchors ); } - public function setAgeOver(int $age): void + /** + * @param int $age + * @param \DateTime $date + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + */ + public static function forAgeOver(int $age, \DateTime $date, array $anchors = []): self { - $this->derivation = sprintf(self::AGE_OVER_FORMAT, $age); + return new static($date, sprintf(self::AGE_OVER_FORMAT, $age), $anchors); } - public function setAgeUnder(int $age): void + /** + * @param int $age + * @param \DateTime $date + * @param \Yoti\Sandbox\Profile\Request\Attribute\SandboxAnchor[] $anchors + */ + public static function forAgeUnder(int $age, \DateTime $date, array $anchors = []): self { - $this->derivation = sprintf(self::AGE_UNDER_FORMAT, $age); + return new static($date, sprintf(self::AGE_UNDER_FORMAT, $age), $anchors); } } diff --git a/tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php b/tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php index 366a206..75afc91 100644 --- a/tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php +++ b/tests/Profile/Request/Attribute/SandboxAgeVerificationTest.php @@ -49,12 +49,15 @@ public function testJsonSerialize() } /** - * @covers ::setAgeOver + * @covers ::forAgeOver * @covers ::__construct */ - public function testGetAgeOver() + public function testForAgeOver() { - $this->ageVerification->setAgeOver(20); + $ageOverVerification = SandboxAgeVerification::forAgeOver( + 20, + new \DateTime(self::SOME_TIMESTAMP_DATE_STRING) + ); $this->assertJsonStringEqualsJsonString( json_encode([ @@ -64,17 +67,20 @@ public function testGetAgeOver() 'optional' => true, 'anchors' => [], ]), - json_encode($this->ageVerification) + json_encode($ageOverVerification) ); } /** - * @covers ::setAgeUnder + * @covers ::forAgeUnder * @covers ::__construct */ - public function testAgeUnder() + public function testForAgeUnder() { - $this->ageVerification->setAgeUnder(30); + $ageUnderVerification = SandboxAgeVerification::forAgeUnder( + 30, + new \DateTime(self::SOME_TIMESTAMP_DATE_STRING) + ); $this->assertJsonStringEqualsJsonString( json_encode([ @@ -84,7 +90,7 @@ public function testAgeUnder() 'optional' => true, 'anchors' => [], ]), - json_encode($this->ageVerification) + json_encode($ageUnderVerification) ); } } From 04ddbf3aa96dae71594ee68d9fae28cef18d428e Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 18 Mar 2020 15:02:45 +0000 Subject: [PATCH 09/17] SDK-1472: Remove unneeded sample token --- tests/Profile/SandboxClientTest.php | 8 ++++---- tests/Profile/ServiceTest.php | 8 ++++---- tests/TestData.php | 1 - tests/sample-data/connect-token.txt | 1 - 4 files changed, 8 insertions(+), 10 deletions(-) delete mode 100755 tests/sample-data/connect-token.txt diff --git a/tests/Profile/SandboxClientTest.php b/tests/Profile/SandboxClientTest.php index 0bf15f5..b144986 100644 --- a/tests/Profile/SandboxClientTest.php +++ b/tests/Profile/SandboxClientTest.php @@ -16,19 +16,19 @@ */ class SandboxClientTest extends TestCase { + private const SOME_TOKEN = 'some-token'; + /** * @covers ::setupSharingProfile * @covers ::__construct */ public function testSetupSharingProfile() { - $expectedConnectToken = file_get_contents(TestData::YOTI_CONNECT_TOKEN); - $mockResponse = $this->createMock(ResponseInterface::class); $mockResponse ->method('getBody') ->willReturn(json_encode([ - 'token' => $expectedConnectToken + 'token' => self::SOME_TOKEN ])); $mockResponse ->method('getStatusCode') @@ -52,6 +52,6 @@ public function testSetupSharingProfile() $token = $sandboxClient->setupSharingProfile($mockTokenRequest); - $this->assertEquals($expectedConnectToken, $token); + $this->assertEquals(self::SOME_TOKEN, $token); } } diff --git a/tests/Profile/ServiceTest.php b/tests/Profile/ServiceTest.php index be9fd5b..acc7bec 100644 --- a/tests/Profile/ServiceTest.php +++ b/tests/Profile/ServiceTest.php @@ -19,19 +19,19 @@ */ class ServiceTest extends TestCase { + private const SOME_TOKEN = 'some-token'; + /** * @covers ::setupSharingProfile * @covers ::__construct */ public function testSetupSharingProfile() { - $expectedConnectToken = file_get_contents(TestData::YOTI_CONNECT_TOKEN); - $mockResponse = $this->createMock(ResponseInterface::class); $mockResponse ->method('getBody') ->willReturn(json_encode([ - 'token' => $expectedConnectToken + 'token' => self::SOME_TOKEN ])); $mockResponse ->method('getStatusCode') @@ -55,6 +55,6 @@ public function testSetupSharingProfile() $token = $service->setupSharingProfile($mockTokenRequest); - $this->assertEquals($expectedConnectToken, $token); + $this->assertEquals(self::SOME_TOKEN, $token); } } diff --git a/tests/TestData.php b/tests/TestData.php index 57a5232..0ba888a 100644 --- a/tests/TestData.php +++ b/tests/TestData.php @@ -8,5 +8,4 @@ class TestData { public const SDK_ID = '990a3996-5762-4e8a-aa64-cb406fdb0e68'; public const PEM_FILE = __DIR__ . '/sample-data/test.pem'; - public const YOTI_CONNECT_TOKEN = __DIR__ . '/sample-data/connect-token.txt'; } diff --git a/tests/sample-data/connect-token.txt b/tests/sample-data/connect-token.txt deleted file mode 100755 index 49f59a6..0000000 --- a/tests/sample-data/connect-token.txt +++ /dev/null @@ -1 +0,0 @@ -c31Db4y6ClxSWy26xDpa9LEX3ZTUuR-rKaAhjQWnmKilR20IshkysR5Y3Hh3R6hanOyxcu7fl5vbjikkGZZb3_iH6NjxmBXuGY_Fr23AhrHvGL9WMg4EtemVvr6VI2f_5H_PDhDpYUvv-YpEM0f_SReoVxGIc8VGfj1gukuhPyNJ9hs55-SDdUjN77JiA6FPcYZxEIaqQE_yT_c3Y4V72Jnq3RHbG0vL6SefSfY_fFsnx_HeddsJc10qJYCwAkdGzVzbJH2DQ2Swp821Gwyj9eNK54S6HvpIg7LclID7BtymG6z7cTNp3fXX7mgKYoQlh_DHmPmaiqyj398w424RBg== \ No newline at end of file From 905153d54ec50ce483e588c01034a529e8b236f9 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 18 Mar 2020 15:05:44 +0000 Subject: [PATCH 10/17] SDK-1472: Add export ignore items to .gitattributes --- .gitattributes | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9e64743 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +/tests export-ignore +/phpunit.xml export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/README.md export-ignore +/.php_cs.dist export-ignore +/.phpcs.xml.dist export-ignore +/.travis.yml export-ignore +/sonar-project.properties export-ignore +/.dependabot export-ignore +/phpstan.neon.dist export-ignore + +# Auto detect text files and perform LF normalization +* text=auto \ No newline at end of file From 3daf3ed026d424a4a832b5f4b82fed357449a094 Mon Sep 17 00:00:00 2001 From: David Grayston <1229335+davidgrayston@users.noreply.github.com> Date: Wed, 18 Mar 2020 17:16:16 +0000 Subject: [PATCH 11/17] SDK-1472: Update .dependabot/config.yml Co-Authored-By: Ed Harrod --- .dependabot/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index dd7a819..45403b6 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -8,5 +8,4 @@ update_configs: default_reviewers: - "echarrod" - "davidgrayston" - - "emmas-yoti" - "MrBurtyyy" From 2084184ef3002856311a013b836b4dca8754badc Mon Sep 17 00:00:00 2001 From: David Grayston <1229335+davidgrayston@users.noreply.github.com> Date: Wed, 18 Mar 2020 17:17:00 +0000 Subject: [PATCH 12/17] SDK-1472: Add default dependabot assignee Co-Authored-By: Ed Harrod --- .dependabot/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.dependabot/config.yml b/.dependabot/config.yml index 45403b6..ecb611c 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -9,3 +9,5 @@ update_configs: - "echarrod" - "davidgrayston" - "MrBurtyyy" + + default_assignees: ["davidgrayston"] From 027f4db3daefeb6602cbfa73ddd4b17b730a9134 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 20 Mar 2020 14:43:11 +0000 Subject: [PATCH 13/17] SDK-1472: Add phpunit/phpunit ^7.5 for PHP 7.1 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e39b611..66669be 100755 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "yoti/yoti-php-sdk": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^8.5", + "phpunit/phpunit": "^7.5 || ^8.5", "squizlabs/php_codesniffer": "^3.4", "friendsofphp/php-cs-fixer": "^2.15", "brainmaestro/composer-git-hooks": "^2.8", From 910a57566101213d1ee453f9cdf86fe878554d3a Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 20 Mar 2020 14:54:49 +0000 Subject: [PATCH 14/17] SDK-1472: Remove coveralls --- .travis.yml | 6 ------ composer.json | 5 ----- 2 files changed, 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8b937bd..a38ff3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,12 +23,6 @@ jobs: php: "7.2" - <<: *compatibility php: "7.3" - - <<: *test - stage: Coverage - name: Coveralls - if: type = pull_request OR branch = master - script: - - composer coveralls - <<: *test stage: Analyze name: Sonarcloud diff --git a/composer.json b/composer.json index 66669be..98ab337 100755 --- a/composer.json +++ b/composer.json @@ -16,7 +16,6 @@ "squizlabs/php_codesniffer": "^3.4", "friendsofphp/php-cs-fixer": "^2.15", "brainmaestro/composer-git-hooks": "^2.8", - "php-coveralls/php-coveralls": "^2.1", "phpstan/phpstan-strict-rules": "^0.12.1", "phpstan/extension-installer": "^1.0" }, @@ -35,10 +34,6 @@ "test": "phpunit", "coverage-clover": "phpunit --coverage-clover ./coverage/coverage.xml", "coverage-html": "phpunit --coverage-html ./coverage/report", - "coveralls": [ - "@coverage-clover", - "php-coveralls --coverage_clover ./coverage/coverage.xml --json_path ./coverage/coveralls-upload.json" - ], "lint": [ "phpcs", "php-cs-fixer fix --config=.php_cs.dist -v --dry-run --using-cache=no --diff-format=udiff --ansi", From dd6dc7fd8e0d190978e56f9fd65b9cd53eb46e29 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 20 Mar 2020 15:25:36 +0000 Subject: [PATCH 15/17] SDK-1472: Correct version for SonarCloud --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index f90f820..5389e8c 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.organization = getyoti sonar.projectKey = getyoti:php-sandbox sonar.projectName = PHP SDK Sandbox -sonar.projectVersion = 3.1.0 +sonar.projectVersion = 1.0.0 sonar.language = php sonar.sources=src From 1e73b8e17b1706329acbb8e255b6f35e9a37e0d8 Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 20 Mar 2020 16:21:26 +0000 Subject: [PATCH 16/17] SDK-1472: Remove analyze task from Travis --- .travis.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index a38ff3c..1a939c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,14 +23,3 @@ jobs: php: "7.2" - <<: *compatibility php: "7.3" - - <<: *test - stage: Analyze - name: Sonarcloud - dist: trusty - addons: - sonarcloud: - organization: "getyoti" - if: type == pull_request OR branch = master - script: - - composer coverage-clover - - sonar-scanner From 1d3ddd6e986895bfde9fb24015e888d1663a181b Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 20 Mar 2020 16:54:58 +0000 Subject: [PATCH 17/17] SDK-1472: Set client API URL in README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19d26cb..c70d065 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,9 @@ try { $token = $sandboxClient->setupSharingProfile($tokenRequest)->getToken(); - $client = new \Yoti\YotiClient('CLIENT_SDK_ID', '/path/to/your-pem-file.pem'); + $client = new \Yoti\YotiClient('CLIENT_SDK_ID', '/path/to/your-pem-file.pem', [ + 'api.url' => 'https://api.yoti.com/sandbox/v1' + ]); $activityDetails = $client->getActivityDetails($token); } catch(Exception $e) {