├── php-fmt.sh ├── .gitignore ├── phpstan.neon ├── test ├── bootstrap.php ├── assets │ └── CDFAttributeChild.php ├── AssetTest.php ├── SettingsTestHmacV1.php ├── SettingsTestHmacV2.php ├── ContentHubTestHmacV1.php ├── ContentHubTestHmacV2.php ├── EventSubscriber │ ├── DefaultCDFTest.php │ └── ClientCDFTest.php ├── Event │ └── GetCDFTypeEventTest.php ├── SettingsTest.php ├── CDF │ ├── ClientCDFObjectTest.php │ └── CDFObjectTest.php ├── WebhookTest.php ├── SettingsTestBase.php ├── SearchCriteria │ └── SearchCriteriaBuilderTest.php ├── CDFAttributeTest.php ├── ContentHubTestBase.php └── CDFDocumentTest.php ├── src ├── StatusCodes.php ├── ContentHubLibraryEvents.php ├── EventSubscriber │ ├── DefaultCDF.php │ └── ClientCDF.php ├── Asset.php ├── Guzzle │ └── Middleware │ │ ├── RequestResponseHandler.php │ │ └── RequestResponseLogger.php ├── Event │ └── GetCDFTypeEvent.php ├── CDF │ ├── ClientCDFObject.php │ ├── CDFObjectInterface.php │ └── CDFObject.php ├── SearchCriteria │ ├── SearchCriteriaBuilder.php │ └── SearchCriteria.php ├── CDFAttribute.php ├── Webhook.php ├── CDFDocument.php ├── Settings.php ├── ObjectFactory.php ├── ContentHubClientCommonTrait.php ├── ContentHubClientTrait.php └── ContentHubClient.php ├── infection.json.dist ├── .github └── workflows │ └── content-hub-php-actions.yml ├── phpmd.xml ├── phpunit.xml ├── composer.json ├── Makefile └── README.md /php-fmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./vendor/bin/php-cs-fixer fix ./src --level=psr2 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bootstrap.php 2 | build 3 | vendor 4 | composer.lock 5 | infection 6 | .DS_Store 7 | nbproject 8 | .idea -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 1 3 | paths: 4 | - ./src 5 | ignoreErrors: 6 | - '#Unsafe usage of new static\(\).#' 7 | excludePaths: 8 | - test/ 9 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | setUrl($url); 23 | $asset->setReplaceToken($replaceToken); 24 | $this->assertEquals($url, $asset->getUrl()); 25 | $this->assertEquals($replaceToken, $asset->getReplaceToken()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /infection.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "directories": [ 4 | "src" 5 | ] 6 | }, 7 | "timeout": 10, 8 | "logs": { 9 | "text": "infection/logs/infection.log", 10 | "summary": "infection/logs/summary.log", 11 | "debug": "infection/logs/debug.log", 12 | "perMutator": "infection/logs/per-mutator.md" 13 | }, 14 | "tmpDir": "infection/tmp", 15 | "phpUnit": { 16 | "configDir": ".", 17 | "customPath": "vendor/phpunit/phpunit/phpunit" 18 | }, 19 | "mutators": { 20 | "@default": true, 21 | "@function_signature": false, 22 | "TrueValue": { 23 | "ignore": [ 24 | "NameSpace\\*\\Class::method" 25 | ] 26 | } 27 | }, 28 | "initialTestsPhpOptions": "-d zend_extension=xdebug.so", 29 | "testFrameworkOptions": "-vvv" 30 | } -------------------------------------------------------------------------------- /test/SettingsTestHmacV1.php: -------------------------------------------------------------------------------- 1 | $stack]); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /test/SettingsTestHmacV2.php: -------------------------------------------------------------------------------- 1 | $stack]); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /test/ContentHubTestHmacV1.php: -------------------------------------------------------------------------------- 1 | $stack]); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/ContentHubLibraryEvents.php: -------------------------------------------------------------------------------- 1 | $stack]); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/content-hub-php-actions.yml: -------------------------------------------------------------------------------- 1 | name: Content Hub PHP 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ${{ matrix.os }} 6 | strategy: 7 | matrix: 8 | os: [ ubuntu-latest ] 9 | php-version: [ '7.4', '8.1', '8.2' ] 10 | steps: 11 | # This step checks out a copy of your repository. 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup PHP 16 | uses: shivammathur/setup-php@v2 17 | with: 18 | php-version: ${{ matrix.php-version }} 19 | coverage: xdebug 20 | 21 | - name: Install composer dependencies 22 | run: composer install 23 | 24 | - name: Run phpcs 25 | run: | 26 | ./vendor/bin/phpcs --config-set installed_paths vendor/drupal/coder/coder_sniffer 27 | ./vendor/bin/phpcs -n --standard=Drupal,DrupalPractice src/ test/ 28 | 29 | - name: Run PHPUnit 30 | run: ./vendor/bin/phpunit test/ 31 | 32 | - name: Run phpstan 33 | run: ./vendor/bin/phpstan analyse -c ./phpstan.neon 34 | -------------------------------------------------------------------------------- /src/EventSubscriber/DefaultCDF.php: -------------------------------------------------------------------------------- 1 | setObject(CDFObject::fromArray($event->getData())); 35 | $event->stopPropagation(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /phpmd.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | PHP Project Starter Rulset: Adopted From Jenkins for Symfony 2 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | test 15 | 16 | 17 | 18 | 19 | 20 | src 21 | 22 | src/ObjectFactory.php 23 | src/ContentHubLibraryEvents.php 24 | src/Guzzle 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Asset.php: -------------------------------------------------------------------------------- 1 | getValue('url', ''); 40 | } 41 | 42 | /** 43 | * Getter for 'replace-token'. 44 | * 45 | * @return string 46 | * 'replace-token' value. 47 | */ 48 | public function getReplaceToken(): string { 49 | return $this->getValue('replace-token', ''); 50 | } 51 | 52 | /** 53 | * Properties getter. 54 | * 55 | * @param string $key 56 | * Property name. 57 | * @param string $default 58 | * Default value. 59 | * 60 | * @return mixed 61 | * Property value. 62 | */ 63 | protected function getValue($key, $default) { 64 | return $this[$key] ?? $default; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/EventSubscriber/ClientCDF.php: -------------------------------------------------------------------------------- 1 | getType() === 'client') { 36 | $data = $event->getData(); 37 | /* @deprecated Backwards Compatibility, Remove by 2.0 */ 38 | if (!isset($data['metadata']['settings'])) { 39 | $data['metadata'] = [ 40 | 'settings' => $data['metadata'], 41 | ]; 42 | } 43 | /* End deprecated code */ 44 | $object = ClientCDFObject::fromArray($data); 45 | $event->setObject($object); 46 | $event->stopPropagation(); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/Guzzle/Middleware/RequestResponseHandler.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 29 | } 30 | 31 | /** 32 | * Logs any response/request. 33 | * 34 | * @param callable $handler 35 | * Request handler. 36 | * 37 | * @return \Closure 38 | * Request handler. 39 | */ 40 | public function __invoke(callable $handler): \Closure { 41 | return function (RequestInterface $request, array $options) use ($handler) { 42 | $promise = function (ResponseInterface $response) use ($request) { 43 | try { 44 | (new RequestResponseLogger($request, $response, 45 | $this->logger))->log(); 46 | } 47 | catch (\Exception $exception) { 48 | $message = sprintf('Failed to make log entry. Reason: %s', 49 | $exception->getMessage()); 50 | $this->logger->critical($message); 51 | } 52 | 53 | return $response; 54 | }; 55 | 56 | return $handler($request, $options)->then($promise); 57 | }; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "acquia/content-hub-php", 3 | "type": "library", 4 | "description": "A PHP Client library to consume the Acquia Content Hub API.", 5 | "homepage": "https://github.com/acquia/content-hub-php", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "See Contributors", 10 | "homepage": "https://github.com/acquia/content-hub-php/graphs/contributors" 11 | } 12 | ], 13 | "support": { 14 | "issues": "https://github.com/acquia/content-hub-php/issues" 15 | }, 16 | "require": { 17 | "acquia/http-hmac-php": ">=3.4", 18 | "ext-json": "*", 19 | "php": ">=7.3", 20 | "psr/log": ">=1.0", 21 | "guzzlehttp/guzzle": ">=6.5", 22 | "symfony/event-dispatcher": ">=4.4", 23 | "symfony/http-foundation": ">=4.4", 24 | "symfony/serializer": ">=4.4" 25 | }, 26 | "require-dev": { 27 | "drupal/coder": "dev-8.x-3.x", 28 | "mockery/mockery": "^1.2", 29 | "pdepend/pdepend": "~1.0", 30 | "phploc/phploc": "~2.0", 31 | "phpmd/phpmd": "~1.0", 32 | "phpunit/phpunit": "^9", 33 | "scrutinizer/ocular": "~1.0", 34 | "sebastian/phpcpd": "~2.0", 35 | "squizlabs/php_codesniffer": "^3.5", 36 | "phpstan/phpstan": "^1.8", 37 | "phpspec/prophecy-phpunit": "^2" 38 | }, 39 | "suggest": { 40 | "ramsey/uuid": "A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID)" 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "Acquia\\ContentHubClient\\": "src/" 45 | } 46 | }, 47 | "autoload-dev": { 48 | "psr-4": { 49 | "Acquia\\ContentHubClient\\": "test/" 50 | } 51 | }, 52 | "extra": { 53 | "branch-alias": { 54 | "dev-2.x": "2.x-dev" 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Event/GetCDFTypeEvent.php: -------------------------------------------------------------------------------- 1 | data = $data; 44 | 45 | if (!isset($data['type'])) { 46 | throw new \InvalidArgumentException('Parameters should have a \'type\' key'); 47 | } 48 | $this->type = $data['type']; 49 | } 50 | 51 | /** 52 | * Returns CDF object. 53 | * 54 | * @return \Acquia\ContentHubClient\CDF\CDFObjectInterface 55 | * CDF object. 56 | */ 57 | public function getObject() { 58 | return $this->cdfObject; 59 | } 60 | 61 | /** 62 | * CDF object setter. 63 | * 64 | * @param \Acquia\ContentHubClient\CDF\CDFObjectInterface $object 65 | * CDF object. 66 | */ 67 | public function setObject(CDFObjectInterface $object) { 68 | $this->cdfObject = $object; 69 | } 70 | 71 | /** 72 | * Returns event data. 73 | * 74 | * @return array 75 | * CDF representation from plexus. 76 | */ 77 | public function getData() { 78 | return $this->data; 79 | } 80 | 81 | /** 82 | * Returns CDF Type. 83 | * 84 | * @return string 85 | * CDF Type. 86 | */ 87 | public function getType() { 88 | return $this->type; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /test/EventSubscriber/DefaultCDFTest.php: -------------------------------------------------------------------------------- 1 | defaultCdf = new DefaultCDF(); 41 | $this->handler = current(current($this->defaultCdf::getSubscribedEvents()[ContentHubLibraryEvents::GET_CDF_CLASS])); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function tearDown(): void { 48 | parent::tearDown(); 49 | 50 | unset($this->clientCdf, $this->handler); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function testGetSubscribedEventsAddsHandlerToEvent() { 57 | $this->assertNotNull($this->handler); 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public function testHandler() { 64 | $cdfObjectInterfaceMock = \Mockery::mock(CDFObjectInterface::class); 65 | $cdfObjectMock = \Mockery::mock('overload:' . CDFObject::class); 66 | 67 | $cdfObjectMock->shouldReceive('fromArray') 68 | ->once() 69 | ->andReturn($cdfObjectInterfaceMock); 70 | 71 | $getCDFTypeEventMock = $this->getMockBuilder(GetCDFTypeEvent::class) 72 | ->disableOriginalConstructor() 73 | ->onlyMethods(['setObject', 'stopPropagation', 'getData']) 74 | ->getMock(); 75 | 76 | $getCDFTypeEventMock->expects($this->once()) 77 | ->method('getData'); 78 | 79 | $getCDFTypeEventMock->expects($this->once()) 80 | ->method('setObject'); 81 | 82 | $getCDFTypeEventMock->expects($this->once()) 83 | ->method('stopPropagation'); 84 | 85 | $this->defaultCdf->{$this->handler}($getCDFTypeEventMock); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ######################################################## 2 | # Constants 3 | ######################################################## 4 | # Console Colors 5 | GREEN := $(shell tput -Txterm setaf 2) 6 | YELLOW := $(shell tput -Txterm setaf 3) 7 | WHITE := $(shell tput -Txterm setaf 7) 8 | RESET := $(shell tput -Txterm sgr0) 9 | TARGET_MAX_CHAR_NUM=20 10 | 11 | # PHPUnit and Infection 12 | INFECTION_DATA_DIR := infection 13 | DEPENDENCY_DIR := ./vendor 14 | VENDOR_BIN_PATH := $(DEPENDENCY_DIR)/bin 15 | PHPUNIT_EXEC := $(VENDOR_BIN_PATH)/phpunit 16 | INFECTION_EXEC := $(VENDOR_BIN_PATH)/infection 17 | COMPOSER_INSTALL := composer install 18 | ######################################################## 19 | # Automatic Help Generator 20 | ######################################################## 21 | help: 22 | @echo '' 23 | @echo 'Usage:' 24 | @echo ' ${YELLOW}make${RESET} ${GREEN}${RESET}' 25 | @echo '' 26 | @echo 'Targets:' 27 | @awk '/^[a-zA-Z\-\_0-9]+:/ { \ 28 | helpMessage = match(lastLine, /^## (.*)/); \ 29 | if (helpMessage) { \ 30 | helpCommand = substr($$1, 0, index($$1, ":")-1); \ 31 | helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ 32 | printf " ${YELLOW}%-$(TARGET_MAX_CHAR_NUM)s${RESET} ${GREEN}%s${RESET}\n", helpCommand, helpMessage; \ 33 | } \ 34 | } \ 35 | { lastLine = $$0 }' $(MAKEFILE_LIST) 36 | ######################################################## 37 | # Commands 38 | ######################################################## 39 | .PHONY: install file_test dir_tests all_tests coverage infection 40 | 41 | ## install all the dependencies 42 | install: 43 | @$(COMPOSER_INSTALL) 44 | 45 | ## run a specific test file ==> format: file=path/to/file 46 | method_test: 47 | @$(PHPUNIT_EXEC) --filter $(method) --no-coverage $(file) 48 | 49 | ## run a specific test file ==> format: file=path/to/file 50 | file_test: 51 | @$(PHPUNIT_EXEC) --no-coverage $(file) 52 | 53 | ## run all tests inside a directory ==> format: dir=path/to/directory 54 | dir_tests: 55 | @$(PHPUNIT_EXEC) --no-coverage $(dir) 56 | 57 | ## run all tests 58 | all_tests: 59 | @$(PHPUNIT_EXEC) --no-coverage 60 | 61 | ## generate test coverage (required for infection) 62 | coverage: 63 | @$(PHPUNIT_EXEC) 64 | 65 | ## run infection to see how quality the tests are 66 | infection: 67 | @[ -d $(INFECTION_DATA_DIR) ] || $(PHPUNIT_EXEC) 68 | @composer require --dev infection/infection 69 | @$(INFECTION_EXEC) --coverage=$(INFECTION_DATA_DIR) 70 | @composer remove --dev infection/infection 71 | 72 | -------------------------------------------------------------------------------- /src/CDF/ClientCDFObject.php: -------------------------------------------------------------------------------- 1 | addAttribute('clientname', CDFAttribute::TYPE_STRING, $metadata['settings']['name']); 39 | return $cdf; 40 | } 41 | 42 | /** 43 | * Grabs the clientname on the cdf. 44 | * 45 | * @return \Acquia\ContentHubClient\CDFAttribute 46 | * The 'clientname' attribute. 47 | */ 48 | public function getClientName() { 49 | return $this->getAttribute('clientname'); 50 | } 51 | 52 | /** 53 | * Grabs the settings object instead of the attributes which are an array. 54 | * 55 | * @return \Acquia\ContentHubClient\Settings 56 | * Settings object. 57 | */ 58 | public function getSettings() { 59 | // Add all the client settings as attributes to the client object. 60 | if (empty($this->settings)) { 61 | $metadata = $this->getMetadata(); 62 | $this->settings = new Settings( 63 | $metadata['settings']['name'], 64 | $metadata['settings']['uuid'], 65 | $metadata['settings']['apiKey'], 66 | $metadata['settings']['secretKey'], 67 | $metadata['settings']['url'], 68 | $metadata['settings']['sharedSecret'], 69 | $metadata['settings']['webhook'] 70 | ); 71 | } 72 | return $this->settings; 73 | } 74 | 75 | /** 76 | * Grabs the webhook for the client. 77 | * 78 | * @return array 79 | * Webhook array. 80 | */ 81 | public function getWebhook() { 82 | $metadata = $this->getMetadata(); 83 | if (isset($metadata['settings']['webhook'])) { 84 | return $metadata['settings']['webhook']; 85 | } 86 | 87 | return []; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /test/Event/GetCDFTypeEventTest.php: -------------------------------------------------------------------------------- 1 | 'dummy_type', 24 | ]; 25 | 26 | /** 27 | * GetCDFTypeEvent instance. 28 | * 29 | * @var \Acquia\ContentHubClient\Event\GetCDFTypeEvent 30 | */ 31 | private $getCdfTypeEvent; 32 | 33 | /** 34 | * Mocked CDF object. 35 | * 36 | * @var \PHPUnit\Framework\MockObject\MockObject 37 | */ 38 | private $mockedCdfObject; 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | public function setUp(): void { 44 | parent::setUp(); 45 | 46 | $this->mockedCdfObject = $this->getMockBuilder(CDFObject::class) 47 | ->disableOriginalConstructor() 48 | ->getMock(); 49 | $this->getCdfTypeEvent = new GetCDFTypeEvent(self::DATA); 50 | } 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | public function tearDown(): void { 56 | parent::tearDown(); 57 | 58 | unset($this->getCdfTypeEvent); 59 | } 60 | 61 | /** 62 | * Tests event creation. 63 | */ 64 | public function testObjectCreationWithNoTypeSpecifiedWillThrowAnException(): void { 65 | $this->expectException(\InvalidArgumentException::class); 66 | new GetCDFTypeEvent([]); 67 | } 68 | 69 | /** 70 | * Tests event getter and setter. 71 | */ 72 | public function testGetAndSetObject(): void { 73 | $this->assertNull($this->getCdfTypeEvent->getObject()); 74 | 75 | $this->getCdfTypeEvent->setObject($this->mockedCdfObject); 76 | $cdfTypeEvent = $this->getCdfTypeEvent->getObject(); 77 | 78 | $this->assertEquals($this->mockedCdfObject, $cdfTypeEvent); 79 | $this->assertInstanceOf(CDFObjectInterface::class, $cdfTypeEvent); 80 | } 81 | 82 | /** 83 | * @covers \Acquia\ContentHubClient\Event\GetCDFTypeEvent::getData 84 | */ 85 | public function testGetData(): void { 86 | $this->assertEquals(self::DATA, $this->getCdfTypeEvent->getData()); 87 | } 88 | 89 | /** 90 | * @covers \Acquia\ContentHubClient\Event\GetCDFTypeEvent::getType 91 | */ 92 | public function testGetType(): void { 93 | $this->assertEquals(self::DATA['type'], $this->getCdfTypeEvent->getType()); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/SearchCriteria/SearchCriteriaBuilder.php: -------------------------------------------------------------------------------- 1 | ['search_term'], 49 | 'type' => ['type'], 50 | 'bundle' => ['bundle'], 51 | 'tags' => ['tags'], 52 | 'label' => ['label'], 53 | 'start_date' => ['start_date'], 54 | 'end_date' => ['end_date'], 55 | 'from' => ['from', 'start'], 56 | 'size' => ['size', 'limit'], 57 | 'sorting' => ['sort'], 58 | 'version' => ['version'], 59 | 'languages' => ['languages'], 60 | ]; 61 | } 62 | 63 | /** 64 | * Extracts property value. 65 | * 66 | * @param string $propertyName 67 | * Property name. 68 | * @param array $data 69 | * Initial data. 70 | * @param string $default 71 | * Default value. 72 | * 73 | * @return array|mixed|string 74 | * Value. 75 | */ 76 | protected static function extractPropertyFromArray(string $propertyName, array $data, $default = '') { 77 | $map = self::propertiesMap(); 78 | $values = array_values(array_intersect($map[$propertyName], 79 | array_keys($data))); 80 | 81 | if (empty($values[0])) { 82 | return $default; 83 | } 84 | 85 | $key = $values[0]; 86 | 87 | return is_array($default) && !is_array($data[$key]) ? [$data[$key]] : $data[$key]; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /test/SettingsTest.php: -------------------------------------------------------------------------------- 1 | setting_data = [ 38 | 'name' => 'some-name', 39 | 'uuid' => 'some-uuid', 40 | 'apiKey' => 'some-api-key', 41 | 'secretKey' => 'some-secret-key', 42 | 'url' => 'some-url', 43 | 'sharedSecret' => 'some-shared-secret', 44 | 'webhook' => [ 45 | 'webhook1' => 'w1-uuid', 46 | 'webhook2' => 'w2-uuid', 47 | ], 48 | ]; 49 | 50 | $this->settings = new Settings(...array_values($this->setting_data)); 51 | } 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | public function tearDown(): void { 57 | parent::tearDown(); 58 | 59 | unset($this->settings, $this->setting_data); 60 | } 61 | 62 | /** 63 | * @covers \Acquia\ContentHubClient\Settings::toArray 64 | */ 65 | public function testToArrayReturnsExactlyTheArraySettingWasCreatedOff(): void { 66 | $this->assertEquals($this->settings->toArray(), $this->setting_data); 67 | } 68 | 69 | /** 70 | * @covers \Acquia\ContentHubClient\Settings::getUuid 71 | */ 72 | public function testToGetUuidReturnsFalseIfInitializedWithEmptyValue(): void { 73 | $this->setting_data['uuid'] = ''; 74 | $this->settings = new Settings(...array_values($this->setting_data)); 75 | $this->assertFalse($this->settings->getUuid()); 76 | } 77 | 78 | /** 79 | * @covers \Acquia\ContentHubClient\Settings::getWebhook 80 | */ 81 | public function testToGetWebhookReturnsFalseIfInitializedWithEmptyValue(): void { 82 | $this->setting_data['webhook'] = []; 83 | $this->settings = new Settings(...array_values($this->setting_data)); 84 | $this->assertFalse($this->settings->getWebhook()); 85 | } 86 | 87 | /** 88 | * @covers \Acquia\ContentHubClient\Settings::getWebhook 89 | */ 90 | public function testToGetWebhookReturnsFalseIfCalledWithNonExistentKey(): void { 91 | $this->assertFalse($this->settings->getWebhook('some-non-existent-key')); 92 | } 93 | 94 | /** 95 | * @covers \Acquia\ContentHubClient\Settings::getWebhook 96 | */ 97 | public function testToGetWebhookReturnsRespectiveValueCalledWithExistentKey(): void { 98 | $webhook = $this->settings->getWebhook('webhook1'); 99 | 100 | $this->assertNotFalse($webhook); 101 | $this->assertEquals('w1-uuid', $webhook); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /test/CDF/ClientCDFObjectTest.php: -------------------------------------------------------------------------------- 1 | 'some-origin', 24 | 'created' => 'some-time', 25 | 'modified' => 'some-other-time', 26 | 'settings' => [ 27 | 'uuid' => 'some-uuid', 28 | 'name' => 'some-name', 29 | 'apiKey' => 'some-api-key', 30 | 'secretKey' => 'some-secret-key', 31 | 'url' => 'some-url', 32 | 'sharedSecret' => NULL, 33 | 'webhook' => [ 34 | 'webhook1' => 'w1-uuid', 35 | 'webhook2' => 'w2-uuid', 36 | ], 37 | ], 38 | ]; 39 | 40 | /** 41 | * ClientCDFObject instance. 42 | * 43 | * @var \Acquia\ContentHubClient\CDF\ClientCDFObject 44 | */ 45 | private $clientCdfObject; 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | public function setUp(): void { 51 | parent::setUp(); 52 | 53 | $this->clientCdfObject = ClientCDFObject::create('client_cdf_id_1', self::METADATA); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | public function tearDown(): void { 60 | parent::tearDown(); 61 | 62 | unset($this->clientCdfObject); 63 | } 64 | 65 | /** 66 | * @covers \Acquia\ContentHubClient\CDF\ClientCDFObject::getClientName 67 | */ 68 | public function testGetClientName(): void { 69 | $clientName = $this->clientCdfObject->getClientName(); 70 | 71 | $this->assertInstanceOf(CDFAttribute::class, $clientName); 72 | $this->assertEquals(CDFAttribute::TYPE_STRING, $clientName->getType()); 73 | $this->assertEquals(self::METADATA['settings']['name'], $clientName->getValue()[CDFObject::LANGUAGE_UNDETERMINED]); 74 | } 75 | 76 | /** 77 | * @covers \Acquia\ContentHubClient\CDF\ClientCDFObject::getSettings 78 | */ 79 | public function testGetSettings(): void { 80 | $settings = self::METADATA['settings']; 81 | $settings['secretKey'] = '********'; 82 | $this->assertEquals($settings, $this->clientCdfObject->getSettings()->toArray()); 83 | } 84 | 85 | /** 86 | * @covers \Acquia\ContentHubClient\CDF\ClientCDFObject::getWebhook 87 | */ 88 | public function testGetWebhookReturnsNonEmptyArrayWhenThereAreSome(): void { 89 | $this->assertEquals(self::METADATA['settings']['webhook'], $this->clientCdfObject->getWebhook()); 90 | } 91 | 92 | /** 93 | * @covers \Acquia\ContentHubClient\CDF\ClientCDFObject::getWebhook 94 | */ 95 | public function testGetWebhookReturnsEmptyArrayWhenThereIsNone(): void { 96 | $metadata = self::METADATA; 97 | unset($metadata['settings']['webhook']); 98 | $this->clientCdfObject = ClientCDFObject::create('client_cdf_id_1', $metadata); 99 | 100 | $this->assertCount(0, $this->clientCdfObject->getWebhook()); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /test/WebhookTest.php: -------------------------------------------------------------------------------- 1 | definition = [ 39 | 'uuid' => 'some-uuid', 40 | 'client_uuid' => 'some-client_uuid', 41 | 'client_name' => 'some-client-name', 42 | 'url' => 'some-url', 43 | 'version' => 1, 44 | 'disable_retries' => FALSE, 45 | 'filters' => [ 46 | 'filter1_uuid', 47 | 'filter2_uuid', 48 | ], 49 | 'status' => 'some-status', 50 | 'is_migrated' => FALSE, 51 | 'suppressed_until' => 0, 52 | ]; 53 | 54 | $this->webhook = new Webhook($this->definition); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | protected function tearDown(): void { 61 | parent::tearDown(); 62 | 63 | unset($this->webhook, $this->definition); 64 | } 65 | 66 | /** 67 | * Tests webhook creation. 68 | */ 69 | public function testGettersReturnWhatPropertiesWereSetTo(): void { 70 | $this->assertEquals( 71 | [ 72 | $this->webhook->getUuid(), 73 | $this->webhook->getClientUuid(), 74 | $this->webhook->getClientName(), 75 | $this->webhook->getUrl(), 76 | $this->webhook->getVersion(), 77 | $this->webhook->getDisableRetries(), 78 | $this->webhook->getFilters(), 79 | $this->webhook->getStatus(), 80 | $this->webhook->getIsMigrated(), 81 | $this->webhook->getSuppressedUntil(), 82 | ], 83 | array_values($this->definition) 84 | ); 85 | 86 | $this->assertEquals($this->webhook->getDefinition(), $this->definition); 87 | } 88 | 89 | /** 90 | * Tests webhook status. 91 | */ 92 | public function testGetIsEnabledReturnsTrueIfStatusIsEnabled(): void { 93 | $this->definition['status'] = 'ENABLED'; 94 | $this->webhook = new Webhook($this->definition); 95 | $this->assertTrue($this->webhook->isEnabled()); 96 | } 97 | 98 | /** 99 | * Tests webhook status. 100 | */ 101 | public function testGetIsEnabledReturnsTrueIfStatusIsEmptyString(): void { 102 | $this->definition['status'] = ''; 103 | $this->webhook = new Webhook($this->definition); 104 | $this->assertTrue($this->webhook->isEnabled()); 105 | } 106 | 107 | /** 108 | * Tests webhook status. 109 | */ 110 | public function testGetIsEnabledReturnsFalseIfStatusIsNeitherENABLEDnorEmptyString(): void { // phpcs:ignore 111 | $this->definition['status'] = 'some-status'; 112 | $this->webhook = new Webhook($this->definition); 113 | $this->assertFalse($this->webhook->isEnabled()); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/CDF/CDFObjectInterface.php: -------------------------------------------------------------------------------- 1 | '; 32 | 33 | const TYPE_ARRAY_STRING = 'array'; 34 | 35 | const TYPE_ARRAY_KEYWORD = 'array'; 36 | 37 | const TYPE_ARRAY_BOOLEAN = 'array'; 38 | 39 | const TYPE_ARRAY_NUMBER = 'array'; 40 | 41 | const TYPE_ARRAY_REFERENCE = 'array'; 42 | 43 | /** 44 | * The identifier of the attribute. 45 | * 46 | * @var string 47 | */ 48 | protected $id; 49 | 50 | /** 51 | * The attribute's data type. 52 | * 53 | * @var string 54 | */ 55 | protected $type; 56 | 57 | /** 58 | * The value of the attribute. 59 | * 60 | * @var mixed 61 | */ 62 | protected $value; 63 | 64 | /** 65 | * CDFAttribute constructor. 66 | * 67 | * @param string $id 68 | * The identifier of the attribute. 69 | * @param string $type 70 | * The attribute's data type. 71 | * @param mixed $value 72 | * The value of the attribute. 73 | * @param string $language 74 | * The language of the initial value. 75 | * 76 | * @throws \Exception 77 | * Unsupported data type exception. 78 | */ 79 | public function __construct($id, $type, $value = NULL, $language = CDFObject::LANGUAGE_UNDETERMINED) { 80 | $r = new \ReflectionClass(__CLASS__); 81 | if (!in_array($type, $r->getConstants())) { 82 | // @todo validate value against data type? 83 | throw new \Exception(sprintf("Unsupported CDF Attribute data type \"%s\".", $type)); 84 | } 85 | $this->id = $id; 86 | $this->type = $type; 87 | 88 | if ($value !== NULL) { 89 | $this->value[$language] = $value; 90 | } 91 | } 92 | 93 | /** 94 | * ID getter. 95 | * 96 | * @return string 97 | * Attribute ID. 98 | */ 99 | public function getId() { 100 | return $this->id; 101 | } 102 | 103 | /** 104 | * Type getter. 105 | * 106 | * @return string 107 | * Attribute type. 108 | */ 109 | public function getType() { 110 | return $this->type; 111 | } 112 | 113 | /** 114 | * Value getter. 115 | * 116 | * @return mixed 117 | * Attribute value. 118 | */ 119 | public function getValue() { 120 | return $this->value; 121 | } 122 | 123 | /** 124 | * Value setter. 125 | * 126 | * @param mixed $value 127 | * Attribute value. 128 | * @param string $language 129 | * Attribute language. 130 | */ 131 | public function setValue($value, $language = CDFObject::LANGUAGE_UNDETERMINED) { 132 | $this->value[$language] = $value; 133 | } 134 | 135 | /** 136 | * Transforms attribute to array. 137 | * 138 | * @return array 139 | * Array representation. 140 | */ 141 | public function toArray() { 142 | return [ 143 | 'type' => $this->getType(), 144 | 'value' => $this->getValue(), 145 | ]; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/Webhook.php: -------------------------------------------------------------------------------- 1 | definition = $definition; 29 | } 30 | 31 | /** 32 | * Returns webhook UUID. 33 | * 34 | * @return string 35 | * Webhook UUID. 36 | */ 37 | public function getUuid() { 38 | return $this->definition['uuid']; 39 | } 40 | 41 | /** 42 | * Returns client UUID of the webhook. 43 | * 44 | * @return string 45 | * Client's UUID. 46 | */ 47 | public function getClientUuid() { 48 | return $this->definition['client_uuid']; 49 | } 50 | 51 | /** 52 | * Returns client name of the webhook. 53 | * 54 | * @return string 55 | * Client's name. 56 | */ 57 | public function getClientName() { 58 | return $this->definition['client_name']; 59 | } 60 | 61 | /** 62 | * Returns URL of the webhook. 63 | * 64 | * @return string 65 | * Webhook URL. 66 | */ 67 | public function getUrl() { 68 | return $this->definition['url']; 69 | } 70 | 71 | /** 72 | * Returns version of the webhook. 73 | * 74 | * @return string 75 | * Webhook version string. 76 | */ 77 | public function getVersion() { 78 | return $this->definition['version']; 79 | } 80 | 81 | /** 82 | * Returns state of the 'disable_retries' option. 83 | * 84 | * @return string 85 | * State of the 'disable_retries' option. 86 | */ 87 | public function getDisableRetries() { 88 | return $this->definition['disable_retries']; 89 | } 90 | 91 | /** 92 | * Returns filters list of the webhook. 93 | * 94 | * @return array 95 | * Filters list. 96 | */ 97 | public function getFilters() { 98 | return $this->definition['filters']; 99 | } 100 | 101 | /** 102 | * Returns status of the webhook. 103 | * 104 | * @return string 105 | * Webhook status. 106 | */ 107 | public function getStatus() { 108 | return $this->definition['status']; 109 | } 110 | 111 | /** 112 | * Returns 'is_migrated' property. 113 | * 114 | * @return bool 115 | * 'is_migrated' property. 116 | */ 117 | public function getIsMigrated(): bool { 118 | return $this->definition['is_migrated']; 119 | } 120 | 121 | /** 122 | * Returns 'suppressed_until' property. 123 | * 124 | * @return int 125 | * 'suppressed_until' property. 126 | */ 127 | public function getSuppressedUntil(): int { 128 | return $this->definition['suppressed_until']; 129 | } 130 | 131 | /** 132 | * Returns definition of the webhook. 133 | * 134 | * @return array 135 | * Webhook definition. 136 | */ 137 | public function getDefinition() { 138 | return $this->definition; 139 | } 140 | 141 | /** 142 | * Returns the state of the webhook. 143 | * 144 | * @return bool 145 | * Whether the webhook is in enabled or in the disabled state. 146 | */ 147 | public function isEnabled() { 148 | $enabled = [ 149 | 'enabled', 150 | '', 151 | ]; 152 | return in_array(strtolower($this->getStatus()), $enabled, TRUE); 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/CDFDocument.php: -------------------------------------------------------------------------------- 1 | setCdfEntities(...$entities); 29 | } 30 | 31 | /** 32 | * Returns entities list. 33 | * 34 | * @return \Acquia\ContentHubClient\CDF\CDFObject[] 35 | * Entities list. 36 | */ 37 | public function getEntities() { 38 | return $this->entities; 39 | } 40 | 41 | /** 42 | * Returns entity by UUID. 43 | * 44 | * @param string $uuid 45 | * Entity UUID. 46 | * 47 | * @return \Acquia\ContentHubClient\CDF\CDFObject|null 48 | * CDFObject if exists, otherwise NULL 49 | */ 50 | public function getCdfEntity($uuid) { 51 | return $this->entities[$uuid] ?? NULL; 52 | } 53 | 54 | /** 55 | * Entities setter. 56 | * 57 | * @param \Acquia\ContentHubClient\CDF\CDFObject[] $entities 58 | * Entities list. 59 | */ 60 | public function setCdfEntities(CDFObject ...$entities) { // phpcs:ignore 61 | $entitiesList = []; 62 | foreach ($entities as $entity) { 63 | $entitiesList[$entity->getUuid()] = $entity; 64 | } 65 | $this->entities = $entitiesList; 66 | } 67 | 68 | /** 69 | * Appends CDF object to document. 70 | * 71 | * @param \Acquia\ContentHubClient\CDF\CDFObject $object 72 | * CDF object. 73 | */ 74 | public function addCdfEntity(CDFObject $object) { 75 | $this->entities[$object->getUuid()] = $object; 76 | } 77 | 78 | /** 79 | * Removes entity from document. 80 | * 81 | * @param string $uuid 82 | * Entity UUID. 83 | */ 84 | public function removeCdfEntity($uuid) { 85 | unset($this->entities[$uuid]); 86 | } 87 | 88 | /** 89 | * Checks if document contains entities. 90 | * 91 | * @return bool 92 | * TRUE if entities exists, otherwise FALSE. 93 | */ 94 | public function hasEntities() { 95 | return (bool) $this->entities; 96 | } 97 | 98 | /** 99 | * Checks that entity exists in document. 100 | * 101 | * @param string $uuid 102 | * Entity UUID. 103 | * 104 | * @return bool 105 | * TRUE if entity exists, otherwise FALSE. 106 | */ 107 | public function hasEntity($uuid) { 108 | return !empty($this->entities[$uuid]); 109 | } 110 | 111 | /** 112 | * Merges CDF document into current. 113 | * 114 | * @param \Acquia\ContentHubClient\CDFDocument $document 115 | * CDF document. 116 | */ 117 | public function mergeDocuments(CDFDocument $document) { 118 | foreach ($document->getEntities() as $entity) { 119 | $this->addCdfEntity($entity); 120 | } 121 | } 122 | 123 | /** 124 | * Converts CDF document to string (JSON). 125 | * 126 | * @return false|string 127 | * String representation. 128 | */ 129 | public function toString() { 130 | $entities = []; 131 | foreach ($this->getEntities() as $entity) { 132 | $entities[] = $entity->toArray(); 133 | } 134 | $output = ['entities' => $entities]; 135 | 136 | return json_encode($output, JSON_PRETTY_PRINT); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /test/EventSubscriber/ClientCDFTest.php: -------------------------------------------------------------------------------- 1 | client_cdf = new ClientCDF(); 43 | $this->handler = current(current($this->client_cdf::getSubscribedEvents()[ContentHubLibraryEvents::GET_CDF_CLASS])); 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function tearDown(): void { 50 | parent::tearDown(); 51 | 52 | unset($this->client_cdf, $this->handler); 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function testGetSubscribedEventsAddsHandlerToEvent() { 59 | $this->assertNotNull($this->handler); 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function testEventGetsHandledWhenTypeIsClient() { 66 | $event = $this->getGetCDFTypeEvent(); 67 | $event->expects($this->once()) 68 | ->method('getType') 69 | ->willReturn('client'); 70 | 71 | $event->expects($this->once()) 72 | ->method('getData') 73 | ->willReturn(['metadata' => 'Client Metadata']); 74 | 75 | $event->expects($this->once()) 76 | ->method('setObject'); 77 | 78 | $event->expects($this->once()) 79 | ->method('stopPropagation'); 80 | 81 | $this->client_cdf->{$this->handler}($event); 82 | } 83 | 84 | /** 85 | * {@inheritdoc} 86 | */ 87 | public function testHandlerDoesNothingWhenTypeIsNonClient() { 88 | $event = $this->getGetCDFTypeEvent(); 89 | $event->expects($this->once()) 90 | ->method('getType') 91 | ->willReturn('non-client'); 92 | 93 | $event->expects($this->never()) 94 | ->method('getData'); 95 | 96 | $event->expects($this->never()) 97 | ->method('setObject'); 98 | 99 | $event->expects($this->never()) 100 | ->method('stopPropagation'); 101 | 102 | $this->client_cdf->{$this->handler}($event); 103 | } 104 | 105 | /** 106 | * Mock builder for GetCDFTypeEvent class. 107 | * 108 | * @return \Acquia\ContentHubClient\Event\GetCDFTypeEvent 109 | * Mocked object. 110 | */ 111 | private function getGetCDFTypeEvent(): GetCDFTypeEvent { // phpcs:ignore 112 | $mock_cdf_object = \Mockery::mock(CDFObject::class); 113 | $mock_client_cdf_object = \Mockery::mock('overload:' . ClientCDFObject::class); 114 | 115 | $mock_client_cdf_object->shouldReceive('fromArray') 116 | ->once() 117 | ->andReturn($mock_cdf_object); 118 | 119 | return $this->getMockBuilder(GetCDFTypeEvent::class) 120 | ->disableOriginalConstructor() 121 | ->onlyMethods([ 122 | 'getType', 123 | 'setObject', 124 | 'stopPropagation', 125 | 'getData', 126 | 'getObject', 127 | ]) 128 | ->getMock(); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/Guzzle/Middleware/RequestResponseLogger.php: -------------------------------------------------------------------------------- 1 | request = $request; 57 | $this->response = $response; 58 | $this->logger = $logger; 59 | $this->decodedResponseBody = json_decode($response->getBody(), TRUE); 60 | } 61 | 62 | /** 63 | * Logs response/request data. 64 | */ 65 | public function log(): void { 66 | if (!$this->isTrackable()) { 67 | return; 68 | } 69 | 70 | $message = $this->buildLogMessage(); 71 | 72 | $this->logMessage($message, $this->response->getStatusCode()); 73 | } 74 | 75 | /** 76 | * Checks if a response can be tracked. 77 | * 78 | * @return bool 79 | * TRUE in the case when the response should be tracked. 80 | */ 81 | protected function isTrackable(): bool { 82 | // Skip tracking for requests without ID. 83 | if (empty($this->decodedResponseBody['request_id'])) { 84 | return FALSE; 85 | } 86 | 87 | // Skip tracking for requests with a shared secret. 88 | if (isset($this->decodedResponseBody['shared_secret'])) { 89 | return FALSE; 90 | } 91 | 92 | // Skip tracking for requests with sensitive data. 93 | if (isset($this->decodedResponseBody['data']['data']['metadata']['settings'])) { 94 | return FALSE; 95 | } 96 | 97 | return TRUE; 98 | } 99 | 100 | /** 101 | * Builds log message. 102 | * 103 | * @return string 104 | * Log message. 105 | */ 106 | protected function buildLogMessage(): string { 107 | return sprintf( 108 | 'Request ID: %s. Method: %s. Path: %s. Status code: %d.', 109 | $this->decodedResponseBody['request_id'], 110 | $this->request->getMethod(), 111 | $this->request->getUri()->getPath(), 112 | $this->response->getStatusCode() 113 | ); 114 | } 115 | 116 | /** 117 | * Logs message depending on response status code. 118 | * 119 | * @param string $message 120 | * Log message. 121 | * @param int $responseStatusCode 122 | * Response status code. 123 | */ 124 | protected function logMessage(string $message, int $responseStatusCode): void { 125 | if ($responseStatusCode >= Response::HTTP_INTERNAL_SERVER_ERROR) { 126 | $this->logger->error($message); 127 | 128 | return; 129 | } 130 | 131 | if ($responseStatusCode >= Response::HTTP_BAD_REQUEST) { 132 | $this->logger->warning($message); 133 | 134 | return; 135 | } 136 | 137 | $this->logger->info($message); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /test/SettingsTestBase.php: -------------------------------------------------------------------------------- 1 | "someuser", 34 | "created" => "2014-12-21T20:12:11+00:00Z", 35 | "modified" => "2014-12-21T20:12:11+00:00Z", 36 | "webhooks" => [ 37 | [ 38 | "url" => "http://example1.com/webhooks", 39 | "uuid" => "00000000-0000-0000-0000-000000000000", 40 | ], 41 | [ 42 | "url" => "http://example2.com/webhooks", 43 | "uuid" => "11111111-0000-0000-0000-000000000000", 44 | ], 45 | ], 46 | "clients" => [ 47 | [ 48 | "name" => "My Client Site 1", 49 | "uuid" => "22222222-0000-0000-0000-000000000000", 50 | ], 51 | ], 52 | "success" => 1, 53 | ]; 54 | } 55 | 56 | /** 57 | * Tests settings. 58 | */ 59 | public function testReadSettings() { 60 | // Setup. 61 | $data = $this->setData(); 62 | $responses = [ 63 | new Response(200, [], json_encode($data)), 64 | ]; 65 | $client = $this->getClient($responses); 66 | 67 | // Read Settings. 68 | $settings = $client->getSettings(); 69 | $this->assertEquals($data['uuid'], $settings->getUuid()); 70 | $this->assertEquals($data['created'], $settings->getCreated()); 71 | $this->assertEquals($data['modified'], $settings->getModified()); 72 | $this->assertEquals($data['webhooks'], $settings->getWebhooks()); 73 | $this->assertEquals($data['clients'], $settings->getClients()); 74 | $this->assertEquals($data['success'], $settings->success()); 75 | 76 | $this->assertEquals($data['webhooks'][0], 77 | $settings->getWebhook('http://example1.com/webhooks')); 78 | $this->assertFalse($settings->getWebhook('http://example.com/webhook')); 79 | $this->assertEquals($data['clients'][0], 80 | $settings->getClient('My Client Site 1')); 81 | $this->assertFalse($settings->getClient('My Client Site 2')); 82 | } 83 | 84 | /** 85 | * @covers \Acquia\ContentHubClient\ContentHubClient::register 86 | */ 87 | public function testRegisterClients() { 88 | // Setup. 89 | $data = $this->setData()['clients'][0]; 90 | $responses = [ 91 | new Response(200, [], json_encode($data)), 92 | ]; 93 | $client = $this->getClient($responses); 94 | 95 | // Add a Client. 96 | $registered_client = $client->register('My Client Site 1'); 97 | $this->assertEquals($data, $registered_client); 98 | } 99 | 100 | /** 101 | * @covers \Acquia\ContentHubClient\ContentHubClient::addWebhook 102 | */ 103 | public function testAddWebhook() { 104 | // Setup. 105 | $data = $this->setData()['webhooks'][0]; 106 | $responses = [ 107 | new Response(200, [], json_encode($data)), 108 | ]; 109 | $client = $this->getClient($responses); 110 | 111 | // Set a Webhook. 112 | $webhook = $client->addWebhook('http://example1.com/webhooks'); 113 | $this->assertEquals($data, $webhook); 114 | } 115 | 116 | /** 117 | * @covers \Acquia\ContentHubClient\ContentHubClient::deleteWebhook 118 | */ 119 | public function testDeleteWebhook() { 120 | // Setup. 121 | $data = [ 122 | 'success' => 1, 123 | ]; 124 | $responses = [ 125 | new Response(200, [], json_encode($data)), 126 | ]; 127 | $client = $this->getClient($responses); 128 | 129 | // Deletes a Webhook. 130 | $webhook = $client->deleteWebhook('http://example1.com/webhooks'); 131 | $this->assertEquals($data, json_decode($webhook->getBody(), TRUE)); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /test/SearchCriteria/SearchCriteriaBuilderTest.php: -------------------------------------------------------------------------------- 1 | default_search_criteria_data = [ 41 | 'search_term' => SearchCriteria::DEFAULT_SEARCH_TERM, 42 | 'type' => [], 43 | 'bundle' => [], 44 | 'tags' => [], 45 | 'label' => '', 46 | 'start_date' => NULL, 47 | 'end_date' => NULL, 48 | 'from' => SearchCriteria::DEFAULT_OFFSET, 49 | 'size' => SearchCriteria::DEFAULT_LIMIT, 50 | 'sorting' => '', 51 | 'version' => SearchCriteria::DEFAULT_VERSION, 52 | 'languages' => [CDFObject::LANGUAGE_UNDETERMINED], 53 | ]; 54 | 55 | $this->search_criteria_data = [ 56 | 'search_term' => 'some-search-term', 57 | 'type' => ['type1', 'type2'], 58 | 'bundle' => ['bundle1', 'bundle2', 'bundle3'], 59 | 'tags' => ['tag1', 'tag2'], 60 | 'label' => 'some-label', 61 | 'start_date' => new \DateTimeImmutable(), 62 | 'end_date' => new \DateTimeImmutable(), 63 | 'from' => 10, 64 | 'size' => 20, 65 | 'sorting' => '', 66 | 'version' => 'some-version', 67 | 'languages' => ['lang1'], 68 | ]; 69 | } 70 | 71 | /** 72 | * {@inheritDoc} 73 | */ 74 | protected function tearDown(): void { 75 | parent::tearDown(); 76 | } 77 | 78 | /** 79 | * Tests SearchCriteria creation with all required params. 80 | */ 81 | public function testObjectCreationWithAllRequiredParams(): void { 82 | $search_criteria = SearchCriteriaBuilder::createFromArray($this->search_criteria_data); 83 | 84 | $this->assertEquals($search_criteria->jsonSerialize(), $this->search_criteria_data); 85 | $this->assertEquals($search_criteria->getSearchTerm(), $this->search_criteria_data['search_term']); 86 | $this->assertEquals($search_criteria->getEntityType(), $this->search_criteria_data['type']); 87 | $this->assertEquals($search_criteria->getBundle(), $this->search_criteria_data['bundle']); 88 | $this->assertEquals($search_criteria->getTags(), $this->search_criteria_data['tags']); 89 | $this->assertEquals($search_criteria->getLabel(), $this->search_criteria_data['label']); 90 | $this->assertEquals($search_criteria->getStartDate(), $this->search_criteria_data['start_date']); 91 | $this->assertEquals($search_criteria->getEndDate(), $this->search_criteria_data['end_date']); 92 | $this->assertEquals($search_criteria->getFrom(), $this->search_criteria_data['from']); 93 | $this->assertEquals($search_criteria->getSize(), $this->search_criteria_data['size']); 94 | $this->assertEquals($search_criteria->getSorting(), $this->search_criteria_data['sorting']); 95 | $this->assertEquals($search_criteria->getVersion(), $this->search_criteria_data['version']); 96 | } 97 | 98 | /** 99 | * Tests SearchCriteria creation with all default params. 100 | */ 101 | public function testObjectCreationWithAllDefaultParams(): void { 102 | $search_criteria = SearchCriteriaBuilder::createFromArray($this->default_search_criteria_data); 103 | 104 | $this->assertEquals($search_criteria->jsonSerialize(), $this->default_search_criteria_data); 105 | } 106 | 107 | /** 108 | * Tests SearchCriteria creation from empty array. 109 | */ 110 | public function testObjectCreationWithUnsetParamsUsesDefault(): void { 111 | $search_criteria = SearchCriteriaBuilder::createFromArray([]); 112 | 113 | $this->assertEquals($search_criteria->jsonSerialize(), $this->default_search_criteria_data); 114 | } 115 | 116 | /** 117 | * Tests language switching. 118 | */ 119 | public function testSetLanguagesOnSearchCriteriaChangesLanguages(): void { 120 | $new_languages = ['new-lang1', 'new-lang2']; 121 | 122 | $search_criteria = SearchCriteriaBuilder::createFromArray($this->search_criteria_data); 123 | $this->assertNotEquals($search_criteria->getLanguages(), $new_languages); 124 | 125 | $search_criteria->setLanguages($new_languages); 126 | 127 | $this->assertEquals($search_criteria->getLanguages(), $new_languages); 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/Settings.php: -------------------------------------------------------------------------------- 1 | name = $name; 92 | $this->uuid = $uuid; 93 | $this->apiKey = $api_key; 94 | $this->secretKey = $secret_key; 95 | $this->url = $url; 96 | $this->sharedSecret = $shared_secret; 97 | $this->webhook = $webhook; 98 | } 99 | 100 | /** 101 | * Transforms settings object to array. 102 | * 103 | * @return array 104 | * Array representation. 105 | */ 106 | public function toArray() { 107 | return [ 108 | 'name' => $this->getName(), 109 | 'uuid' => $this->getUuid(), 110 | 'apiKey' => $this->getApiKey(), 111 | 'secretKey' => $this->getSecretKey(), 112 | 'url' => $this->getUrl(), 113 | 'sharedSecret' => $this->getSharedSecret(), 114 | 'webhook' => $this->webhook, 115 | ]; 116 | } 117 | 118 | /** 119 | * Get the settings name. 120 | * 121 | * @return string 122 | * Name attribute. 123 | */ 124 | public function getName() { 125 | return $this->name; 126 | } 127 | 128 | /** 129 | * Returns the Uuid. 130 | * 131 | * @return string|bool 132 | * UUID attribute. 133 | */ 134 | public function getUuid() { 135 | return !empty($this->uuid) ? $this->uuid : FALSE; 136 | } 137 | 138 | /** 139 | * Returns the webhook UUID associated with this site. 140 | * 141 | * @param string $op 142 | * Optional setting to grab the specific key for a webhook. Options are: 143 | * * uuid: The UUID for the webhook. 144 | * * url: The fully qualified URL that plexus accesses the site. 145 | * * settings_url: The nice URL that users interact with. 146 | * 147 | * @return string 148 | * The Webhook if set from connection settings. 149 | */ 150 | public function getWebhook($op = 'settings_url') { 151 | if (empty($this->webhook) || !isset($this->webhook[$op])) { 152 | return FALSE; 153 | } 154 | 155 | return $this->webhook[$op]; 156 | } 157 | 158 | /** 159 | * Returns URL of the endpoint. 160 | * 161 | * @return string 162 | * URL of the endpoint. 163 | */ 164 | public function getUrl() { 165 | return $this->url; 166 | } 167 | 168 | /** 169 | * Returns middleware. 170 | * 171 | * @codeCoverageIgnore 172 | * 173 | * @return \Acquia\Hmac\Guzzle\HmacAuthMiddleware 174 | * Auth middleware. 175 | */ 176 | public function getMiddleware() { 177 | $key = new Key($this->getApiKey(), $this->getSecretKey()); 178 | 179 | return new HmacAuthMiddleware($key); 180 | } 181 | 182 | /** 183 | * Returns API key of these settings. 184 | * 185 | * @return string 186 | * API key of these settings. 187 | */ 188 | public function getApiKey() { 189 | return $this->apiKey; 190 | } 191 | 192 | /** 193 | * Returns the API Secret Key used for Webhook verification. 194 | * 195 | * @return string|bool 196 | * The api secret key if it is set, FALSE otherwise. 197 | */ 198 | public function getSecretKey() { 199 | return $this->secretKey; 200 | } 201 | 202 | /** 203 | * Returns shared secret. 204 | * 205 | * @return null|string 206 | * Shared secret. 207 | */ 208 | public function getSharedSecret() { 209 | return $this->sharedSecret; 210 | } 211 | 212 | } 213 | -------------------------------------------------------------------------------- /src/ObjectFactory.php: -------------------------------------------------------------------------------- 1 | attributeData = $this->getAttributeData(); 37 | $this->attributeId = $this->attributeData['attributes']['id']; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | public function tearDown(): void { 44 | unset($this->attributeId); 45 | unset($this->attributeData); 46 | } 47 | 48 | /** 49 | * @covers \Acquia\ContentHubClient\CDFAttribute::getType 50 | * 51 | * @throws \Exception 52 | */ 53 | public function testCreateStringAttribute() { 54 | $title = $this->attributeData['attributes']['title']; 55 | 56 | $attribute = new CDFAttribute($this->attributeId, CDFAttribute::TYPE_STRING); 57 | $this->assertEquals('string', $attribute->getType()); 58 | 59 | foreach ($title['value'] as $language => $value) { 60 | $attribute->setValue($value, $language); 61 | } 62 | 63 | $this->assertEquals($title['value'], $attribute->getValue()); 64 | } 65 | 66 | /** 67 | * @covers \Acquia\ContentHubClient\CDFAttribute::getType 68 | * 69 | * @throws \Exception 70 | */ 71 | public function testCreateNumericAttribute() { 72 | $numericAttribute = $this->attributeData['attributes']['num']; 73 | 74 | $attribute = new CDFAttribute($this->attributeId, CDFAttribute::TYPE_NUMBER); 75 | $this->assertEquals('number', $attribute->getType()); 76 | 77 | foreach ($numericAttribute['value'] as $language => $value) { 78 | $attribute->setValue($value, $language); 79 | } 80 | 81 | $this->assertEquals($numericAttribute['value'], $attribute->getValue()); 82 | } 83 | 84 | /** 85 | * @covers \Acquia\ContentHubClient\CDFAttribute 86 | * @throws \Exception 87 | */ 88 | public function testAttributeToArrayConvert() { 89 | $value = $this->attributeData['attributes']['num']['value']['en']; 90 | 91 | $attribute = new CDFAttribute($this->attributeId, CDFAttribute::TYPE_NUMBER, $value); 92 | 93 | $expected = [ 94 | 'type' => CDFAttribute::TYPE_NUMBER, 95 | 'value' => [CDFObject::LANGUAGE_UNDETERMINED => $value], 96 | ]; 97 | 98 | $this->assertEquals($this->attributeId, $attribute->getId()); 99 | $this->assertEquals($expected, $attribute->toArray()); 100 | } 101 | 102 | /** 103 | * @covers \Acquia\ContentHubClient\CDFAttribute 104 | * 105 | * @throws \Exception 106 | */ 107 | public function testCreateNumericArrayAttribute() { 108 | $numericArrayAttribute = $this->attributeData['attributes']['num_array']; 109 | 110 | $attribute = new CDFAttribute($this->attributeId, CDFAttribute::TYPE_ARRAY_NUMBER); 111 | $this->assertEquals('array', $attribute->getType()); 112 | 113 | foreach ($numericArrayAttribute['value'] as $language => $value) { 114 | $attribute->setValue($value, $language); 115 | } 116 | 117 | $this->assertEquals($numericArrayAttribute['value'], $attribute->getValue()); 118 | } 119 | 120 | /** 121 | * @covers \Acquia\ContentHubClient\CDFAttribute 122 | */ 123 | public function testUnsupportedDataTypeAttribute() { 124 | try { 125 | $dataType = 'unsupported_data_type'; 126 | $attribute = new CDFAttribute($this->attributeId, $dataType); 127 | $this->fail(sprintf("It was expected an exception from \"%s\".", $dataType)); 128 | } 129 | catch (\Exception $e) { 130 | $this->assertEquals(sprintf("Unsupported CDF Attribute data type \"%s\".", $dataType), $e->getMessage()); 131 | } 132 | } 133 | 134 | /** 135 | * Provides test data. 136 | * 137 | * @return array 138 | * Test data. 139 | */ 140 | private function getAttributeData() { 141 | return [ 142 | "attributes" => [ 143 | "id" => 1, 144 | "num_array" => [ 145 | "type" => "array", 146 | "value" => [ 147 | "en" => [ 148 | 6.66, 149 | 3.23, 150 | ], 151 | "hu" => [ 152 | 4.66, 153 | 4.23, 154 | ], 155 | "und" => [ 156 | 1.22, 157 | 1.11, 158 | ], 159 | ], 160 | ], 161 | "num" => [ 162 | "type" => "number", 163 | "value" => [ 164 | 'en' => 13.45, 165 | 'es' => 1.43, 166 | CDFObject::LANGUAGE_UNDETERMINED => 1.23, 167 | ], 168 | ], 169 | "title" => [ 170 | "type" => "string", 171 | "value" => [ 172 | "en" => "nothing", 173 | "es" => "nada", 174 | CDFObject::LANGUAGE_UNDETERMINED => "niente", 175 | ], 176 | ], 177 | ], 178 | ]; 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /src/ContentHubClientCommonTrait.php: -------------------------------------------------------------------------------- 1 | = 7. 10 | if (defined('\GuzzleHttp\ClientInterface::MAJOR_VERSION')) { 11 | /** 12 | * Common client trait for guzzle 7 and above. 13 | */ 14 | trait ContentHubClientCommonTrait { 15 | 16 | /** 17 | * The last call's response object. 18 | * 19 | * @var \Psr\Http\Message\ResponseInterface 20 | */ 21 | private $response; 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function request(string $method, $uri, array $options = []): ResponseInterface { 27 | try { 28 | $response = $this->httpClient->request($method, $uri, $options); 29 | } 30 | catch (\Exception $e) { 31 | $response = $this->getExceptionResponse($method, $uri, $e); 32 | } 33 | $this->response = $response; 34 | return $response; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function requestAsync(string $method, $uri, array $options = []): PromiseInterface { 41 | try { 42 | return $this->httpClient->requestAsync($method, $uri, $options); 43 | } 44 | catch (\Exception $e) { 45 | return $this->getExceptionResponse($method, $uri, $e); 46 | } 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function send(RequestInterface $request, array $options = []): ResponseInterface { 53 | try { 54 | return $this->httpClient->send($request, $options); 55 | } 56 | catch (\Exception $e) { 57 | return $this->getExceptionResponse($request->getMethod(), $request->getUri()->getPath(), $e); 58 | } 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function sendAsync(RequestInterface $request, array $options = []): PromiseInterface { 65 | try { 66 | return $this->httpClient->sendAsync($request, $options); 67 | } 68 | catch (\Exception $e) { 69 | return $this->getExceptionResponse($request->getMethod(), $request->getUri()->getPath(), $e); 70 | } 71 | } 72 | 73 | /** 74 | * Get a client configuration option. 75 | * 76 | * @param string|null $option 77 | * The config option to retrieve. 78 | * 79 | * @return mixed 80 | * The client configurations. 81 | */ 82 | public function getConfig(?string $option = NULL) { 83 | return $option === NULL 84 | ? $this->config 85 | : ($this->config[$option] ?? NULL); 86 | } 87 | 88 | /** 89 | * Returns the response object from the last call. 90 | * 91 | * In case further examination needed e.g. status code or error message. 92 | * 93 | * @return \Psr\Http\Message\ResponseInterface 94 | * The response object. 95 | */ 96 | public function getResponse(): ResponseInterface { 97 | return $this->response; 98 | } 99 | 100 | } 101 | } 102 | else { 103 | /** 104 | * Common client trait for guzzle 6. 105 | */ 106 | trait ContentHubClientCommonTrait { 107 | 108 | /** 109 | * The last call's response object. 110 | * 111 | * @var \Psr\Http\Message\ResponseInterface 112 | */ 113 | private $response; 114 | 115 | /** 116 | * {@inheritdoc} 117 | */ 118 | public function request($method, $uri, array $options = []) { 119 | try { 120 | $response = $this->httpClient->request($method, $uri, $options); 121 | } 122 | catch (\Exception $e) { 123 | $response = $this->getExceptionResponse($method, $uri, $e); 124 | } 125 | $this->response = $response; 126 | return $response; 127 | } 128 | 129 | /** 130 | * Returns the response object from the last call. 131 | * 132 | * In case further examination needed e.g. status code or error message. 133 | * 134 | * @return \Psr\Http\Message\ResponseInterface 135 | * The response object. 136 | */ 137 | public function getResponse(): ResponseInterface { 138 | return $this->response; 139 | } 140 | 141 | /** 142 | * {@inheritdoc} 143 | */ 144 | public function requestAsync($method, $uri, array $options = []) { 145 | try { 146 | return $this->httpClient->requestAsync($method, $uri, $options); 147 | } 148 | catch (\Exception $e) { 149 | return $this->getExceptionResponse($method, $uri, $e); 150 | } 151 | } 152 | 153 | /** 154 | * {@inheritdoc} 155 | */ 156 | public function send(RequestInterface $request, array $options = []) { 157 | try { 158 | return $this->httpClient->send($request, $options); 159 | } 160 | catch (\Exception $e) { 161 | return $this->getExceptionResponse($request->getMethod(), $request->getUri()->getPath(), $e); 162 | } 163 | } 164 | 165 | /** 166 | * {@inheritdoc} 167 | */ 168 | public function sendAsync(RequestInterface $request, array $options = []) { 169 | try { 170 | return $this->httpClient->sendAsync($request, $options); 171 | } 172 | catch (\Exception $e) { 173 | return $this->getExceptionResponse($request->getMethod(), $request->getUri()->getPath(), $e); 174 | } 175 | } 176 | 177 | /** 178 | * Get a client configuration option. 179 | * 180 | * @param string|null $option 181 | * The config option to retrieve. 182 | * 183 | * @return mixed 184 | * The client configurations. 185 | */ 186 | public function getConfig($option = NULL) { 187 | return $option === NULL 188 | ? $this->config 189 | : ($this->config[$option] ?? NULL); 190 | } 191 | 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/SearchCriteria/SearchCriteria.php: -------------------------------------------------------------------------------- 1 | searchTerm = $searchTerm; 149 | $this->entityType = $entityType; 150 | $this->bundle = $bundle; 151 | $this->tags = $tags; 152 | $this->label = $label; 153 | $this->startDate = $startDate; 154 | $this->endDate = $endDate; 155 | $this->from = $from; 156 | $this->size = $size; 157 | $this->sorting = $sorting; 158 | $this->version = $version; 159 | $this->languages = $languages; 160 | } 161 | 162 | /** 163 | * {@inheritDoc} 164 | * 165 | * @return array 166 | * The serialized array. 167 | */ 168 | public function jsonSerialize(): array { 169 | return [ 170 | 'search_term' => $this->getSearchTerm(), 171 | 'type' => $this->getEntityType(), 172 | 'bundle' => $this->getBundle(), 173 | 'tags' => $this->getTags(), 174 | 'label' => $this->getLabel(), 175 | 'start_date' => $this->getStartDate(), 176 | 'end_date' => $this->getEndDate(), 177 | 'from' => $this->getFrom(), 178 | 'size' => $this->getSize(), 179 | 'sorting' => $this->getSorting(), 180 | 'version' => $this->getVersion(), 181 | 'languages' => $this->getLanguages(), 182 | ]; 183 | } 184 | 185 | /** 186 | * Search term getter. 187 | * 188 | * @return string 189 | * Search term. 190 | */ 191 | public function getSearchTerm(): string { 192 | return $this->searchTerm; 193 | } 194 | 195 | /** 196 | * Entity types getter. 197 | * 198 | * @return array 199 | * Entity types list. 200 | */ 201 | public function getEntityType(): array { 202 | return $this->entityType; 203 | } 204 | 205 | /** 206 | * Bundle getter. 207 | * 208 | * @return array 209 | * Bundles list. 210 | */ 211 | public function getBundle(): array { 212 | return $this->bundle; 213 | } 214 | 215 | /** 216 | * Tags getter. 217 | * 218 | * @return array 219 | * Tags list. 220 | */ 221 | public function getTags(): array { 222 | return $this->tags; 223 | } 224 | 225 | /** 226 | * Label getter. 227 | * 228 | * @return string 229 | * Label. 230 | */ 231 | public function getLabel(): string { 232 | return $this->label; 233 | } 234 | 235 | /** 236 | * Start date getter. 237 | * 238 | * @return \DateTimeInterface|null 239 | * Search start date. 240 | */ 241 | public function getStartDate(): ?\DateTimeInterface { 242 | return $this->startDate; 243 | } 244 | 245 | /** 246 | * End date getter. 247 | * 248 | * @return \DateTimeInterface|null 249 | * Search end date. 250 | */ 251 | public function getEndDate(): ?\DateTimeInterface { 252 | return $this->endDate; 253 | } 254 | 255 | /** 256 | * Returns number of items that should be skipped before selection. 257 | * 258 | * @return int 259 | * A number of items that should be skipped before selection. 260 | */ 261 | public function getFrom(): int { 262 | return $this->from; 263 | } 264 | 265 | /** 266 | * Returns how many items should be selected. 267 | * 268 | * @return int 269 | * Number of items that should be selected. 270 | */ 271 | public function getSize(): int { 272 | return $this->size; 273 | } 274 | 275 | /** 276 | * Sorting getter. 277 | * 278 | * @return string 279 | * Sorting value. 280 | */ 281 | public function getSorting(): string { 282 | return $this->sorting; 283 | } 284 | 285 | /** 286 | * Version getter. 287 | * 288 | * @return string 289 | * Version string. 290 | */ 291 | public function getVersion(): string { 292 | return $this->version; 293 | } 294 | 295 | /** 296 | * Languages getter. 297 | * 298 | * @return array 299 | * Languages list. 300 | */ 301 | public function getLanguages(): array { 302 | return $this->languages; 303 | } 304 | 305 | /** 306 | * Languages setter. 307 | * 308 | * @param array $languages 309 | * Languages list. 310 | */ 311 | public function setLanguages(array $languages): void { 312 | $this->languages = $languages; 313 | } 314 | 315 | } 316 | -------------------------------------------------------------------------------- /src/CDF/CDFObject.php: -------------------------------------------------------------------------------- 1 | type = $type; 97 | $this->uuid = $uuid; 98 | $this->created = $created; 99 | $this->modified = $modified; 100 | $this->origin = $origin; 101 | $this->setMetadata($metadata); 102 | } 103 | 104 | /** 105 | * Static Factory method to allow CDFObject to interpret their own data. 106 | * 107 | * @param array $data 108 | * Initial data. 109 | * 110 | * @return \Acquia\ContentHubClient\CDF\CDFObject 111 | * CDFObject. 112 | * 113 | * @throws \ReflectionException 114 | */ 115 | public static function fromArray(array $data) { 116 | $object = new static($data['type'], $data['uuid'], $data['created'], $data['modified'], $data['origin'], $data['metadata']); 117 | foreach ($data['attributes'] as $attribute_name => $values) { 118 | if (!$attribute = $object->getAttribute($attribute_name)) { 119 | $class = $object->getMetadata()['attributes'][$attribute_name]['class'] ?? 'non-existing-class'; 120 | 121 | if (class_exists($class)) { 122 | $object->addAttribute($attribute_name, $values['type'], NULL, self::LANGUAGE_UNDETERMINED, $class); 123 | } 124 | else { 125 | $object->addAttribute($attribute_name, $values['type'], NULL); 126 | } 127 | 128 | $attribute = $object->getAttribute($attribute_name); 129 | } 130 | $value_property = (new \ReflectionClass($attribute))->getProperty('value'); 131 | $value_property->setAccessible(TRUE); 132 | $value_property->setValue($attribute, $values['value']); 133 | } 134 | return $object; 135 | } 136 | 137 | /** 138 | * Static Factory method to format data from JSON into the CDFObject. 139 | * 140 | * @param string $json 141 | * Data in JSON format. 142 | * 143 | * @return \Acquia\ContentHubClient\CDF\CDFObject 144 | * CDFObject. 145 | * 146 | * @throws \ReflectionException 147 | */ 148 | public static function fromJson(string $json) { 149 | return self::fromArray(json_decode($json, TRUE)); 150 | } 151 | 152 | /** 153 | * {@inheritdoc} 154 | */ 155 | public function getType() { 156 | return $this->type; 157 | } 158 | 159 | /** 160 | * {@inheritdoc} 161 | */ 162 | public function getUuid() { 163 | return $this->uuid; 164 | } 165 | 166 | /** 167 | * {@inheritdoc} 168 | */ 169 | public function getCreated() { 170 | return $this->created; 171 | } 172 | 173 | /** 174 | * {@inheritdoc} 175 | */ 176 | public function getModified() { 177 | return $this->modified; 178 | } 179 | 180 | /** 181 | * {@inheritdoc} 182 | */ 183 | public function getOrigin() { 184 | return $this->origin; 185 | } 186 | 187 | /** 188 | * {@inheritdoc} 189 | */ 190 | public function getMetadata() { 191 | return $this->metadata; 192 | } 193 | 194 | /** 195 | * {@inheritdoc} 196 | */ 197 | public function setMetadata(array $metadata) { 198 | $this->metadata = $metadata; 199 | } 200 | 201 | /** 202 | * {@inheritdoc} 203 | */ 204 | public function getModuleDependencies() { 205 | return !empty($this->metadata['dependencies']['module']) ? $this->metadata['dependencies']['module'] : []; 206 | } 207 | 208 | /** 209 | * {@inheritdoc} 210 | */ 211 | public function getDependencies() { 212 | return !empty($this->metadata['dependencies']['entity']) ? $this->metadata['dependencies']['entity'] : []; 213 | } 214 | 215 | /** 216 | * {@inheritdoc} 217 | */ 218 | public function hasProcessedDependencies() { 219 | return $this->processed; 220 | } 221 | 222 | /** 223 | * {@inheritdoc} 224 | */ 225 | public function markProcessedDependencies() { 226 | $this->processed = TRUE; 227 | } 228 | 229 | /** 230 | * {@inheritdoc} 231 | */ 232 | public function getAttributes() { 233 | return $this->attributes; 234 | } 235 | 236 | /** 237 | * {@inheritdoc} 238 | * 239 | * @return null|\Acquia\ContentHubClient\CDFAttribute 240 | * Attribute object. 241 | */ 242 | public function getAttribute($id): ?CDFAttribute { 243 | if (!empty($this->attributes[$id])) { 244 | return $this->attributes[$id]; 245 | } 246 | return NULL; 247 | } 248 | 249 | /** 250 | * {@inheritdoc} 251 | */ 252 | public function addAttribute( 253 | $id, 254 | $type, 255 | $value = NULL, 256 | $language = self::LANGUAGE_UNDETERMINED, 257 | $className = CDFAttribute::class 258 | ) { 259 | if ($className !== CDFAttribute::class && !is_subclass_of($className, CDFAttribute::class)) { 260 | throw new \Exception(sprintf("The %s class must be a subclass of \Acquia\ContentHubClient\CDFAttribute", $className)); 261 | } 262 | $attribute = new $className($id, $type, $value, $language); 263 | $this->attributes[$attribute->getId()] = $attribute; 264 | // Keep track of the class used for this attribute. 265 | if ($className !== CDFAttribute::class) { 266 | $this->metadata['attributes'][$attribute->getId()]['class'] = $className; 267 | } 268 | else { 269 | unset($this->metadata['attributes'][$attribute->getId()]); 270 | } 271 | } 272 | 273 | /** 274 | * {@inheritdoc} 275 | */ 276 | public function toArray() { 277 | $output = [ 278 | 'uuid' => $this->getUuid(), 279 | 'type' => $this->getType(), 280 | 'created' => $this->getCreated(), 281 | 'modified' => $this->getModified(), 282 | 'origin' => $this->getOrigin(), 283 | ]; 284 | if ($attributes = $this->getAttributes()) { 285 | foreach ($attributes as $attribute) { 286 | $output['attributes'][$attribute->getId()] = $attribute->toArray(); 287 | } 288 | } 289 | if ($metadata = $this->getMetadata()) { 290 | $output['metadata'] = $metadata; 291 | } 292 | 293 | return $output; 294 | } 295 | 296 | } 297 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Acquia Content Hub Client for PHP 2 | 3 | [![Build Status](https://travis-ci.org/acquia/content-hub-php.svg)](https://travis-ci.org/acquia/content-hub-php) 4 | 5 | A PHP Client library to consume the Acquia Content Hub API. 6 | 7 | ## Version Information 8 | 9 | * `0.6.x` branch: Uses guzzle version `~5.0`. Drupal 7 [content hub module](https://docs.acquia.com/content-hub) depends upon builds against this branch. 10 | * `master` branch: Uses guzzle version `~6.0`. Drupal 8 content hub work, that is in progress at the moment, depends upon builds against this branch. 11 | 12 | ## Installation 13 | 14 | Install the latest version with [Composer](https://getcomposer.org/): 15 | 16 | ```bash 17 | $ composer require acquia/content-hub-php 18 | ``` 19 | 20 | ## Usage 21 | 22 | #### Register the application 23 | 24 | Applications must register themselves with Content Hub so that they are assigned 25 | a unique identifier. The identifier is required by most API endpoints and is 26 | used as the "origin" of entities that are created by the application and 27 | published to the hub. 28 | 29 | ```php 30 | 1.3, the api key is passed via the HMAC middleware. 44 | $middleware = new MiddlewareHmacV1($apiKey, $secretKey, 'V1'); 45 | $client = new ContentHub('', $middleware, ['base_url' => $url]); 46 | 47 | // For versions < 1.3, use the following client callback. 48 | $client = new ContentHub($apiKey, $secretKey, '', ['base_url' => $url]); 49 | 50 | // Register the application (or client site) with Content Hub. The parameter 51 | // passed to this method is the human-readable name of the application. 52 | $clientSite = $client->register('myclientsite'); 53 | 54 | // Stores the application's unique identifier that is assigned by Content Hub. 55 | $clientId = $clientSite['uuid']; 56 | 57 | ``` 58 | 59 | #### Add a webhook receiver endpoint 60 | 61 | Content Hub sends push notifications and status messages for asynchronous 62 | operations via webhooks. Even if your application doesn't require near-real-time 63 | content updates, you should implement a webhook receiver endpoint so that you 64 | have visibility into what is happening during asynchronous operations. 65 | 66 | ```php 67 | addWebhook('http://example.com/content-hub/webhooks'); 71 | 72 | // Deleting the webhook receiver endpoint so that the application will no longer 73 | // receive webhooks. 74 | $client->deleteWebhook($webhook['uuid']); 75 | 76 | ``` 77 | 78 | #### Creating entities 79 | 80 | ```php 81 | $url]); 92 | 93 | // The unique identifier of the entity, usually a randomly generated UUID. 94 | // See https://github.com/ramsey/uuid to simplify UUID generation in PHP. 95 | $uuid = '00000000-0000-0000-0000-000000000000' 96 | 97 | // Build the entity, add required metadata 98 | $entity = new Entity(); 99 | $entity->setUuid($uuid); 100 | $entity->setType('product'); 101 | $entity->setOrigin($clientId); 102 | $entity->setCreated('2014-12-21T20:12:11+00:00Z'); 103 | $entity->setModified('2014-12-21T20:12:11+00:00Z'); 104 | 105 | // Add attributes 106 | $attribute = new Attribute(Attribute::TYPE_STRING); 107 | $attribute->setValue('nothing', 'en'); 108 | $attribute->setValue('nada', 'es'); 109 | $attribute->setValue('nothing'); 110 | $entity->setAttribute('name', $attribute); 111 | 112 | $attribute = new Attribute(Attribute::TYPE_INTEGER); 113 | $attribute->setValue(4); 114 | $entity->setAttribute('age', $attribute); 115 | 116 | // Add references to binary assets, e.g. images. 117 | $attribute = new Attribute(Attribute::TYPE_STRING); 118 | $attribute->setValue('[asset-1]'); 119 | $entity->setAttribute('image', $attribute); 120 | 121 | $asset = new Asset(); 122 | $asset->setUrl('http://placehold.it/100'); 123 | $asset->setReplaceToken('[asset-1]'); 124 | $entity->addAsset($asset); 125 | 126 | // Create an entity container, add the entity to it. 127 | $entities = new Entities(); 128 | $entities->addEntity($entity); 129 | 130 | // Render the entities in Common Data Format (CDF). This should be the payload 131 | // returned by requests to $resourceUrl (defined below). 132 | $cdf = $entities->json(); 133 | 134 | // Queue the entity to be added to Content Hub. An important concept in Content 135 | // Hub is that write operations are asynchronous, meaning that the actions are 136 | // not performed right away. In this example, a URL is passed to Content Hub 137 | // which is expected to render the entities that you want to add to the hub in 138 | // CDF. Content Hub receives the request and immediately returns a 202 which 139 | // signifies that the request was received. In the background, Content Hub then 140 | // makes a request to the URL, reads the CDF, and adds the entities. Success and 141 | // error messages are sent via webhooks, so it is important to implement a 142 | // webhook receiver endpoint so that you know what is going on. 143 | $resourceUrl = 'http://example.com/path/to/cdf'; 144 | $client->createEntities($resourceUrl); 145 | 146 | ``` 147 | 148 | #### Reading entities 149 | 150 | ```php 151 | readEntity($uuid); 155 | 156 | // Get the "name" attribute in English, then Spanish. 157 | $name = $entity->getAttribute('name')->getValue('en'); 158 | $nombre = $entity->getAttribute('name')->getValue('es'); 159 | 160 | // Get the URL of the image attribute by dereferencing the token. 161 | $token = $entity->getAttribute('image')->getValue(); 162 | $url = $entity->getAsset($token)->getUrl(); 163 | 164 | ``` 165 | 166 | #### Updating entities 167 | 168 | ```php 169 | setValue(5); 174 | $entity->setAttribute('age', $attribute); 175 | 176 | // Updating entities is also an asynchronous operation, so it may take a couple 177 | // of seconds for the changes to be reflected in the hub. 178 | $client->updateEntity($resourceUrl, $uuid); 179 | 180 | ``` 181 | 182 | #### Deleting entities 183 | 184 | ```php 185 | deleteEntity($uuid); 190 | 191 | ``` 192 | 193 | #### Running Tests 194 | 195 | To better facilitate running tests, this library is now equipped with a Makefile with the following targets: 196 | (running `make help` also shows this information) 197 | - install: To install the dependencies 198 | - example: make install 199 | - method_test file=path/to/test/file: To run a specific test 200 | - example: make method_test method=testFromJSONStringCreation file=test/CDFObjectTest.php 201 | - file_test file=path/to/test/file: To run a specific test 202 | - example: make file_test file=test/CDFObjectTest.php 203 | - dir_tests dir=path/to/test-directory: To run all the tests inside a directory 204 | - example: make dir_tests test 205 | - all_tests: To run all Unit Tests 206 | - example: make all_tests 207 | - coverage: To create test coverage for the Unit Tests 208 | - example: make coverage 209 | - infection: To run infection on the existing tests 210 | - example: make infection 211 | 212 | -------------------------------------------------------------------------------- /test/CDF/CDFObjectTest.php: -------------------------------------------------------------------------------- 1 | 'some-type', 24 | 'uuid' => 'some-uuid', 25 | 'created' => 'some-creation-date', 26 | 'modified' => 'some-modification-date', 27 | 'origin' => 'some-origin', 28 | 'attributes' => [ 29 | 'name1' => [ 30 | 'type' => 'integer', 31 | 'value' => 123, 32 | ], 33 | 'name2' => [ 34 | 'type' => 'string', 35 | 'value' => 'some-attribute-value', 36 | ], 37 | ], 38 | 'metadata' => [ 39 | 'webhook-1' => 'w1-uuid', 40 | 'webhook-2' => 'w2-uuid', 41 | 'attributes' => [ 42 | 'name2' => [ 43 | 'class' => CDFAttributeChild::class, 44 | ], 45 | ], 46 | ], 47 | ]; 48 | 49 | /** 50 | * CDFObject instance. 51 | * 52 | * @var \Acquia\ContentHubClient\CDF\CDFObject 53 | */ 54 | private $cdfObject; 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | public function setUp(): void { 60 | parent::setUp(); 61 | 62 | $this->cdfObject = new CDFObject( 63 | self::SAMPLE_CDF_OBJECT_PARAMS['type'], 64 | self::SAMPLE_CDF_OBJECT_PARAMS['uuid'], 65 | self::SAMPLE_CDF_OBJECT_PARAMS['created'], 66 | self::SAMPLE_CDF_OBJECT_PARAMS['modified'], 67 | self::SAMPLE_CDF_OBJECT_PARAMS['origin'], 68 | self::SAMPLE_CDF_OBJECT_PARAMS['metadata'] 69 | ); 70 | } 71 | 72 | /** 73 | * {@inheritDoc} 74 | */ 75 | public function tearDown(): void { 76 | parent::tearDown(); 77 | unset($this->cdfObject); 78 | } 79 | 80 | /** 81 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::toArray 82 | */ 83 | public function testToArray(): void { 84 | $this->cdfObject->addAttribute('id', CDFAttribute::TYPE_ARRAY_INTEGER, 1, 85 | CDFObject::LANGUAGE_UNDETERMINED, CDFAttribute::class); 86 | $base_array = self::SAMPLE_CDF_OBJECT_PARAMS; 87 | $base_array['attributes'] = $this->attributesToArray($this->cdfObject->getAttributes()); 88 | 89 | $this->assertEquals($this->cdfObject->toArray(), $base_array); 90 | } 91 | 92 | /** 93 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::getAttributes 94 | */ 95 | public function testFromArrayCreation(): void { 96 | $cdf_object = CDFObject::fromArray(self::SAMPLE_CDF_OBJECT_PARAMS); 97 | 98 | $attributes = $cdf_object->getAttributes(); 99 | 100 | $this->assertInstanceOf(CDFAttribute::class, $attributes['name1']); 101 | $this->assertInstanceOf(CDFAttributeChild::class, $attributes['name2']); 102 | } 103 | 104 | /** 105 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::fromJson 106 | */ 107 | public function testFromJSONStringCreation(): void { // phpcs:ignore 108 | $cdf_object = CDFObject::fromJson(json_encode(self::SAMPLE_CDF_OBJECT_PARAMS)); 109 | 110 | $this->assertEquals( 111 | self::SAMPLE_CDF_OBJECT_PARAMS['attributes'], 112 | $this->attributesToArray($cdf_object->getAttributes()) 113 | ); 114 | } 115 | 116 | /** 117 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::getAttribute 118 | */ 119 | public function testGetAttributeReturnsNullIfNoAttributeIsPresent(): void { 120 | $this->assertNull($this->cdfObject->getAttribute('some-id')); 121 | } 122 | 123 | /** 124 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::getModuleDependencies 125 | */ 126 | public function testGetModuleDependenciesReturnsEmptyArrayWhenEmpty(): void { 127 | $this->assertEquals($this->cdfObject->getModuleDependencies(), []); 128 | } 129 | 130 | /** 131 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::getModuleDependencies 132 | */ 133 | public function testGetModuleDependenciesReturnsNoEmptyArrayWhenNonEmpty(): void { 134 | $some_value = 'some-value'; 135 | $this->cdfObject->setMetadata([ 136 | 'dependencies' => [ 137 | 'module' => $some_value, 138 | ], 139 | ]); 140 | 141 | $this->assertEquals($this->cdfObject->getModuleDependencies(), $some_value); 142 | } 143 | 144 | /** 145 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::getDependencies 146 | */ 147 | public function testGetDependenciesReturnsEmptyArrayWhenEmpty(): void { 148 | $this->assertEquals($this->cdfObject->getDependencies(), []); 149 | } 150 | 151 | /** 152 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::getDependencies 153 | */ 154 | public function testGetDependenciesReturnsNoEmptyArrayWhenNonEmpty(): void { 155 | $some_value = 'some-value'; 156 | $this->cdfObject->setMetadata([ 157 | 'dependencies' => [ 158 | 'entity' => $some_value, 159 | ], 160 | ]); 161 | 162 | $this->assertEquals($this->cdfObject->getDependencies(), $some_value); 163 | } 164 | 165 | /** 166 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::hasProcessedDependencies 167 | */ 168 | public function testProcessedDependencies(): void { 169 | $this->assertFalse($this->cdfObject->hasProcessedDependencies()); 170 | $this->cdfObject->markProcessedDependencies(); 171 | $this->assertTrue($this->cdfObject->hasProcessedDependencies()); 172 | } 173 | 174 | /** 175 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::addAttribute 176 | */ 177 | public function testAddIncorrectAttributeThrowsException(): void { 178 | $this->expectException(\Exception::class); 179 | $this->cdfObject->addAttribute('dummy_attribute_id', CDFAttribute::TYPE_ARRAY_BOOLEAN, [], CDFObject::LANGUAGE_UNDETERMINED, 'DummyClass'); 180 | } 181 | 182 | /** 183 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::addAttribute 184 | * 185 | * @dataProvider attributeDataProvider 186 | * 187 | * @param mixed $value 188 | * Test data. 189 | * 190 | * @throws \Exception 191 | */ 192 | public function testAddAttributeAltersMetadataWithCDFAttributeSubclasses($value): void { // phpcs:ignore 193 | $attribute_id = 'attribute_id_1'; 194 | $cdf_attribute_child_class = CDFAttributeChild::class; 195 | 196 | $this->cdfObject->addAttribute($attribute_id, CDFAttribute::TYPE_ARRAY_INTEGER, $value, CDFObject::LANGUAGE_UNDETERMINED, $cdf_attribute_child_class); 197 | 198 | $this->assertEquals(get_class($this->cdfObject->getAttribute($attribute_id)), $cdf_attribute_child_class); 199 | $this->assertTrue(isset($this->cdfObject->getMetadata()['attributes'][$attribute_id])); 200 | $this->assertEquals($this->cdfObject->getMetadata()['attributes'][$attribute_id]['class'], $cdf_attribute_child_class); 201 | } 202 | 203 | /** 204 | * @covers \Acquia\ContentHubClient\CDF\CDFObject::addAttribute 205 | * 206 | * @dataProvider attributeDataProvider 207 | * 208 | * @param mixed $value 209 | * Test data. 210 | * 211 | * @throws \Exception 212 | */ 213 | public function testAddAttributeUnsetsAttributeFromMetadataWithCDFAttributeClass($value): void { // phpcs:ignore 214 | $attribute_id = 'attribute_id_1'; 215 | $this->cdfObject->addAttribute($attribute_id, CDFAttribute::TYPE_ARRAY_INTEGER, $value, CDFObject::LANGUAGE_UNDETERMINED, CDFAttribute::class); 216 | 217 | $this->assertFalse(isset($this->cdfObject->getMetadata()['attributes'][$attribute_id])); 218 | } 219 | 220 | /** 221 | * Data provider. 222 | * 223 | * @return array 224 | * Test data. 225 | */ 226 | public function attributeDataProvider(): array { 227 | return [ 228 | [ 229 | 'value' => [ 230 | 'en' => [ 231 | 6.66, 232 | 3.23, 233 | ], 234 | 'hu' => [ 235 | 4.66, 236 | 4.23, 237 | ], 238 | CDFObject::LANGUAGE_UNDETERMINED => [ 239 | 1.22, 240 | 1.11, 241 | ], 242 | ], 243 | ], 244 | ]; 245 | } 246 | 247 | /** 248 | * Converts attributes to array. 249 | * 250 | * @param array $attributes 251 | * Attributes array. 252 | * 253 | * @return array 254 | * Converted data. 255 | */ 256 | private function attributesToArray(array $attributes) { 257 | return array_map(static function (CDFAttribute $attribute) { 258 | return $attribute->toArray(); 259 | }, $attributes); 260 | } 261 | 262 | } 263 | -------------------------------------------------------------------------------- /src/ContentHubClientTrait.php: -------------------------------------------------------------------------------- 1 | logger)) { 44 | return; 45 | } 46 | 47 | if (!$config['handler'] instanceof HandlerStack) { 48 | return; 49 | } 50 | 51 | $config['handler']->push(new RequestResponseHandler($this->logger)); 52 | } 53 | 54 | /** 55 | * Removes all leading and trailing slashes. 56 | * 57 | * Strip all leading and trailing slashes from all components of the given 58 | * array. 59 | * 60 | * @param string[] $components 61 | * Array of strings. 62 | * 63 | * @return string[] 64 | * Processed array. 65 | */ 66 | protected static function removeAllLeadingAndTrailingSlashes(array $components): array { 67 | return array_map(function ($component) { 68 | return trim($component, '/'); 69 | }, $components); 70 | } 71 | 72 | /** 73 | * Glue all elements of an array together. 74 | * 75 | * @param array $parts 76 | * Parts array. 77 | * @param string $glue 78 | * Glue symbol. 79 | * 80 | * @return string 81 | * Processed string. 82 | */ 83 | protected static function gluePartsTogether(array $parts, string $glue): string { 84 | return implode($glue, self::removeAllLeadingAndTrailingSlashes($parts)); 85 | } 86 | 87 | /** 88 | * Make a base url out of components and add a trailing slash to it. 89 | * 90 | * @param string[] $base_url_components 91 | * Base URL components. 92 | * 93 | * @return string 94 | * Processed string. 95 | */ 96 | protected static function makeBaseURL(...$base_url_components): string { // phpcs:ignore 97 | return self::makePath(...$base_url_components) . '/'; 98 | } 99 | 100 | /** 101 | * Make path out of its individual components. 102 | * 103 | * @param string[] $path_components 104 | * Path components. 105 | * 106 | * @return string 107 | * Processed string. 108 | */ 109 | protected static function makePath(...$path_components): string { // phpcs:ignore 110 | return self::gluePartsTogether($path_components, '/'); 111 | } 112 | 113 | /** 114 | * Returns error response. 115 | * 116 | * @param int $code 117 | * Status code. 118 | * @param string $reason 119 | * Reason. 120 | * @param string|null $request_id 121 | * The request id from the ContentHub service if available. 122 | * 123 | * @return \Psr\Http\Message\ResponseInterface 124 | * Response. 125 | */ 126 | protected function getErrorResponse(int $code, string $reason, ?string $request_id = NULL): ResponseInterface { 127 | if ($code < 100 || $code >= 600) { 128 | $code = 500; 129 | } 130 | $body = [ 131 | 'request_id' => $request_id, 132 | 'error' => [ 133 | 'code' => $code, 134 | 'message' => $reason, 135 | ], 136 | ]; 137 | return new Response($code, [], json_encode($body), '1.1', $reason); 138 | } 139 | 140 | /** 141 | * Obtains the appropriate exception Response, logging error messages according to API call. 142 | * 143 | * @param string $method 144 | * The Request to Plexus, as defined in the content-hub-php library. 145 | * @param string $api_call 146 | * The api endpoint. 147 | * @param \Exception $exception 148 | * The Exception object. 149 | * 150 | * @return \Psr\Http\Message\ResponseInterface 151 | * The response after raising an exception. 152 | * 153 | * @codeCoverageIgnore 154 | */ 155 | protected function getExceptionResponse(string $method, string $api_call, \Exception $exception): ResponseInterface { 156 | // If we reach here it is because there was an exception raised in the API call. 157 | $response = $exception->getResponse(); 158 | if (!$response) { 159 | $response = $this->getErrorResponse($exception->getCode(), $exception->getMessage()); 160 | } 161 | $response_body = json_decode($response->getBody(), TRUE); 162 | $error_code = $response_body['error']['code'] ?? ''; 163 | $error_message = $response_body['error']['message'] ?? ''; 164 | 165 | // Customize Error messages according to API Call. 166 | switch ($api_call) { 167 | case'settings/webhooks': 168 | $log_level = LogLevel::WARNING; 169 | break; 170 | 171 | case (preg_match('/filters\?name=*/', $api_call) ? TRUE : FALSE): 172 | case (preg_match('/settings\/clients\/*/', $api_call) ? TRUE : FALSE): 173 | case (preg_match('/settings\/webhooks\/.*\/filters/', $api_call) ? TRUE : FALSE): 174 | $log_level = LogLevel::NOTICE; 175 | break; 176 | 177 | default: 178 | // The default log level is ERROR. 179 | $log_level = LogLevel::ERROR; 180 | break; 181 | } 182 | 183 | $reason = sprintf("Request ID: %s, Method: %s, Path: \"%s\", Status Code: %s, Reason: %s, Error Code: %s, Error Message: \"%s\". Error data: \"%s\"", 184 | $response_body['request_id'] ?? '', 185 | strtoupper($method), 186 | $api_call, 187 | $response->getStatusCode(), 188 | $response->getReasonPhrase(), 189 | $error_code, 190 | $error_message, 191 | print_r($response_body['error']['data'] ?? $response_body['error'] ?? '', TRUE) 192 | ); 193 | $this->logger->log($log_level, $reason); 194 | 195 | // Return the response. 196 | return $response; 197 | } 198 | 199 | /** 200 | * Get the settings that were used to instantiate this client. 201 | * 202 | * @return \Acquia\ContentHubClient\Settings 203 | * Settings object. 204 | * 205 | * @codeCoverageIgnore 206 | */ 207 | public function getSettings() { 208 | return $this->settings; 209 | } 210 | 211 | /** 212 | * Gets a Json Response from a request. 213 | * 214 | * @param \Psr\Http\Message\ResponseInterface $response 215 | * Response. 216 | * 217 | * @return mixed 218 | * Response array. 219 | * 220 | * @throws \Exception 221 | */ 222 | public static function getResponseJson(ResponseInterface $response) { 223 | try { 224 | $body = (string) $response->getBody(); 225 | } 226 | catch (\Exception $exception) { 227 | $message = sprintf("An exception occurred in the JSON response. Message: %s", 228 | $exception->getMessage()); 229 | throw new \Exception($message); 230 | } 231 | 232 | return json_decode($body, TRUE); 233 | } 234 | 235 | /** 236 | * Pings the service to ensure that it is available. 237 | * 238 | * @return \Psr\Http\Message\ResponseInterface 239 | * Response. 240 | * 241 | * @throws \GuzzleHttp\Exception\RequestException 242 | * @throws \Exception 243 | * 244 | * @since 0.2.0 245 | */ 246 | public function ping() { 247 | $makeBaseURL = self::makeBaseURL($this->getConfig()['base_url']); 248 | $client = ObjectFactory::getGuzzleClient([ 249 | 'base_uri' => $makeBaseURL, 250 | ]); 251 | 252 | return $client->get('ping'); 253 | } 254 | 255 | /** 256 | * Create and send an HTTP GET request. 257 | * 258 | * @param string $uri 259 | * URI object or string. 260 | * @param array $options 261 | * Request options to apply. 262 | */ 263 | public function get(string $uri, array $options = []): ResponseInterface { 264 | return $this->request('GET', $uri, $options); 265 | } 266 | 267 | /** 268 | * Create and send an HTTP PUT request. 269 | * 270 | * @param string $uri 271 | * URI object or string. 272 | * @param array $options 273 | * Request options to apply. 274 | */ 275 | public function put(string $uri, array $options = []): ResponseInterface { 276 | return $this->request('PUT', $uri, $options); 277 | } 278 | 279 | /** 280 | * Create and send an HTTP POST request. 281 | * 282 | * @param string $uri 283 | * URI object or string. 284 | * @param array $options 285 | * Request options to apply. 286 | */ 287 | public function post(string $uri, array $options = []): ResponseInterface { 288 | return $this->request('POST', $uri, $options); 289 | } 290 | 291 | /** 292 | * Create and send an HTTP DELETE request. 293 | * 294 | * @param string $uri 295 | * URI object or string. 296 | * @param array $options 297 | * Request options to apply. 298 | */ 299 | public function delete(string $uri, array $options = []): ResponseInterface { 300 | return $this->request('DELETE', $uri, $options); 301 | } 302 | 303 | /** 304 | * Sets configurations. 305 | * 306 | * @param array $config 307 | * Array of configurations. 308 | */ 309 | public function setConfigs(array $config): void { 310 | $this->config = $config; 311 | } 312 | 313 | } 314 | -------------------------------------------------------------------------------- /test/ContentHubTestBase.php: -------------------------------------------------------------------------------- 1 | [ 34 | 'uuid' => '00000000-0000-0000-0000-000000000000', 35 | 'origin' => '11111111-0000-0000-0000-000000000000', 36 | 'data' => [ 37 | 'uuid' => '00000000-0000-0000-0000-000000000000', 38 | "type" => "product", 39 | "created" => "2014-12-21T20:12:11+00:00Z", 40 | "modified" => "2014-12-21T20:12:11+00:00Z", 41 | "attributes" => [ 42 | "title" => [ 43 | "type" => "string", 44 | "value" => [ 45 | "en" => "A", 46 | "hu" => "B", 47 | "und" => "C", 48 | ], 49 | ], 50 | ], 51 | "assets" => [ 52 | [ 53 | "url" => "http://acquia.com/sites/default/files/foo.png", 54 | "replace-token" => "[acquia-logo]", 55 | ], 56 | [ 57 | "url" => "http://acquia.com/sites/default/files/bar.png", 58 | "replace-token" => "[acquia-thumb]", 59 | ], 60 | ], 61 | ], 62 | ], 63 | ]; 64 | } 65 | 66 | /** 67 | * Returns test data. 68 | * 69 | * @return array 70 | * Test data. 71 | */ 72 | protected function setDefinition() { 73 | return [ 74 | 'children' => [ 75 | 0 => '/settings', 76 | 1 => '/register', 77 | 2 => '/entities', 78 | 3 => '/ping', 79 | 4 => '/elastic', 80 | ], 81 | ]; 82 | } 83 | 84 | /** 85 | * Returns list of entities. 86 | * 87 | * @return array 88 | * Test data. 89 | */ 90 | protected function setListOfEntities() { 91 | return [ 92 | 'success' => TRUE, 93 | 'total' => 2, 94 | 'data' => [ 95 | 0 => [ 96 | 'uuid' => '00000000-0000-0000-0000-000000000000', 97 | 'origin' => '11111111-1111-1111-1111-111111111111', 98 | 'modified' => '2015-06-29T12:15:36-04:00', 99 | 'type' => 'node', 100 | 'attributes' => [ 101 | 'body' => [ 102 | 'und' => '{"summary":"","value":"Custom table","format":"filtered_html"}', 103 | ], 104 | 'description' => NULL, 105 | 'field_tags' => [ 106 | 'und' => [ 107 | 0 => '88fa41e8-b959-41f1-aaa9-e9017936d8ca', 108 | 1 => '7f0931f6-2d04-4488-9eec-fbd81e604ce5', 109 | ], 110 | ], 111 | 'status' => [ 112 | 'und' => 1, 113 | ], 114 | 'title' => [ 115 | 'und' => 'A new custom table', 116 | ], 117 | ], 118 | ], 119 | 1 => [ 120 | 'uuid' => '00000000-1111-0000-0000-000000000000', 121 | 'origin' => '11111111-1111-1111-1111-111111111111', 122 | 'modified' => '2015-07-01T14:18:29-04:00', 123 | 'type' => 'node', 124 | 'attributes' => [ 125 | 'body' => [ 126 | 'und' => '{"summary":"","value":"The following is a Custom bench for Boston.","format":"filtered_html"}', 127 | ], 128 | 'description' => NULL, 129 | 'field_tags' => [ 130 | 'und' => [ 131 | 0 => '94e38271-df71-4da6-ae3c-dce143244b65', 132 | ], 133 | ], 134 | 'status' => [ 135 | 'und' => 1, 136 | ], 137 | 'title' => [ 138 | 'und' => 'New Custom Bench 2', 139 | ], 140 | ], 141 | ], 142 | ], 143 | ]; 144 | } 145 | 146 | /** 147 | * Returns test data. 148 | * 149 | * @return array 150 | * Test data. 151 | */ 152 | public function setResponseTrue() { 153 | return [ 154 | 'success' => TRUE, 155 | 'request_id' => '00000000-0000-0000-0000-000000000000', 156 | ]; 157 | } 158 | 159 | /** 160 | * Returns log entry. 161 | * 162 | * @return array 163 | * Test data. 164 | */ 165 | public function setHistoryLogs() { 166 | return [ 167 | '_shards' => [ 168 | 'failed' => 0, 169 | 'successful' => 5, 170 | 'total' => 5, 171 | ], 172 | 'hits' => [ 173 | 'hits' => [ 174 | 0 => [ 175 | '_id' => '7257fb6e-2fba-4919-6311-656385295b2f', 176 | '_index' => '00000000-0000-4000-8000-000000000000_history_index', 177 | '_score' => 1, 178 | '_source' => [ 179 | 'client' => '12340000-0000-4000-6012-000000000000', 180 | 'entity' => 'eadc61e3-b847-4310-946a-511cca7bb14b', 181 | 'id' => '7257fb6e-2fba-4919-6311-656385295b2f', 182 | 'request_id' => '71b983d5-169d-48d9-6f62-c474cbdd5454', 183 | 'status' => 'succeeded', 184 | 'subscription' => '00000000-0000-4000-8000-000000000000', 185 | 'timestamp' => '2017-03-09T13:49:23Z', 186 | 'type' => 'create', 187 | ], 188 | '_type' => 'history', 189 | ], 190 | ], 191 | 'total' => 1, 192 | ], 193 | 'timed_out' => FALSE, 194 | 'took' => 63, 195 | ]; 196 | } 197 | 198 | /** 199 | * Returns mapping. 200 | * 201 | * @return array 202 | * Test data. 203 | */ 204 | public function setMapping() { 205 | return [ 206 | 'entity' => [ 207 | 'dynamic' => 'strict', 208 | 'properties' => [ 209 | 'data' => [ 210 | 'dynamic' => 'strict', 211 | 'properties' => [ 212 | 'assets' => [ 213 | 'dynamic' => 'strict', 214 | 'properties' => [ 215 | 'replace-token' => [ 216 | 'type' => 'string', 217 | ], 218 | 'url' => [ 219 | 'type' => 'string', 220 | ], 221 | ], 222 | ], 223 | 'attributes' => [ 224 | 'dynamic' => 'true', 225 | 'properties' => [ 226 | 'title' => [ 227 | 'dynamic' => 'strict', 228 | 'properties' => [ 229 | 'metadata' => [ 230 | 'type' => 'string', 231 | ], 232 | 'type' => [ 233 | 'type' => 'string', 234 | ], 235 | 'value' => [ 236 | 'dynamic' => 'true', 237 | 'properties' => [ 238 | 'und' => [ 239 | 'type' => 'string', 240 | ], 241 | ], 242 | ], 243 | ], 244 | ], 245 | ], 246 | 'type' => 'object', 247 | ], 248 | 'created' => [ 249 | 'type' => 'date', 250 | ], 251 | 'metadata' => [ 252 | 'dynamic' => 'true', 253 | 'index' => 'no', 254 | 'type' => 'object', 255 | ], 256 | 'modified' => [ 257 | 'type' => 'date', 258 | ], 259 | 'origin' => [ 260 | 'type' => 'string', 261 | ], 262 | 'type' => [ 263 | 'type' => 'string', 264 | ], 265 | 'uuid' => [ 266 | 'type' => 'string', 267 | ], 268 | ], 269 | ], 270 | 'id' => [ 271 | 'type' => 'string', 272 | ], 273 | 'origin' => [ 274 | 'index' => 'not_analyzed', 275 | 'type' => 'string', 276 | ], 277 | 'revision' => [ 278 | 'type' => 'long', 279 | ], 280 | 'subscription' => [ 281 | 'type' => 'string', 282 | ], 283 | 'uuid' => [ 284 | 'index' => 'not_analyzed', 285 | 'type' => 'string', 286 | ], 287 | ], 288 | ], 289 | ]; 290 | } 291 | 292 | /** 293 | * @covers \Acquia\ContentHubClient\ContentHubClient::ping 294 | */ 295 | public function testPing() { 296 | // Setup. 297 | $data = [ 298 | 'success' => 1, 299 | ]; 300 | $responses = [ 301 | new Response('200', [], json_encode($data)), 302 | ]; 303 | $client = $this->getClient($responses); 304 | 305 | // Ping the service. 306 | $response = $client->ping(); 307 | $body = (string) $response->getBody(); 308 | $this->assertEquals($data, json_decode($body, TRUE)); 309 | } 310 | 311 | /** 312 | * @covers \Acquia\ContentHubClient\ContentHubClient::definition 313 | */ 314 | public function testDefinition() { 315 | // Setup. 316 | $data = $this->setDefinition(); 317 | $responses = [ 318 | new Response('200', [], json_encode($data)), 319 | ]; 320 | $client = $this->getClient($responses); 321 | 322 | // Get definition. 323 | $response = $client->definition(); 324 | $this->assertEquals($data, $response); 325 | } 326 | 327 | /** 328 | * @covers \Acquia\ContentHubClient\ContentHubClient::getClientByName 329 | */ 330 | public function testClientByName() { 331 | // Setup. 332 | $data = [ 333 | 'name' => 'mysite', 334 | 'uuid' => '00000000-0000-0000-0000-000000000000', 335 | ]; 336 | $responses = [ 337 | new Response('200', [], json_encode($data)), 338 | ]; 339 | $client = $this->getClient($responses); 340 | 341 | // Get client by name. 342 | $response = $client->getClientByName('mysite'); 343 | $this->assertEquals($data, $response); 344 | } 345 | 346 | /** 347 | * @covers \Acquia\ContentHubClient\ContentHubClient::createEntities 348 | */ 349 | public function testCreateEntity() { 350 | // Setup. 351 | $data = [ 352 | 'success' => TRUE, 353 | ]; 354 | $resource = 'http://acquia.com/content_hub_connector/node/00000000-0000-0000-0000-000000000000'; 355 | $responses = [ 356 | new Response(200, [], json_encode($data)), 357 | new Response(200, [], json_encode($data)), 358 | ]; 359 | $client = $this->getClient($responses); 360 | 361 | // Create an Entity. 362 | $response = $client->createEntity($resource); 363 | $body = json_decode((string) $response->getBody(), TRUE); 364 | $this->assertEquals($data, $body); 365 | $this->assertEquals($responses[0], $response); 366 | 367 | // Create one or more entities. 368 | $response = $client->createEntities($resource); 369 | $body = json_decode((string) $response->getBody(), TRUE); 370 | $this->assertEquals($data, $body); 371 | $this->assertEquals($responses[1], $response); 372 | } 373 | 374 | /** 375 | * @covers \Acquia\ContentHubClient\ContentHubClient::getEntity 376 | */ 377 | public function testReadEntity() { 378 | // Setup. 379 | $data = $this->setData(); 380 | $responses = [ 381 | new Response(200, [], json_encode($data)), 382 | ]; 383 | $client = $this->getClient($responses); 384 | 385 | // Read an Entity. 386 | $entity = $client->readEntity('00000000-0000-0000-0000-000000000000'); 387 | $this->assertEquals(6, count($entity)); 388 | $this->assertEquals($data['data']['data'], (array) $entity); 389 | } 390 | 391 | /** 392 | * @covers \Acquia\ContentHubClient\ContentHubClient::get 393 | */ 394 | public function testUpdateEntity() { 395 | // Setup. 396 | $data = [ 397 | 'success' => TRUE, 398 | ]; 399 | $resource = 'http://acquia.com/content_hub_connector/node/00000000-0000-0000-0000-000000000000'; 400 | $responses = [ 401 | new Response(200, [], json_encode($data)), 402 | new Response(200, [], json_encode($data)), 403 | ]; 404 | $client = $this->getClient($responses); 405 | 406 | // Update an Entity. 407 | $response = $client->updateEntity($resource, 408 | '00000000-0000-0000-0000-000000000000'); 409 | $this->assertEquals(json_encode($data), $response->getBody()); 410 | $this->assertEquals($responses[0], $response); 411 | 412 | // Test Update Entities (one or more) 413 | $response = $client->updateEntities($resource); 414 | $this->assertEquals(json_encode($data), $response->getBody()); 415 | $this->assertEquals($responses[1], $response); 416 | } 417 | 418 | /** 419 | * @covers \Acquia\ContentHubClient\ContentHubClient::deleteEntity 420 | */ 421 | public function testDeleteEntity() { 422 | // Setup. 423 | $responses = [ 424 | new Response(200), 425 | ]; 426 | $client = $this->getClient($responses); 427 | 428 | // Delete an Entity. 429 | $response = $client->deleteEntity('00000000-0000-0000-0000-000000000000'); 430 | $this->assertEquals(200, $response->getStatusCode()); 431 | } 432 | 433 | /** 434 | * @covers \Acquia\ContentHubClient\ContentHubClient::listEntities 435 | */ 436 | public function testListEntities() { 437 | // Setup. 438 | $data = $this->setListOfEntities(); 439 | $responses = [ 440 | new Response(200, [], json_encode($data)), 441 | ]; 442 | $client = $this->getClient($responses); 443 | 444 | // Listing entities. 445 | $options = [ 446 | 'limit' => 20, 447 | 'type' => 'node', 448 | 'origin' => '11111111-1111-1111-1111-111111111111', 449 | 'fields' => 'status,title,body,field_tags,description', 450 | 'filters' => [ 451 | 'status' => 1, 452 | 'title' => 'New*', 453 | 'body' => '/Custom/', 454 | ], 455 | ]; 456 | $response = $client->listEntities($options); 457 | $this->assertEquals($data, $response); 458 | } 459 | 460 | /** 461 | * @covers \Acquia\ContentHubClient\ContentHubClient::purge 462 | */ 463 | public function testPurge() { 464 | // Setup. 465 | $data = $this->setResponseTrue(); 466 | $responses = [ 467 | new Response(200, [], json_encode($data)), 468 | ]; 469 | $client = $this->getClient($responses); 470 | 471 | // Purge entities. 472 | $response = $client->purge(); 473 | $this->assertTrue($response['success']); 474 | } 475 | 476 | /** 477 | * @covers \Acquia\ContentHubClient\ContentHubClient::restore 478 | */ 479 | public function testRestore() { 480 | // Setup. 481 | $data = $this->setResponseTrue(); 482 | $responses = [ 483 | new Response(200, [], json_encode($data)), 484 | ]; 485 | $client = $this->getClient($responses); 486 | 487 | // Restore entities. 488 | $response = $client->restore(); 489 | $this->assertTrue($response['success']); 490 | } 491 | 492 | /** 493 | * @covers \Acquia\ContentHubClient\ContentHubClient::reindex 494 | */ 495 | public function testReindex() { 496 | // Setup. 497 | $data = $this->setResponseTrue(); 498 | $responses = [ 499 | new Response(200, [], json_encode($data)), 500 | ]; 501 | $client = $this->getClient($responses); 502 | 503 | // Reindex. 504 | $response = $client->reindex(); 505 | $this->assertTrue($response['success']); 506 | } 507 | 508 | /** 509 | * @covers \Acquia\ContentHubClient\ContentHubClient::logs 510 | */ 511 | public function testHistory() { 512 | // Setup. 513 | $data = $this->setHistoryLogs(); 514 | $responses = [ 515 | new Response(200, [], json_encode($data)), 516 | ]; 517 | $client = $this->getClient($responses); 518 | 519 | // Get History. 520 | $response = $client->logs(''); 521 | $this->assertEquals($data['hits']['total'], $response['hits']['total']); 522 | $this->assertEquals($data['hits']['hits'], $response['hits']['hits']); 523 | } 524 | 525 | /** 526 | * @covers \Acquia\ContentHubClient\ContentHubClient::mapping 527 | */ 528 | public function testMapping() { 529 | // Setup. 530 | $data = $this->setMapping(); 531 | $responses = [ 532 | new Response(200, [], json_encode($data)), 533 | ]; 534 | $client = $this->getClient($responses); 535 | 536 | // Purge entities. 537 | $response = $client->mapping(); 538 | $this->assertEquals($data['entity'], $response['entity']); 539 | } 540 | 541 | } 542 | -------------------------------------------------------------------------------- /test/CDFDocumentTest.php: -------------------------------------------------------------------------------- 1 | cdfDocument = new CDFDocument(); 31 | } 32 | 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | public function tearDown(): void { 37 | parent::tearDown(); 38 | unset($this->cdfDocument); 39 | } 40 | 41 | /** 42 | * @covers \Acquia\ContentHubClient\CDFDocument::hasEntities 43 | * 44 | * @dataProvider providerEntityOperations 45 | * 46 | * @param \Acquia\ContentHubClient\CDF\CDFObject $objectToAdd 47 | * CDF object. 48 | * @param \Acquia\ContentHubClient\CDF\CDFObject $notAddedObject 49 | * CDF object. 50 | */ 51 | public function testHasEntities(CDFObject $objectToAdd, CDFObject $notAddedObject) { 52 | // Check hasEntities method before and after we add an Object. 53 | $this->assertEquals($this->cdfDocument->hasEntities(), FALSE); 54 | $this->cdfDocument->addCdfEntity($objectToAdd); 55 | $this->assertEquals($this->cdfDocument->hasEntities(), TRUE); 56 | } 57 | 58 | /** 59 | * @covers \Acquia\ContentHubClient\CDFDocument::getCdfEntity 60 | * 61 | * @dataProvider providerEntityOperations 62 | * 63 | * @param \Acquia\ContentHubClient\CDF\CDFObject $objectToAdd 64 | * CDF object. 65 | * @param \Acquia\ContentHubClient\CDF\CDFObject $notAddedObject 66 | * CDF object. 67 | */ 68 | public function testGetEntity(CDFObject $objectToAdd, CDFObject $notAddedObject) { 69 | // Check getting added and not added Object. 70 | $this->cdfDocument->addCdfEntity($objectToAdd); 71 | $this->assertEquals($this->cdfDocument->getCdfEntity($objectToAdd->getUuid()), $objectToAdd); 72 | $this->assertEquals($this->cdfDocument->getCdfEntity($notAddedObject->getUuid()), NULL); 73 | } 74 | 75 | /** 76 | * @covers \Acquia\ContentHubClient\CDFDocument::getEntities 77 | * 78 | * @dataProvider providerEntityOperations 79 | * 80 | * @param \Acquia\ContentHubClient\CDF\CDFObject $objectOne 81 | * CDF object. 82 | * @param \Acquia\ContentHubClient\CDF\CDFObject $objectTwo 83 | * CDF object. 84 | */ 85 | public function testGetEntities(CDFObject $objectOne, CDFObject $objectTwo) { 86 | // Check getting added and not added Object. 87 | $this->cdfDocument->setCdfEntities($objectOne, $objectTwo); 88 | 89 | foreach ($this->cdfDocument->getEntities() as $entity) { 90 | $this->assertInstanceOf(CDFObject::class, $entity); 91 | } 92 | } 93 | 94 | /** 95 | * @covers \Acquia\ContentHubClient\CDFDocument::addCdfEntity 96 | * 97 | * @dataProvider providerEntityOperations 98 | * 99 | * @param \Acquia\ContentHubClient\CDF\CDFObject $objectToAdd 100 | * CDF object. 101 | * @param \Acquia\ContentHubClient\CDF\CDFObject $notAddedObject 102 | * CDF object. 103 | */ 104 | public function testAddEntity(CDFObject $objectToAdd, CDFObject $notAddedObject) { 105 | // Check if hasEntity will return correct values for added and not added Objects. 106 | $this->cdfDocument->addCdfEntity($objectToAdd); 107 | $this->assertEquals($this->cdfDocument->hasEntity($objectToAdd->getUuid()), TRUE); 108 | $this->assertEquals($this->cdfDocument->hasEntity($notAddedObject->getUuid()), FALSE); 109 | } 110 | 111 | /** 112 | * @covers \Acquia\ContentHubClient\CDFDocument::removeCdfEntity 113 | * 114 | * @dataProvider providerEntityOperations 115 | * 116 | * @param \Acquia\ContentHubClient\CDF\CDFObject $objectToAdd 117 | * CDF object. 118 | */ 119 | public function testRemoveEntity(CDFObject $objectToAdd) { 120 | // Test removing Entity. 121 | $this->cdfDocument->addCdfEntity($objectToAdd); 122 | $this->assertEquals($this->cdfDocument->hasEntity($objectToAdd->getUuid()), TRUE); 123 | $this->cdfDocument->removeCdfEntity($objectToAdd->getUuid()); 124 | $this->assertEquals($this->cdfDocument->hasEntity($objectToAdd->getUuid()), FALSE); 125 | } 126 | 127 | /** 128 | * @covers \Acquia\ContentHubClient\CDFDocument::mergeDocuments 129 | * 130 | * @dataProvider providerMergeDocuments 131 | * 132 | * @param array $setOne 133 | * Data set. 134 | * @param array $setTwo 135 | * Data set. 136 | */ 137 | public function testMergeDocuments(array $setOne, array $setTwo) { 138 | $this->cdfDocument->setCdfEntities(...$setOne); 139 | $documentToMerge = new CDFDocument(...$setTwo); 140 | 141 | foreach ($setTwo as $entity) { 142 | $this->assertFalse($this->cdfDocument->hasEntity($entity->getUuid())); 143 | } 144 | $this->cdfDocument->mergeDocuments($documentToMerge); 145 | foreach ($setTwo as $entity) { 146 | $this->assertTrue($this->cdfDocument->hasEntity($entity->getUuid())); 147 | } 148 | } 149 | 150 | /** 151 | * @covers \Acquia\ContentHubClient\CDFDocument::mergeDocuments 152 | * 153 | * @dataProvider providerMergeDocuments 154 | * 155 | * @param array $setOne 156 | * Data set. 157 | * @param array $setTwo 158 | * Data set. 159 | */ 160 | public function testMergeDocumentsByKeys(array $setOne, array $setTwo) { 161 | $this->cdfDocument->setCdfEntities(...$setOne); 162 | $documentToMerge = new CDFDocument(...$setTwo); 163 | 164 | $keysOne = array_keys($this->cdfDocument->getEntities()); 165 | $keysTwo = array_keys($documentToMerge->getEntities()); 166 | 167 | $this->assertEquals(array_diff($keysOne, $keysTwo), $keysOne); 168 | 169 | $this->cdfDocument->mergeDocuments($documentToMerge); 170 | $mergedKeys = array_keys($this->cdfDocument->getEntities()); 171 | $this->assertEquals($mergedKeys, array_merge($keysOne, $keysTwo)); 172 | } 173 | 174 | /** 175 | * @covers \Acquia\ContentHubClient\CDFDocument::mergeDocuments 176 | * 177 | * @dataProvider providerMergeDocumentsNoOverlap 178 | * 179 | * @param array $setOne 180 | * Data set. 181 | * @param array $setTwo 182 | * Data set. 183 | * @param \Acquia\ContentHubClient\CDF\CDFObject $elementFromSetTwo 184 | * CDF object. 185 | */ 186 | public function testMergeDocumentsNoOverlap(array $setOne, array $setTwo, CDFObject $elementFromSetTwo) { 187 | $this->cdfDocument->setCdfEntities(...$setOne); 188 | $documentToMerge = new CDFDocument(...$setTwo); 189 | 190 | $this->assertFalse($this->cdfDocument->hasEntity($elementFromSetTwo->getUuid())); 191 | $this->cdfDocument->mergeDocuments($documentToMerge); 192 | $this->assertTrue($this->cdfDocument->hasEntity($elementFromSetTwo->getUuid())); 193 | } 194 | 195 | /** 196 | * @covers \Acquia\ContentHubClient\CDFDocument::mergeDocuments 197 | * 198 | * @dataProvider providerMergeDocumentsOverlap 199 | * 200 | * @param array $setOne 201 | * Data set. 202 | * @param array $setTwo 203 | * Data set. 204 | * @param \Acquia\ContentHubClient\CDF\CDFObject $overlappingElement 205 | * CDF object. 206 | */ 207 | public function testMergeDocumentsOverlap(array $setOne, array $setTwo, CDFObject $overlappingElement) { 208 | $this->cdfDocument->setCdfEntities(...$setOne); 209 | $documentToMerge = new CDFDocument(...$setTwo); 210 | 211 | $this->assertTrue($this->cdfDocument->hasEntity($overlappingElement->getUuid())); 212 | $this->cdfDocument->mergeDocuments($documentToMerge); 213 | $this->assertTrue($this->cdfDocument->hasEntity($overlappingElement->getUuid())); 214 | } 215 | 216 | /** 217 | * @covers \Acquia\ContentHubClient\CDFDocument::toString 218 | * 219 | * @dataProvider providerToString 220 | * 221 | * @param array $objectsList 222 | * Objects list. 223 | * @param string $emptyObjectsJson 224 | * JSON string. 225 | * @param string $filledObjectsJson 226 | * JSON string. 227 | */ 228 | public function testToString(array $objectsList, $emptyObjectsJson, $filledObjectsJson) { 229 | $this->assertJsonStringEqualsJsonString($this->cdfDocument->toString(), $emptyObjectsJson); 230 | $this->cdfDocument->setCdfEntities(...$objectsList); 231 | $this->assertJsonStringEqualsJsonString($this->cdfDocument->toString(), $filledObjectsJson); 232 | } 233 | 234 | /** 235 | * Data provider for ::testHasEntities. 236 | * 237 | * @return array 238 | * Test data. 239 | */ 240 | public function providerEntityOperations() { 241 | $cdfObjectMockFirst = $this->getMockBuilder(CDFObject::class) 242 | ->disableOriginalConstructor() 243 | ->onlyMethods(['getUuid']) 244 | ->getMock(); 245 | $cdfObjectMockSecond = $this->getMockBuilder(CDFObject::class) 246 | ->disableOriginalConstructor() 247 | ->onlyMethods(['getUuid']) 248 | ->getMock(); 249 | 250 | $cdfObjectMockFirst->expects($this->any()) 251 | ->method('getUuid') 252 | ->will($this->returnValue('11111111-0000-0000-0000-000000000000')); 253 | 254 | $cdfObjectMockSecond->expects($this->any()) 255 | ->method('getUuid') 256 | ->will($this->returnValue('22222222-0000-0000-0000-000000000000')); 257 | 258 | return [ 259 | [ 260 | $cdfObjectMockFirst, 261 | $cdfObjectMockSecond, 262 | ], 263 | ]; 264 | } 265 | 266 | /** 267 | * Data provider for ::testMergeDocuments. 268 | * 269 | * @return array 270 | * Test data. 271 | */ 272 | public function providerMergeDocuments() { 273 | $cdfObjectMockFirst = $this->getMockBuilder(CDFObject::class) 274 | ->disableOriginalConstructor() 275 | ->onlyMethods(['getUuid']) 276 | ->getMock(); 277 | $cdfObjectMockSecond = $this->getMockBuilder(CDFObject::class) 278 | ->disableOriginalConstructor() 279 | ->onlyMethods(['getUuid']) 280 | ->getMock(); 281 | 282 | $cdfObjectMockFirst->expects($this->any()) 283 | ->method('getUuid') 284 | ->willReturn('33333333-0000-0000-0000-000000000000'); 285 | 286 | $cdfObjectMockSecond->expects($this->any()) 287 | ->method('getUuid') 288 | ->willReturn('44444444-0000-0000-0000-000000000000'); 289 | 290 | return [ 291 | array_merge($this->providerEntityOperations(), [ 292 | [ 293 | $cdfObjectMockFirst, 294 | $cdfObjectMockSecond, 295 | ], 296 | ]), 297 | ]; 298 | } 299 | 300 | /** 301 | * Data provider for ::testMergeDocumentsNoOverlap. 302 | * 303 | * @return array 304 | * Test data. 305 | */ 306 | public function providerMergeDocumentsNoOverlap() { 307 | // First set of objects. 308 | $setOneFirst = $this->getMockBuilder(CDFObject::class) 309 | ->disableOriginalConstructor() 310 | ->onlyMethods(['getUuid']) 311 | ->getMock(); 312 | $setOneSecond = $this->getMockBuilder(CDFObject::class) 313 | ->disableOriginalConstructor() 314 | ->onlyMethods(['getUuid']) 315 | ->getMock(); 316 | 317 | $setOneFirst->expects($this->any()) 318 | ->method('getUuid') 319 | ->willReturn('11111111-0000-0000-0000-000000000000'); 320 | 321 | $setOneSecond->expects($this->any()) 322 | ->method('getUuid') 323 | ->willReturn('22222222-0000-0000-0000-000000000000'); 324 | 325 | // Second set of objects. 326 | $setTwoFirst = $this->getMockBuilder(CDFObject::class) 327 | ->disableOriginalConstructor() 328 | ->onlyMethods(['getUuid']) 329 | ->getMock(); 330 | $setTwoSecond = $this->getMockBuilder(CDFObject::class) 331 | ->disableOriginalConstructor() 332 | ->onlyMethods(['getUuid']) 333 | ->getMock(); 334 | 335 | $setTwoFirst->expects($this->any()) 336 | ->method('getUuid') 337 | ->willReturn('33333333-0000-0000-0000-000000000000'); 338 | 339 | $setTwoSecond->expects($this->any()) 340 | ->method('getUuid') 341 | ->willReturn('44444444-0000-0000-0000-000000000000'); 342 | 343 | return [ 344 | [ 345 | [ 346 | $setOneFirst, 347 | $setOneSecond, 348 | ], 349 | [ 350 | $setTwoFirst, 351 | $setTwoSecond, 352 | ], 353 | $setTwoFirst, 354 | ], 355 | [ 356 | [ 357 | $setOneFirst, 358 | $setOneSecond, 359 | ], 360 | [ 361 | $setTwoFirst, 362 | $setTwoSecond, 363 | ], 364 | $setTwoSecond, 365 | ], 366 | ]; 367 | } 368 | 369 | /** 370 | * Data provider for ::testMergeDocumentsOverlap. 371 | * 372 | * @return array 373 | * Test data. 374 | */ 375 | public function providerMergeDocumentsOverlap() { 376 | // First set of objects. 377 | $setOneFirst = $this->getMockBuilder(CDFObject::class) 378 | ->disableOriginalConstructor() 379 | ->onlyMethods(['getUuid']) 380 | ->getMock(); 381 | $setOneSecond = $this->getMockBuilder(CDFObject::class) 382 | ->disableOriginalConstructor() 383 | ->onlyMethods(['getUuid']) 384 | ->getMock(); 385 | 386 | $setOneFirst->expects($this->any()) 387 | ->method('getUuid') 388 | ->willReturn('11111111-0000-0000-0000-000000000000'); 389 | 390 | $setOneSecond->expects($this->any()) 391 | ->method('getUuid') 392 | ->willReturn('22222222-0000-0000-0000-000000000000'); 393 | 394 | // Second set of objects. 395 | $setTwoFirst = $this->getMockBuilder(CDFObject::class) 396 | ->disableOriginalConstructor() 397 | ->onlyMethods(['getUuid']) 398 | ->getMock(); 399 | $setTwoSecond = $this->getMockBuilder(CDFObject::class) 400 | ->disableOriginalConstructor() 401 | ->onlyMethods(['getUuid']) 402 | ->getMock(); 403 | 404 | $setTwoFirst->expects($this->any()) 405 | ->method('getUuid') 406 | ->willReturn('11111111-0000-0000-0000-000000000000'); 407 | 408 | $setTwoSecond->expects($this->any()) 409 | ->method('getUuid') 410 | ->willReturn('22222222-0000-0000-0000-000000000000'); 411 | 412 | return [ 413 | [ 414 | [ 415 | $setOneFirst, 416 | $setOneSecond, 417 | ], 418 | [ 419 | $setTwoFirst, 420 | $setTwoSecond, 421 | ], 422 | $setTwoFirst, 423 | ], 424 | [ 425 | [ 426 | $setOneFirst, 427 | $setOneSecond, 428 | ], 429 | [ 430 | $setTwoFirst, 431 | $setTwoSecond, 432 | ], 433 | $setTwoSecond, 434 | ], 435 | ]; 436 | } 437 | 438 | /** 439 | * Data provider for ::testToString. 440 | * 441 | * @return array 442 | * Test data. 443 | */ 444 | public function providerToString() { 445 | $cdfObjectMockFirst = $this->getMockBuilder(CDFObject::class) 446 | ->disableOriginalConstructor() 447 | ->onlyMethods(['getUuid', 'toArray']) 448 | ->getMock(); 449 | $cdfObjectMockSecond = $this->getMockBuilder(CDFObject::class) 450 | ->disableOriginalConstructor() 451 | ->onlyMethods(['getUuid', 'toArray']) 452 | ->getMock(); 453 | 454 | $cdfObjectMockFirst->expects($this->any()) 455 | ->method('getUuid') 456 | ->will($this->returnValue('55555555-0000-0000-0000-000000000000')); 457 | 458 | $cdfArrayMock = [ 459 | 'uuid' => '00000000-0000-0000-0000-000000000000', 460 | 'type' => 'product', 461 | 'created' => '2014-12-21T20:12:11+00:00Z', 462 | 'modified' => '2014-12-21T20:12:11+00:00Z', 463 | 'origin' => '00000000-0000-0000-0000-000000000000', 464 | ]; 465 | $cdfObjectMockFirstToArray = $cdfArrayMock; 466 | $cdfObjectMockSecondToArray = $cdfArrayMock; 467 | 468 | $cdfObjectMockFirstToArray['uuid'] = '55555555-0000-0000-0000-000000000000'; 469 | $cdfObjectMockFirstToArray['origin'] = '11111111-0000-0000-0000-000000000000'; 470 | 471 | $cdfObjectMockFirstToArray['uuid'] = '66666666-0000-0000-0000-000000000000'; 472 | $cdfObjectMockFirstToArray['origin'] = '22222222-0000-0000-0000-000000000000'; 473 | 474 | $cdfObjectMockFirst->expects($this->any()) 475 | ->method('toArray') 476 | ->willReturn($cdfObjectMockFirstToArray); 477 | 478 | $cdfObjectMockSecond->expects($this->any()) 479 | ->method('getUuid') 480 | ->willReturn('66666666-0000-0000-0000-000000000000'); 481 | 482 | $cdfObjectMockSecond->expects($this->any()) 483 | ->method('toArray') 484 | ->willReturn($cdfObjectMockSecondToArray); 485 | 486 | return [ 487 | [ 488 | [ 489 | $cdfObjectMockFirst, 490 | $cdfObjectMockSecond, 491 | ], 492 | json_encode(['entities' => []]), 493 | json_encode([ 494 | 'entities' => [ 495 | $cdfObjectMockFirstToArray, 496 | $cdfObjectMockSecondToArray, 497 | ], 498 | ]), 499 | ], 500 | ]; 501 | } 502 | 503 | } 504 | -------------------------------------------------------------------------------- /src/ContentHubClient.php: -------------------------------------------------------------------------------- 1 | FALSE, 39 | 'error' => [ 40 | 'code' => HttpResponse::HTTP_GONE, 41 | 'message' => 'This feature is deprecated', 42 | ], 43 | ]; 44 | 45 | /** 46 | * The settings. 47 | * 48 | * @var \Acquia\ContentHubClient\Settings 49 | */ 50 | protected $settings; 51 | 52 | /** 53 | * The logger responsible for tracking request failures. 54 | * 55 | * @var \Psr\Log\LoggerInterface 56 | */ 57 | protected $logger; 58 | 59 | /** 60 | * The Event Dispatcher. 61 | * 62 | * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface 63 | */ 64 | protected $dispatcher; 65 | 66 | /** 67 | * Cached remote settings. 68 | * 69 | * @var array 70 | */ 71 | protected $remoteSettings = []; 72 | 73 | /** 74 | * Whether to return cached remote settings. 75 | * 76 | * @var bool 77 | * True if it should return cached. 78 | */ 79 | protected $shouldReturnCachedRemoteSettings = FALSE; 80 | 81 | // phpcs:disable 82 | /** 83 | * {@inheritdoc} 84 | * 85 | * @codeCoverageIgnore 86 | */ 87 | public function __construct( 88 | LoggerInterface $logger, 89 | Settings $settings, 90 | HmacAuthMiddleware $middleware, 91 | EventDispatcherInterface $dispatcher, 92 | array $config = [], 93 | $api_version = 'v2' 94 | ) { 95 | $this->logger = $logger; 96 | $this->settings = $settings; 97 | $this->dispatcher = $dispatcher; 98 | 99 | // "base_url" parameter changed to "base_uri" in Guzzle6, so the following line 100 | // is there to make sure it does not disrupt previous configuration. 101 | if (!isset($config['base_uri']) && isset($config['base_url'])) { 102 | $config['base_uri'] = self::makeBaseURL($config['base_url'], 103 | $api_version); 104 | } 105 | else { 106 | $config['base_uri'] = self::makeBaseURL($config['base_uri'], 107 | $api_version); 108 | } 109 | 110 | // Setting up the User Header string. 111 | $user_agent_string = self::LIBRARYNAME . '/' . self::LIB_VERSION . ' ' . default_user_agent(); 112 | if (isset($config['client-user-agent'])) { 113 | $user_agent_string = $config['client-user-agent'] . ' ' . $user_agent_string; 114 | } 115 | 116 | // Setting up the headers. 117 | $config['headers']['Content-Type'] = 'application/json'; 118 | $config['headers']['X-Acquia-Plexus-Client-Id'] = $settings->getUuid(); 119 | $config['headers']['User-Agent'] = $user_agent_string; 120 | 121 | // Add the authentication handler. 122 | // @see https://github.com/acquia/http-hmac-spec 123 | if (!isset($config['handler'])) { 124 | $config['handler'] = ObjectFactory::getHandlerStack(); 125 | } 126 | $config['handler']->push($middleware); 127 | $this->addRequestResponseHandler($config); 128 | 129 | $this->httpClient = ObjectFactory::getGuzzleClient($config); 130 | $this->setConfigs($config); 131 | } 132 | // phpcs:enable 133 | 134 | /** 135 | * Discoverability of the API. 136 | * 137 | * @param string $endpoint 138 | * Endpoint URI. 139 | * 140 | * @return array 141 | * Response. 142 | * 143 | * @throws \Exception 144 | * 145 | * @codeCoverageIgnore 146 | */ 147 | public function definition($endpoint = '') { 148 | return self::getResponseJson($this->request('options', $endpoint)); 149 | } 150 | 151 | /** 152 | * Registers a new client for the active subscription. 153 | * 154 | * This method also returns the UUID for the new client being registered. 155 | * 156 | * @param \Psr\Log\LoggerInterface $logger 157 | * Logger. 158 | * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher 159 | * Event dispatcher. 160 | * @param string $name 161 | * The human-readable name for the client. 162 | * @param string $url 163 | * URL. 164 | * @param string $api_key 165 | * API key. 166 | * @param string $secret 167 | * API secret. 168 | * @param string $api_version 169 | * API version. 170 | * 171 | * @return \Acquia\ContentHubClient\ContentHubClient 172 | * ContentHubClient instance. 173 | * 174 | * @throws \Exception 175 | */ 176 | public static function register( 177 | LoggerInterface $logger, 178 | EventDispatcherInterface $dispatcher, 179 | $name, 180 | $url, 181 | $api_key, 182 | $secret, 183 | $api_version = 'v2' 184 | ) { 185 | $config = [ 186 | 'base_uri' => self::makeBaseURL($url, $api_version), 187 | 'headers' => [ 188 | 'Content-Type' => 'application/json', 189 | 'User-Agent' => self::LIBRARYNAME . '/' . self::LIB_VERSION . ' ' . default_user_agent(), 190 | ], 191 | 'handler' => ObjectFactory::getHandlerStack(), 192 | ]; 193 | 194 | // Add the authentication handler. 195 | // @see https://github.com/acquia/http-hmac-spec 196 | $key = ObjectFactory::getAuthenticationKey($api_key, $secret); 197 | $middleware = ObjectFactory::getHmacAuthMiddleware($key); 198 | $config['handler']->push($middleware); 199 | $client = ObjectFactory::getGuzzleClient($config); 200 | $options['body'] = json_encode(['name' => $name]); 201 | try { 202 | $response = $client->post('register', $options); 203 | $values = self::getResponseJson($response); 204 | $settings = ObjectFactory::instantiateSettings($values['name'], 205 | $values['uuid'], $api_key, $secret, $url); 206 | $config = [ 207 | 'base_url' => $settings->getUrl(), 208 | ]; 209 | $client = ObjectFactory::getCHClient($config, $logger, $settings, 210 | $settings->getMiddleware(), $dispatcher); 211 | // @todo remove this once shared secret is returned on the register 212 | // endpoint. 213 | // We need the shared secret to be fully functional, so an additional 214 | // request is required to get that. 215 | $remote = $client->getRemoteSettings(); 216 | // Now that we have the shared secret, reinstantiate everything and 217 | // return a new instance of this class. 218 | $settings = ObjectFactory::instantiateSettings($settings->getName(), 219 | $settings->getUuid(), $settings->getApiKey(), $settings->getSecretKey(), 220 | $settings->getUrl(), $remote['shared_secret']); 221 | return ObjectFactory::getCHClient($config, $logger, $settings, 222 | $settings->getMiddleware(), $dispatcher); 223 | } 224 | catch (\Exception $exception) { 225 | if ($exception instanceof BadResponseException) { 226 | $message = sprintf('Error registering client with name="%s" (Error Code = %d: %s)', 227 | $name, $exception->getResponse()->getStatusCode(), 228 | $exception->getResponse()->getReasonPhrase()); 229 | $logger->error($message); 230 | throw new RequestException($message, $exception->getRequest(), 231 | $exception->getResponse()); 232 | } 233 | if ($exception instanceof RequestException) { 234 | $message = sprintf('Could not get authorization from Content Hub to register client %s. Are your credentials inserted correctly? (Error message = %s)', 235 | $name, $exception->getMessage()); 236 | $logger->error($message); 237 | throw new RequestException($message, $exception->getRequest(), 238 | $exception->getResponse()); 239 | } 240 | $message = sprintf("An unknown exception was caught. Message: %s", 241 | $exception->getMessage()); 242 | $logger->error($message); 243 | throw new \Exception($message); 244 | } 245 | } 246 | 247 | /** 248 | * Checks Plexus to see if the client name is already in use. 249 | * 250 | * @param string $name 251 | * Name. 252 | * @param string $url 253 | * URL. 254 | * @param string $api_key 255 | * API key. 256 | * @param string $secret 257 | * API secret. 258 | * @param string $api_version 259 | * API version. 260 | * 261 | * @return bool 262 | * Whether the clientName from the request matches the name passed to it. 263 | */ 264 | public static function clientNameExists( 265 | $name, 266 | $url, 267 | $api_key, 268 | $secret, 269 | $api_version = 'v2' 270 | ) { 271 | $config = [ 272 | 'base_uri' => self::makeBaseURL($url, $api_version), 273 | 'headers' => [ 274 | 'Content-Type' => 'application/json', 275 | 'User-Agent' => self::LIBRARYNAME . '/' . self::LIB_VERSION . ' ' . default_user_agent(), 276 | ], 277 | 'handler' => ObjectFactory::getHandlerStack(), 278 | ]; 279 | 280 | // Add the authentication handler. 281 | // @see https://github.com/acquia/http-hmac-spec 282 | $key = ObjectFactory::getAuthenticationKey($api_key, $secret); 283 | $middleware = ObjectFactory::getHmacAuthMiddleware($key); 284 | $config['handler']->push($middleware); 285 | $client = ObjectFactory::getGuzzleClient($config); 286 | $options['body'] = json_encode(['name' => $name]); 287 | // Attempt to fetch the client name, if it works. 288 | try { 289 | $client->get("settings/clients/$name"); 290 | 291 | return TRUE; 292 | } 293 | catch (ClientException $error) { 294 | return $error->getResponse()->getStatusCode() !== HttpResponse::HTTP_NOT_FOUND; 295 | } 296 | } 297 | 298 | /** 299 | * Sends request to asynchronously create entities. 300 | * 301 | * phpcs:ignore @param \Acquia\ContentHubClient\CDF\CDFObject ...$objects 302 | * Individual CDFObjects to send to ContentHub. 303 | * 304 | * @return \Psr\Http\Message\ResponseInterface 305 | * Response. 306 | */ 307 | public function createEntities(CDFObject ...$objects) { 308 | $json = [ 309 | 'resource' => "", 310 | ]; 311 | foreach ($objects as $object) { 312 | $json['entities'][] = $object->toArray(); 313 | } 314 | $options['body'] = json_encode($json); 315 | return $this->post('entities', $options); 316 | } 317 | 318 | /** 319 | * Returns an entity by UUID. 320 | * 321 | * @param string $uuid 322 | * UUID. 323 | * 324 | * @return \Acquia\ContentHubClient\CDF\CDFObjectInterface|array 325 | * A CDFObject representing the entity or an array if there was no data. 326 | * 327 | * @throws \GuzzleHttp\Exception\RequestException 328 | * @throws \Exception 329 | * 330 | * @todo can we return a CDFObject here? 331 | */ 332 | public function getEntity($uuid) { 333 | $return = self::getResponseJson($this->get("entities/$uuid")); 334 | if (!empty($return['data']['data'])) { 335 | return $this->getCDFObject($return['data']['data']); 336 | } 337 | 338 | return $return; 339 | } 340 | 341 | /** 342 | * Searches for entities. 343 | * 344 | * @param array $uuids 345 | * An array of UUIDs. 346 | * 347 | * @return \Acquia\ContentHubClient\CDFDocument 348 | * CDFDocument instance. 349 | * 350 | * @throws \GuzzleHttp\Exception\RequestException 351 | * @throws \Exception 352 | */ 353 | public function getEntities(array $uuids) { 354 | $chunks = array_chunk($uuids, 50); 355 | $objects = []; 356 | foreach ($chunks as $chunk) { 357 | $query = [ 358 | 'size' => 50, 359 | 'query' => [ 360 | 'constant_score' => [ 361 | 'filter' => [ 362 | 'terms' => [ 363 | 'uuid' => $chunk, 364 | ], 365 | ], 366 | ], 367 | ], 368 | ]; 369 | $options['body'] = json_encode($query); 370 | $results = self::getResponseJson($this->get('_search', $options)); 371 | if (isset($results['hits']['total'])) { 372 | foreach ($results['hits']['hits'] as $key => $item) { 373 | $objects[] = $this->getCDFObject($item['_source']['data']); 374 | } 375 | } 376 | } 377 | 378 | return ObjectFactory::getCDFDocument(...$objects); 379 | } 380 | 381 | /** 382 | * Retrieves a CDF Object. 383 | * 384 | * @param mixed $data 385 | * Data. 386 | * 387 | * @return \Acquia\ContentHubClient\CDF\CDFObjectInterface 388 | * CDFObject 389 | */ 390 | public function getCDFObject($data) { // phpcs:ignore 391 | $event = ObjectFactory::getCDFTypeEvent($data); 392 | $this->dispatcher->dispatch($event, ContentHubLibraryEvents::GET_CDF_CLASS); 393 | 394 | return $event->getObject(); 395 | } 396 | 397 | /** 398 | * Updates many entities asynchronously. 399 | * 400 | * phpcs:ignore @param \Acquia\ContentHubClient\CDF\CDFObject ...$objects 401 | * The CDFObjects to update. 402 | * 403 | * @return \Psr\Http\Message\ResponseInterface 404 | * Response. 405 | */ 406 | public function putEntities(CDFObject ...$objects) { 407 | $json = [ 408 | 'resource' => '', 409 | ]; 410 | 411 | foreach ($objects as $object) { 412 | $json['data']['entities'][] = $object->toArray(); 413 | } 414 | 415 | $options['body'] = json_encode($json); 416 | 417 | return $this->put('entities', $options); 418 | } 419 | 420 | /** 421 | * Post entities. 422 | * 423 | * phpcs:ignore @param \Acquia\ContentHubClient\CDF\CDFObject ...$objects 424 | * 425 | * @return \Psr\Http\Message\ResponseInterface 426 | * Response. 427 | */ 428 | public function postEntities(CDFObject ...$objects) { 429 | $json = [ 430 | 'resource' => "", 431 | ]; 432 | foreach ($objects as $object) { 433 | $json['data']['entities'][] = $object->toArray(); 434 | } 435 | $options['body'] = json_encode($json); 436 | return $this->post('entities', $options); 437 | } 438 | 439 | /** 440 | * Deletes an entity by UUID. 441 | * 442 | * @param string $uuid 443 | * Entity UUID. 444 | * 445 | * @return \Psr\Http\Message\ResponseInterface 446 | * Response. 447 | * 448 | * @throws \GuzzleHttp\Exception\RequestException 449 | */ 450 | public function deleteEntity($uuid) { 451 | return $this->delete("entities/$uuid"); 452 | } 453 | 454 | /** 455 | * Deletes an entity from a webhook's interest list. 456 | * 457 | * @param string $uuid 458 | * Interest UUID. 459 | * @param string $webhook_uuid 460 | * Webhook UUID. 461 | * 462 | * @return \Psr\Http\Message\ResponseInterface 463 | * Response. 464 | */ 465 | public function deleteInterest($uuid, $webhook_uuid) { 466 | return $this->delete("interest/$uuid/$webhook_uuid"); 467 | } 468 | 469 | /** 470 | * Purges all entities from the Content Hub. 471 | * 472 | * This method should be used carefully as it deletes all the entities for 473 | * the current subscription from the Content Hub. This creates a backup that 474 | * can be restored at any time. Any subsequent purges overwrite the existing 475 | * backup. Be VERY careful when using this endpoint. 476 | * 477 | * @return mixed 478 | * Response. 479 | * 480 | * @throws \Exception 481 | */ 482 | public function purge() { 483 | return self::getResponseJson($this->post('entities/purge')); 484 | } 485 | 486 | /** 487 | * Restores the state of entities before the previous purge. 488 | * 489 | * Only to be used if a purge has been called previously. This means new 490 | * entities added after the purge was enacted will be overwritten by the 491 | * previous state. Be VERY careful when using this endpoint. 492 | * 493 | * @return mixed 494 | * Response. 495 | * 496 | * @throws \Exception 497 | */ 498 | public function restore() { 499 | return self::getResponseJson($this->post('entities/restore')); 500 | } 501 | 502 | /** 503 | * Reindex a subscription. 504 | * 505 | * Schedules a reindex process. 506 | * 507 | * @return mixed 508 | * Response. 509 | * 510 | * @throws \GuzzleHttp\Exception\RequestException 511 | * @throws \Exception 512 | */ 513 | public function reindex() { 514 | return self::getResponseJson($this->post('reindex')); 515 | } 516 | 517 | /** 518 | * Obtains Customer-Facing-Logs for the subscription. 519 | * 520 | * This is forward search request to Elastic Search. 521 | * 522 | * @param string $query 523 | * An elastic search query. 524 | * @param array $query_options 525 | * An array with the number of items to show in the list and offset. 526 | * 527 | * @return mixed 528 | * Response. 529 | * 530 | * @throws \Exception 531 | * @throws \GuzzleHttp\Exception\RequestException 532 | */ 533 | public function logs($query = '', array $query_options = []) { 534 | return new Response( 535 | self::FEATURE_DEPRECATED_RESPONSE['error']['code'], 536 | [], 537 | json_encode(self::FEATURE_DEPRECATED_RESPONSE), 538 | '1.1', 539 | self::FEATURE_DEPRECATED_RESPONSE['error']['message'] 540 | ); 541 | } 542 | 543 | /** 544 | * Retrieves active ElasticSearch mapping of entities. 545 | * 546 | * @return mixed 547 | * Response. 548 | * 549 | * @throws \Exception 550 | * @throws \GuzzleHttp\Exception\RequestException 551 | */ 552 | public function mapping() { 553 | return self::getResponseJson($this->get('_mapping')); 554 | } 555 | 556 | /** 557 | * Lists Entities from the Content Hub. 558 | * 559 | * Example of how to structure the $options parameter: 560 | * 561 | * $options = [ 562 | * 'limit' => 20, 563 | * 'type' => 'node', 564 | * 'origin' => '11111111-1111-1111-1111-111111111111', 565 | * 'fields' => 'status,title,body,field_tags,description', 566 | * 'filters' => [ 567 | * 'status' => 1, 568 | * 'title' => 'New*', 569 | * 'body' => '/Boston/', 570 | * ], 571 | * ]; 572 | * 573 | * 574 | * @param array $options 575 | * Query options. 576 | * 577 | * @return mixed 578 | * Response. 579 | * 580 | * @throws \GuzzleHttp\Exception\RequestException 581 | * @throws \Exception 582 | */ 583 | public function listEntities(array $options = []) { 584 | $variables = $options + [ 585 | 'limit' => 1000, 586 | 'start' => 0, 587 | 'filters' => [], 588 | ]; 589 | 590 | foreach ($variables['filters'] as $key => $value) { 591 | $variables["filter:${key}"] = $value; 592 | } 593 | unset($variables['filters']); 594 | 595 | // Now make the request. 596 | return self::getResponseJson($this->get('entities?' . http_build_query($variables))); 597 | } 598 | 599 | /** 600 | * Searches for entities. 601 | * 602 | * @param mixed $query 603 | * Search query. 604 | * 605 | * @return mixed 606 | * Response. 607 | * 608 | * @throws \Exception 609 | * @throws \GuzzleHttp\Exception\RequestException 610 | */ 611 | public function searchEntity($query) { 612 | $options['body'] = json_encode((array) $query); 613 | return self::getResponseJson($this->get('_search', $options)); 614 | } 615 | 616 | /** 617 | * Returns the Client, given the site name. 618 | * 619 | * @param string $name 620 | * Client name. 621 | * 622 | * @return mixed 623 | * Response. 624 | * 625 | * @throws \Exception 626 | */ 627 | public function getClientByName($name) { 628 | return self::getResponseJson($this->get("settings/client/name/$name")); 629 | } 630 | 631 | /** 632 | * Returns the Client, given its uuid. 633 | * 634 | * @param string $uuid 635 | * Client uuid. 636 | * 637 | * @return array 638 | * The client array (uuid, name). 639 | * 640 | * @throws \Exception 641 | */ 642 | public function getClientByUuid(string $uuid): array { 643 | $settings = $this->getRemoteSettings(); 644 | foreach ($settings['clients'] as $client) { 645 | if ($client['uuid'] === $uuid) { 646 | return $client; 647 | } 648 | } 649 | return []; 650 | } 651 | 652 | /** 653 | * Returns clients. 654 | * 655 | * @return mixed 656 | * Clients list. 657 | * 658 | * @throws \Exception 659 | */ 660 | public function getClients() { 661 | $data = $this->getRemoteSettings(); 662 | 663 | return $data['clients'] ?? []; 664 | } 665 | 666 | /** 667 | * Returns webhooks list. 668 | * 669 | * @return \Acquia\ContentHubClient\Webhook[] 670 | * Webhooks list. 671 | * 672 | * @throws \Exception 673 | */ 674 | public function getWebHooks() { 675 | $data = $this->getRemoteSettings(); 676 | $webhooks = $data['webhooks'] ?? []; 677 | array_walk($webhooks, function (&$webhook) { 678 | $webhook = ObjectFactory::getWebhook($webhook); 679 | }); 680 | return $webhooks; 681 | } 682 | 683 | /** 684 | * Returns webhook by URL. 685 | * 686 | * @param string $url 687 | * URL. 688 | * 689 | * @return array 690 | * Webhook. 691 | * 692 | * @throws \Exception 693 | * 694 | * @codeCoverageIgnore 695 | */ 696 | public function getWebHook($url) { 697 | return current(array_filter($this->getWebHooks(), 698 | function (Webhook $webhook) use ($url) { 699 | return $webhook->getUrl() === $url; 700 | })) ?? []; 701 | } 702 | 703 | /** 704 | * Returns status information for all webhooks. 705 | * 706 | * @return array 707 | * Webhooks status information. 708 | * 709 | * @throws \Exception 710 | */ 711 | public function getWebhookStatus() { 712 | return self::getResponseJson($this->get('settings/webhooks/status')); 713 | } 714 | 715 | /** 716 | * Returns interests list. 717 | * 718 | * @param string $webhook_uuid 719 | * Webhook UUID. 720 | * 721 | * @return array 722 | * Interests list. 723 | * 724 | * @throws \Exception 725 | */ 726 | public function getInterestsByWebhook($webhook_uuid) { 727 | $data = self::getResponseJson($this->get("interest/webhook/$webhook_uuid")); 728 | 729 | return $data['data']['interests'] ?? []; 730 | } 731 | 732 | /** 733 | * Obtains the Settings for the active subscription. 734 | * 735 | * @return array 736 | * Response. 737 | * 738 | * @throws \Exception 739 | * 740 | * @codeCoverageIgnore 741 | */ 742 | public function getRemoteSettings(): array { 743 | if ($this->shouldReturnCachedRemoteSettings && !empty($this->remoteSettings)) { 744 | return $this->remoteSettings; 745 | } 746 | $this->remoteSettings = self::getResponseJson($this->get('settings')); 747 | return !is_array($this->remoteSettings) ? [] : $this->remoteSettings; 748 | } 749 | 750 | /** 751 | * Sets cachable remote settings. 752 | * 753 | * @param bool $should_cache 754 | * If set to true, returns cached remote settings. 755 | */ 756 | public function cacheRemoteSettings(bool $should_cache): void { 757 | $this->shouldReturnCachedRemoteSettings = $should_cache; 758 | } 759 | 760 | /** 761 | * Adds a webhook to the active subscription. 762 | * 763 | * @param string $webhook_url 764 | * Webhook URL. 765 | * 766 | * @return mixed 767 | * Response. 768 | * 769 | * @throws \Exception 770 | */ 771 | public function addWebhook($webhook_url) { 772 | $options['body'] = json_encode(['url' => $webhook_url, 'version' => 2.0]); 773 | 774 | return self::getResponseJson($this->post('settings/webhooks', $options)); 775 | } 776 | 777 | /** 778 | * Deletes a webhook from the active subscription. 779 | * 780 | * @param string $uuid 781 | * The UUID of the webhook to delete. 782 | * 783 | * @return \Psr\Http\Message\ResponseInterface 784 | * Response. 785 | * 786 | * @throws \GuzzleHttp\Exception\RequestException 787 | */ 788 | public function deleteWebhook($uuid) { 789 | return $this->delete("settings/webhooks/$uuid"); 790 | } 791 | 792 | /** 793 | * Updates a webhook from the active subscription. 794 | * 795 | * @param string $uuid 796 | * The UUID of the webhook to update. 797 | * @param array $options 798 | * What to change in the webhook: url, version, disable_retries, etc. 799 | * 800 | * @return \Psr\Http\Message\ResponseInterface 801 | * Response. 802 | * 803 | * @throws \GuzzleHttp\Exception\RequestException 804 | */ 805 | public function updateWebhook($uuid, array $options) { 806 | if (isset($options['version']) && !in_array($options['version'], [1, 2], 807 | TRUE)) { 808 | $options['version'] = 2; 809 | } 810 | $acceptable_keys = [ 811 | 'version', 812 | 'url', 813 | 'disable_retries', 814 | 'status', 815 | ]; 816 | $values = []; 817 | foreach ($acceptable_keys as $key) { 818 | if (isset($options[$key])) { 819 | $values[$key] = $options[$key]; 820 | } 821 | } 822 | $data['body'] = json_encode($values); 823 | return $this->put("settings/webhooks/$uuid", $data); 824 | } 825 | 826 | /** 827 | * Suppress webhook. 828 | * 829 | * @param string $webhook_uuid 830 | * Webhook uuid. 831 | * 832 | * @return mixed 833 | * Response body of backend call. 834 | */ 835 | public function suppressWebhook(string $webhook_uuid) { 836 | return self::getResponseJson($this->put("webhook/$webhook_uuid/suppress")); 837 | } 838 | 839 | /** 840 | * Remove suppression from webhook. 841 | * 842 | * @param string $webhook_uuid 843 | * Webhook uuid. 844 | * 845 | * @return mixed 846 | * Response body of backend call. 847 | */ 848 | public function unSuppressWebhook(string $webhook_uuid) { 849 | return self::getResponseJson($this->put("settings/webhooks/$webhook_uuid/enable")); 850 | } 851 | 852 | /** 853 | * Add entities to Interest List. 854 | * 855 | * @param string $webhook_uuid 856 | * The UUID of the webhook. 857 | * @param array $uuids 858 | * Entity UUIDs to add to Interest List. 859 | * 860 | * @return \Psr\Http\Message\ResponseInterface 861 | * The response. 862 | * 863 | * @throws \GuzzleHttp\Exception\RequestException 864 | */ 865 | public function addEntitiesToInterestList($webhook_uuid, array $uuids) { 866 | $options['body'] = json_encode(['interests' => $uuids]); 867 | 868 | return $this->post("interest/webhook/$webhook_uuid", $options); 869 | } 870 | 871 | /** 872 | * Deletes a client from the active subscription. 873 | * 874 | * @param string $client_uuid 875 | * The UUID of the client to delete, blank for current client. 876 | * 877 | * @return \Psr\Http\Message\ResponseInterface 878 | * Response. 879 | * 880 | * @throws \Exception 881 | */ 882 | public function deleteClient($client_uuid = NULL) { 883 | $settings = $this->getSettings(); 884 | $uuid = $client_uuid ?? $settings->getUuid(); 885 | $response = $this->deleteEntity($uuid); 886 | if (!$response || $response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { 887 | throw new \Exception(sprintf("Entity with UUID = %s cannot be deleted.", $uuid)); 888 | } 889 | return $this->delete("settings/client/uuid/$uuid"); 890 | } 891 | 892 | /** 893 | * Updates a client from the active subscription. 894 | * 895 | * @param string $uuid 896 | * The UUID of the client to update. 897 | * @param string $name 898 | * The new name for the client we're updating. 899 | * 900 | * @return \Psr\Http\Message\ResponseInterface 901 | * Response. 902 | * 903 | * @throws \GuzzleHttp\Exception\RequestException 904 | */ 905 | public function updateClient($uuid, $name) { 906 | $options['body'] = json_encode(['name' => $name]); 907 | return $this->put("settings/client/uuid/$uuid", $options); 908 | } 909 | 910 | /** 911 | * Regenerates a Shared Secret for the Subscription. 912 | * 913 | * @return array 914 | * Response. 915 | * 916 | * @throws \GuzzleHttp\Exception\RequestException 917 | * @throws \Exception 918 | */ 919 | public function regenerateSharedSecret() { 920 | return self::getResponseJson($this->post('settings/secret', ['body' => json_encode([])])); 921 | } 922 | 923 | /** 924 | * Gets filter by UUID. 925 | * 926 | * @param string $filter_id 927 | * The filter UUID. 928 | * 929 | * @return array 930 | * Response 931 | * 932 | * @throws \GuzzleHttp\Exception\RequestException 933 | * @throws \Exception 934 | */ 935 | public function getFilter($filter_id) { 936 | return $this::getResponseJson($this->get("filters/$filter_id")); 937 | } 938 | 939 | /** 940 | * Gets filter by Name. 941 | * 942 | * @param string $filter_name 943 | * The filter name. 944 | * 945 | * @return array 946 | * The filter array. 947 | * 948 | * @throws \GuzzleHttp\Exception\RequestException 949 | * @throws \Exception 950 | */ 951 | public function getFilterByName($filter_name) { 952 | $result = $this::getResponseJson($this->get("filters?name={$filter_name}")); 953 | if ($result['success'] == 1) { 954 | return $result['data']; 955 | } 956 | 957 | return NULL; 958 | } 959 | 960 | /** 961 | * List all filters in the subscription. 962 | * 963 | * @return array 964 | * An array of all filters in the subscription. 965 | * 966 | * @throws \GuzzleHttp\Exception\RequestException 967 | * @throws \Exception 968 | */ 969 | public function listFilters() { 970 | return $this::getResponseJson($this->get('filters')); 971 | } 972 | 973 | /** 974 | * Puts a Filter into Content Hub. 975 | * 976 | * @param string|array $query 977 | * The query to add to the filter. 978 | * @param string $name 979 | * The name of the filter. 980 | * @param string $uuid 981 | * The filter UUID to update existing filter, NULL to create a new one. 982 | * @param array $metadata 983 | * The Metadata array, empty if not given. 984 | * 985 | * @return array 986 | * An array of data including the filter UUID, if succeeds. 987 | * 988 | * @throws \GuzzleHttp\Exception\RequestException 989 | * @throws \Exception 990 | */ 991 | public function putFilter($query, $name, $uuid = NULL, array $metadata = []) { 992 | $data = [ 993 | 'name' => $name, 994 | 'data' => [ 995 | 'query' => $query, 996 | ], 997 | 'metadata' => (object) $metadata, 998 | ]; 999 | if (!empty($uuid)) { 1000 | $data['uuid'] = $uuid; 1001 | } 1002 | $options = ['body' => json_encode($data)]; 1003 | 1004 | return self::getResponseJson($this->put('filters', $options)); 1005 | } 1006 | 1007 | /** 1008 | * Deletes a filter, given its UUID. 1009 | * 1010 | * @param string $filter_uuid 1011 | * The filter UUID. 1012 | * 1013 | * @return \Psr\Http\Message\ResponseInterface 1014 | * The response. 1015 | * 1016 | * @throws \GuzzleHttp\Exception\RequestException 1017 | */ 1018 | public function deleteFilter($filter_uuid) { 1019 | return $this->delete("filters/{$filter_uuid}"); 1020 | } 1021 | 1022 | /** 1023 | * List all filters attached to a particular webhook. 1024 | * 1025 | * @param string $webhook_id 1026 | * The webhook UUID. 1027 | * 1028 | * @return array 1029 | * An array of data including the filter UUID, if succeeds. 1030 | * 1031 | * @throws \GuzzleHttp\Exception\RequestException 1032 | * @throws \Exception 1033 | */ 1034 | public function listFiltersForWebhook($webhook_id) { 1035 | return $this::getResponseJson($this->get("settings/webhooks/$webhook_id/filters")); 1036 | } 1037 | 1038 | /** 1039 | * Attaches a filter to a webhook. 1040 | * 1041 | * @param string $filter_id 1042 | * The filter UUID. 1043 | * @param string $webhook_id 1044 | * The Webhook UUID. 1045 | * 1046 | * @return array 1047 | * An array of data including the filter UUID, if succeeds. 1048 | * 1049 | * @throws \GuzzleHttp\Exception\RequestException 1050 | * @throws \Exception 1051 | */ 1052 | public function addFilterToWebhook($filter_id, $webhook_id) { 1053 | $data = ['filter_id' => $filter_id]; 1054 | $options = ['body' => json_encode($data)]; 1055 | 1056 | return self::getResponseJson($this->post("settings/webhooks/$webhook_id/filters", $options)); 1057 | } 1058 | 1059 | /** 1060 | * Detaches filter from webhook. 1061 | * 1062 | * @param string $filter_id 1063 | * Filter UUID. 1064 | * @param string $webhook_id 1065 | * Webhook UUID. 1066 | * 1067 | * @return mixed 1068 | * Response. 1069 | * 1070 | * @throws \Exception 1071 | */ 1072 | public function removeFilterFromWebhook($filter_id, $webhook_id) { 1073 | $options = ['body' => json_encode(['filter_id' => $filter_id])]; 1074 | $response = $this->delete("settings/webhooks/$webhook_id/filters", $options); 1075 | 1076 | return self::getResponseJson($response); 1077 | } 1078 | 1079 | /** 1080 | * Appends search criteria header. 1081 | * 1082 | * @param array $args 1083 | * Method arguments. 1084 | * 1085 | * @return array 1086 | * Processed arguments. 1087 | */ 1088 | protected function addSearchCriteriaHeader(array $args) { 1089 | $result = explode('?', $args[0] ?? ''); 1090 | if (count($result) < 2) { 1091 | return $args; 1092 | } 1093 | [, $queryString] = $result; 1094 | if (empty($queryString)) { 1095 | return $args; 1096 | } 1097 | parse_str($queryString, $parsedQueryString); 1098 | 1099 | $languages = $this->getConfig(self::OPTION_NAME_LANGUAGES); 1100 | if (!empty($languages) && is_array($languages)) { 1101 | $parsedQueryString['languages'] = $languages; 1102 | } 1103 | 1104 | /** @var \Acquia\ContentHubClient\SearchCriteria\SearchCriteria $criteria */ 1105 | $criteria = SearchCriteriaBuilder::createFromArray($parsedQueryString); 1106 | $args[1]['headers'] = $args[1]['headers'] ?? []; 1107 | $args[1]['headers'][SearchCriteria::HEADER_NAME] = base64_encode(json_encode($criteria)); 1108 | 1109 | return $args; 1110 | } 1111 | 1112 | /** 1113 | * Fetch snapshots. 1114 | * 1115 | * @return mixed 1116 | * Response. 1117 | * 1118 | * @throws \Exception 1119 | */ 1120 | public function getSnapshots() { 1121 | return self::getResponseJson($this->get('snapshots')); 1122 | } 1123 | 1124 | /** 1125 | * Create a snapshot. 1126 | * 1127 | * @return mixed 1128 | * Response. 1129 | * 1130 | * @throws \Exception 1131 | */ 1132 | public function createSnapshot() { 1133 | return self::getResponseJson($this->post('snapshots')); 1134 | } 1135 | 1136 | /** 1137 | * Deletes a snapshot. 1138 | * 1139 | * @param string $name 1140 | * The name of the snapshot. 1141 | * 1142 | * @return mixed 1143 | * Response from backend call. 1144 | * 1145 | * @throws \GuzzleHttp\Exception\RequestException 1146 | */ 1147 | public function deleteSnapshot($name) { 1148 | return self::getResponseJson($this->delete("snapshots/$name")); 1149 | } 1150 | 1151 | /** 1152 | * Restore a snapshot. 1153 | * 1154 | * @param string $name 1155 | * The name of the snapshot. 1156 | * 1157 | * @return mixed 1158 | * Response from backend call. 1159 | * 1160 | * @throws \GuzzleHttp\Exception\RequestException 1161 | */ 1162 | public function restoreSnapshot(string $name) { 1163 | return self::getResponseJson($this->put("snapshots/$name/restore")); 1164 | } 1165 | 1166 | /** 1167 | * Initiates Scroll API request chain. 1168 | * 1169 | * @param string $scroll_time_window 1170 | * How long the scroll cursor will be retained inside memory. Must be 1171 | * suffixed with duration unit (m, s, ms etc.). 1172 | * @param int $size 1173 | * Amount of entities to return. 1174 | * @param array $query 1175 | * Search query. 1176 | * 1177 | * @return array 1178 | * Response from scroll API. 1179 | * 1180 | * @throws \Exception 1181 | */ 1182 | public function startScroll(string $scroll_time_window = '30m', int $size = 100, array $query = []): array { 1183 | $options['body'] = json_encode($query); 1184 | $options['query'] = [ 1185 | 'scroll' => $scroll_time_window, 1186 | 'size' => $size, 1187 | ]; 1188 | return self::getResponseJson($this->post('scroll', $options)); 1189 | } 1190 | 1191 | /** 1192 | * Initiates Scroll API request chain. 1193 | * 1194 | * @param string $filter_uuid 1195 | * Filter uuid to execute by. 1196 | * @param string|int $scroll_time_window 1197 | * How long the scroll cursor will be retained inside memory. Must be 1198 | * suffixed with duration unit (m, s, ms etc.). 1199 | * @param int $size 1200 | * Amount of entities to return. 1201 | * 1202 | * @return array 1203 | * Response from backend call. 1204 | * 1205 | * @throws \Exception 1206 | */ 1207 | public function startScrollByFilter(string $filter_uuid, $scroll_time_window, int $size): array { 1208 | return self::getResponseJson($this->post("filters/$filter_uuid/scroll", [ 1209 | 'query' => [ 1210 | 'scroll' => $scroll_time_window, 1211 | 'size' => $size, 1212 | ], 1213 | ])); 1214 | } 1215 | 1216 | /** 1217 | * Continue Scroll API request chain. 1218 | * 1219 | * Notice: scroll id is changing continuously once you make a call. 1220 | * 1221 | * @param string $scroll_id 1222 | * Scroll id. 1223 | * @param string|int $scroll_time_window 1224 | * How long the scroll cursor will be retained inside memory. 1225 | * 1226 | * @return array 1227 | * Response from backend call. 1228 | * 1229 | * @throws \Exception 1230 | */ 1231 | public function continueScroll(string $scroll_id, $scroll_time_window): array { 1232 | $options = [ 1233 | 'body' => json_encode([ 1234 | 'scroll_id' => $scroll_id, 1235 | 'scroll' => $scroll_time_window, 1236 | ]), 1237 | ]; 1238 | 1239 | return self::getResponseJson($this->post('scroll/continue', $options)); 1240 | } 1241 | 1242 | /** 1243 | * Cancel Scroll API request chain. 1244 | * 1245 | * @param string $scroll_id 1246 | * Scroll id. 1247 | * 1248 | * @return array|null 1249 | * Response from backend call. 1250 | * 1251 | * @throws \Exception 1252 | */ 1253 | public function cancelScroll(string $scroll_id): ?array { 1254 | $options = [ 1255 | 'body' => json_encode([ 1256 | 'scroll_id' => [$scroll_id], 1257 | ]), 1258 | ]; 1259 | 1260 | return self::getResponseJson($this->delete("scroll", $options)); 1261 | } 1262 | 1263 | /** 1264 | * Checks whether the given account is featured. 1265 | * 1266 | * @return bool 1267 | * True if the account is featured. 1268 | * 1269 | * @throws \Exception 1270 | */ 1271 | public function isFeatured(): bool { 1272 | $remote = $this->getRemoteSettings(); 1273 | return $remote['featured'] ?? FALSE; 1274 | } 1275 | 1276 | } 1277 | --------------------------------------------------------------------------------