diff --git a/src/CrowdinApiClient/Api/ApplicationApi.php b/src/CrowdinApiClient/Api/ApplicationApi.php index 0afdca8c..470ee7bd 100644 --- a/src/CrowdinApiClient/Api/ApplicationApi.php +++ b/src/CrowdinApiClient/Api/ApplicationApi.php @@ -5,6 +5,8 @@ namespace CrowdinApiClient\Api; use CrowdinApiClient\Model\ApplicationData; +use CrowdinApiClient\Model\ApplicationInstallation; +use CrowdinApiClient\ModelCollection; /** * Use API to manage custom application data. @@ -76,6 +78,81 @@ public function deleteApplicationData(string $applicationIdentifier, string $pat return $this->_delete($url); } + /** + * List Application Installations + * @link https://developer.crowdin.com/api/v2/#operation/api.applications.installations.getMany API Documentation + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.applications.installations.getMany API Documentation Enterprise + * + * @param array $params + * integer $params[limit]
+ * integer $params[offset]
+ * integer $params[installedBy]
+ * string $params[orderBy] + * @return ModelCollection + */ + public function listInstallations(array $params = []): ModelCollection + { + return $this->_list('applications/installations', ApplicationInstallation::class, $params); + } + + /** + * Install Application + * @link https://developer.crowdin.com/api/v2/#operation/api.applications.installations.post API Documentation + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.applications.installations.post API Documentation Enterprise + * + * @param array $data + * string $data[url] required
+ * array $data[permissions]
+ * array $data[modules]
+ * bool $data[assignAgent] + * @return ApplicationInstallation|null + */ + public function installApplication(array $data): ?ApplicationInstallation + { + return $this->_create('applications/installations', ApplicationInstallation::class, $data); + } + + /** + * Get Application Installation + * @link https://developer.crowdin.com/api/v2/#operation/api.applications.installations.get API Documentation + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.applications.installations.get API Documentation Enterprise + * + * @param string $identifier + * @return ApplicationInstallation|null + */ + public function getInstallation(string $identifier): ?ApplicationInstallation + { + return $this->_get('applications/installations/' . $identifier, ApplicationInstallation::class); + } + + /** + * Delete Application Installation + * @link https://developer.crowdin.com/api/v2/#operation/api.applications.installations.delete API Documentation + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.applications.installations.delete API Documentation Enterprise + * + * @param string $identifier + * @param bool $force + * @return mixed + */ + public function deleteInstallation(string $identifier, bool $force = false) + { + $params = $force ? ['force' => true] : []; + return $this->_delete('applications/installations/' . $identifier, $params); + } + + /** + * Edit Application Installation + * @link https://developer.crowdin.com/api/v2/#operation/api.applications.installations.patch API Documentation + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.applications.installations.patch API Documentation Enterprise + * + * @param ApplicationInstallation $installation + * @return ApplicationInstallation|null + */ + public function updateInstallation(ApplicationInstallation $installation): ?ApplicationInstallation + { + return $this->_update('applications/installations/' . $installation->getIdentifier(), $installation); + } + /** * Edit Application Data * @link https://developer.crowdin.com/api/v2/#operation/api.applications.api.patch API Documentation @@ -83,7 +160,7 @@ public function deleteApplicationData(string $applicationIdentifier, string $pat * * @param string $applicationIdentifier * @param string $path - * @param array $data + * @param array $data Application-specific key-value payload (free-form, not RFC 6902 patch operations) * @return ApplicationData|null */ public function editApplicationData(string $applicationIdentifier, string $path, array $data): ?ApplicationData diff --git a/src/CrowdinApiClient/Model/ApplicationInstallation.php b/src/CrowdinApiClient/Model/ApplicationInstallation.php new file mode 100644 index 00000000..d091dc9a --- /dev/null +++ b/src/CrowdinApiClient/Model/ApplicationInstallation.php @@ -0,0 +1,165 @@ +identifier = (string)$this->getDataProperty('identifier'); + $this->name = (string)$this->getDataProperty('name'); + $this->installedBy = $this->getDataProperty('installedBy'); + $this->description = $this->getDataProperty('description'); + $this->logo = (string)$this->getDataProperty('logo'); + $this->agent = $this->getDataProperty('agent'); + $this->baseUrl = (string)$this->getDataProperty('baseUrl'); + $this->manifestUrl = (string)$this->getDataProperty('manifestUrl'); + $this->createdAt = $this->getDataProperty('createdAt'); + $this->modules = (array)$this->getDataProperty('modules'); + $this->scopes = (array)$this->getDataProperty('scopes'); + $this->permissions = (array)$this->getDataProperty('permissions'); + $this->defaultPermissions = (array)$this->getDataProperty('defaultPermissions'); + $this->limitReached = (bool)$this->getDataProperty('limitReached'); + $this->stringBasedAvailable = (bool)$this->getDataProperty('stringBasedAvailable'); + } + + public function getIdentifier(): string + { + return $this->identifier; + } + + public function getName(): string + { + return $this->name; + } + + /** + * @return array|null + */ + public function getInstalledBy(): ?array + { + return $this->installedBy; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function getLogo(): string + { + return $this->logo; + } + + /** + * @return array|null + */ + public function getAgent(): ?array + { + return $this->agent; + } + + public function getBaseUrl(): string + { + return $this->baseUrl; + } + + public function getManifestUrl(): string + { + return $this->manifestUrl; + } + + public function getCreatedAt(): ?string + { + return $this->createdAt; + } + + public function getModules(): array + { + return $this->modules; + } + + public function getScopes(): array + { + return $this->scopes; + } + + public function getPermissions(): array + { + return $this->permissions; + } + + public function getDefaultPermissions(): array + { + return $this->defaultPermissions; + } + + public function isLimitReached(): bool + { + return $this->limitReached; + } + + public function isStringBasedAvailable(): bool + { + return $this->stringBasedAvailable; + } + + public function setPermissions(array $permissions): void + { + $this->permissions = $permissions; + } + + public function setModules(array $modules): void + { + $this->modules = $modules; + } +} diff --git a/tests/CrowdinApiClient/Api/ApplicationApiTest.php b/tests/CrowdinApiClient/Api/ApplicationApiTest.php index 68ad2c16..a2cceef0 100644 --- a/tests/CrowdinApiClient/Api/ApplicationApiTest.php +++ b/tests/CrowdinApiClient/Api/ApplicationApiTest.php @@ -5,6 +5,8 @@ namespace CrowdinApiClient\Tests\Api; use CrowdinApiClient\Model\ApplicationData; +use CrowdinApiClient\Model\ApplicationInstallation; +use CrowdinApiClient\ModelCollection; class ApplicationApiTest extends AbstractTestApi { @@ -26,6 +28,109 @@ protected function getResponseJson(): string ]); } + protected function getInstallationData(): array + { + return [ + 'identifier' => 'my-app', + 'name' => 'My Application', + 'installedBy' => ['id' => 1, 'username' => 'admin'], + 'description' => 'App description', + 'logo' => '/resources/images/logo.png', + 'agent' => null, + 'baseUrl' => 'https://localhost.dev', + 'manifestUrl' => 'https://localhost.dev/manifest.json', + 'createdAt' => '2023-11-29T10:00:00+00:00', + 'modules' => [], + 'scopes' => ['application'], + 'permissions' => ['user' => ['value' => 'all']], + 'defaultPermissions' => [], + 'limitReached' => false, + 'stringBasedAvailable' => false, + ]; + } + + protected function getInstallationResponseJson(): string + { + return json_encode(['data' => $this->getInstallationData()]); + } + + public function testListInstallations(): void + { + $this->mockRequest([ + 'path' => '/applications/installations', + 'method' => 'get', + 'response' => json_encode([ + 'data' => [ + ['data' => $this->getInstallationData()], + ], + 'pagination' => [['offset' => 0, 'limit' => 25]], + ]), + ]); + + $list = $this->crowdin->application->listInstallations(); + + $this->assertInstanceOf(ModelCollection::class, $list); + $this->assertCount(1, $list); + $this->assertInstanceOf(ApplicationInstallation::class, $list[0]); + $this->assertEquals('my-app', $list[0]->getIdentifier()); + $this->assertEquals('My Application', $list[0]->getName()); + } + + public function testInstallApplication(): void + { + $this->mockRequest([ + 'path' => '/applications/installations', + 'method' => 'post', + 'body' => json_encode(['url' => 'https://localhost.dev/manifest.json']), + 'response' => $this->getInstallationResponseJson(), + ]); + + $installation = $this->crowdin->application->installApplication([ + 'url' => 'https://localhost.dev/manifest.json', + ]); + + $this->assertInstanceOf(ApplicationInstallation::class, $installation); + $this->assertEquals('my-app', $installation->getIdentifier()); + $this->assertEquals('My Application', $installation->getName()); + } + + public function testGetInstallation(): void + { + $this->mockRequestGet( + '/applications/installations/my-app', + $this->getInstallationResponseJson() + ); + + $installation = $this->crowdin->application->getInstallation('my-app'); + + $this->assertInstanceOf(ApplicationInstallation::class, $installation); + $this->assertEquals('my-app', $installation->getIdentifier()); + $this->assertEquals('My Application', $installation->getName()); + } + + public function testDeleteInstallation(): void + { + $this->mockRequestDelete('/applications/installations/my-app'); + + $this->crowdin->application->deleteInstallation('my-app'); + } + + public function testUpdateInstallation(): void + { + $this->mockRequestPatch( + '/applications/installations/my-app', + $this->getInstallationResponseJson() + ); + + $installation = new ApplicationInstallation($this->getInstallationData()); + $installation->setPermissions(['user' => ['value' => 'managers']]); + + $result = $this->crowdin->application->updateInstallation($installation); + + $this->assertInstanceOf(ApplicationInstallation::class, $result); + $this->assertEquals('my-app', $result->getIdentifier()); + } + public function testGetApplicationData(): void { $this->mockRequestGet( diff --git a/tests/CrowdinApiClient/Model/ApplicationInstallationTest.php b/tests/CrowdinApiClient/Model/ApplicationInstallationTest.php new file mode 100644 index 00000000..30651d07 --- /dev/null +++ b/tests/CrowdinApiClient/Model/ApplicationInstallationTest.php @@ -0,0 +1,126 @@ +model = new ApplicationInstallation([ + 'identifier' => 'my-app', + 'name' => 'My Application', + 'installedBy' => ['id' => 1, 'username' => 'admin'], + 'description' => 'App description', + 'logo' => '/resources/images/logo.png', + 'agent' => null, + 'baseUrl' => 'https://localhost.dev', + 'manifestUrl' => 'https://localhost.dev/manifest.json', + 'createdAt' => '2023-11-29T10:00:00+00:00', + 'modules' => [['key' => 'module-1', 'type' => 'custom']], + 'scopes' => ['application'], + 'permissions' => ['user' => ['value' => 'all'], 'project' => ['value' => 'own']], + 'defaultPermissions' => ['user' => ['value' => 'all']], + 'limitReached' => false, + 'stringBasedAvailable' => true, + ]); + } + + public function testGetIdentifier(): void + { + $this->assertEquals('my-app', $this->model->getIdentifier()); + } + + public function testGetName(): void + { + $this->assertEquals('My Application', $this->model->getName()); + } + + public function testGetInstalledBy(): void + { + $this->assertEquals(['id' => 1, 'username' => 'admin'], $this->model->getInstalledBy()); + } + + public function testGetDescription(): void + { + $this->assertEquals('App description', $this->model->getDescription()); + } + + public function testGetLogo(): void + { + $this->assertEquals('/resources/images/logo.png', $this->model->getLogo()); + } + + public function testGetAgent(): void + { + $this->assertNull($this->model->getAgent()); + } + + public function testGetBaseUrl(): void + { + $this->assertEquals('https://localhost.dev', $this->model->getBaseUrl()); + } + + public function testGetManifestUrl(): void + { + $this->assertEquals('https://localhost.dev/manifest.json', $this->model->getManifestUrl()); + } + + public function testGetCreatedAt(): void + { + $this->assertEquals('2023-11-29T10:00:00+00:00', $this->model->getCreatedAt()); + } + + public function testGetModules(): void + { + $this->assertEquals([['key' => 'module-1', 'type' => 'custom']], $this->model->getModules()); + } + + public function testGetScopes(): void + { + $this->assertEquals(['application'], $this->model->getScopes()); + } + + public function testGetPermissions(): void + { + $this->assertEquals( + ['user' => ['value' => 'all'], 'project' => ['value' => 'own']], + $this->model->getPermissions() + ); + } + + public function testGetDefaultPermissions(): void + { + $this->assertEquals(['user' => ['value' => 'all']], $this->model->getDefaultPermissions()); + } + + public function testIsLimitReached(): void + { + $this->assertFalse($this->model->isLimitReached()); + } + + public function testIsStringBasedAvailable(): void + { + $this->assertTrue($this->model->isStringBasedAvailable()); + } + + public function testSetPermissions(): void + { + $newPermissions = ['user' => ['value' => 'managers']]; + $this->model->setPermissions($newPermissions); + $this->assertEquals($newPermissions, $this->model->getPermissions()); + } + + public function testSetModules(): void + { + $newModules = [['key' => 'module-2', 'type' => 'custom']]; + $this->model->setModules($newModules); + $this->assertEquals($newModules, $this->model->getModules()); + } +}