diff --git a/crowdin_api/api_resources/application/enums.py b/crowdin_api/api_resources/application/enums.py index 249ac97..649e03e 100644 --- a/crowdin_api/api_resources/application/enums.py +++ b/crowdin_api/api_resources/application/enums.py @@ -12,3 +12,18 @@ class UserPermissions(Enum): class ProjectPermissions(Enum): OWN = "own" RESTRICTED = "restricted" + + +class ListApplicationConsentsOrderBy(Enum): + ID = "id" + CREATED_AT = "createdAt" + + +class ApplicationConsentStatus(Enum): + GRANTED = "granted" + DENIED = "denied" + + +class ApplicationConsentPatchPath(Enum): + STATUS = "/status" + SCOPES = "/scopes" diff --git a/crowdin_api/api_resources/application/resource.py b/crowdin_api/api_resources/application/resource.py index f61bd49..ef4a962 100644 --- a/crowdin_api/api_resources/application/resource.py +++ b/crowdin_api/api_resources/application/resource.py @@ -1,10 +1,14 @@ -from typing import Optional, Iterable +from typing import Iterable, Optional + from crowdin_api.parser import dumps from crowdin_api.api_resources.application.types import ( + AddApplicationConsentRequest, + ApplicationConsentPatchRequest, ApplicationPermissions, ApplicationInstallationPatchRequest, ) from crowdin_api.api_resources.abstract.resources import BaseResource +from crowdin_api.sorting import Sorting class ApplicationResource(BaseResource): @@ -102,6 +106,86 @@ def edit_applicatoin_installation( request_data=data, ) + def get_application_consents_path(self, consent_id: Optional[int] = None): + if consent_id is not None: + return f"applications/consents/{consent_id}" + return "applications/consents" + + def list_application_consents( + self, + identifier: Optional[str] = None, + order_by: Optional[Sorting] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + ): + """ + List Application Consents + + Available for Crowdin.com only (not supported in Crowdin Enterprise). + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.applications.consents.getMany + """ + params = { + "identifier": identifier, + "orderBy": order_by, + } + params.update(self.get_page_params(limit=limit, offset=offset)) + + return self._get_entire_data( + method="get", + path=self.get_application_consents_path(), + params=params, + ) + + def add_application_consent(self, request_data: AddApplicationConsentRequest): + """ + Add Application Consent + + Available for Crowdin.com only (not supported in Crowdin Enterprise). + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.applications.consents.post + """ + return self.requester.request( + method="post", + path=self.get_application_consents_path(), + request_data=request_data, + ) + + def edit_application_consent( + self, + consent_id: int, + request_data: Iterable[ApplicationConsentPatchRequest], + ): + """ + Edit Application Consent + + Available for Crowdin.com only (not supported in Crowdin Enterprise). + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.applications.consents.patch + """ + return self.requester.request( + method="patch", + path=self.get_application_consents_path(consent_id=consent_id), + request_data=request_data, + ) + + def delete_application_consent(self, consent_id: int): + """ + Delete Application Consent + + Available for Crowdin.com only (not supported in Crowdin Enterprise). + + Link to documentation: + https://developer.crowdin.com/api/v2/#operation/api.applications.consents.delete + """ + return self.requester.request( + method="delete", + path=self.get_application_consents_path(consent_id=consent_id), + ) + def get_application_data(self, applicationIdentifier: str, path: str): """ Get Application Data. diff --git a/crowdin_api/api_resources/application/tests/test_applications_resources.py b/crowdin_api/api_resources/application/tests/test_applications_resources.py index b2d27d1..34f57ca 100644 --- a/crowdin_api/api_resources/application/tests/test_applications_resources.py +++ b/crowdin_api/api_resources/application/tests/test_applications_resources.py @@ -1,11 +1,17 @@ from unittest import mock + import pytest +from crowdin_api.api_resources.enums import PatchOperation from crowdin_api.api_resources.application.resource import ApplicationResource from crowdin_api.api_resources.application.enums import ( + ApplicationConsentPatchPath, + ApplicationConsentStatus, + ListApplicationConsentsOrderBy, UserPermissions, ProjectPermissions, ) from crowdin_api.requester import APIRequester +from crowdin_api.sorting import Sorting, SortingOrder, SortingRule class TestApplicationResource: @@ -158,6 +164,137 @@ def test_edit_application_installation( request_data=data, ) + @pytest.mark.parametrize( + "in_params, path", + ( + ({}, "applications/consents"), + ({"consent_id": 12}, "applications/consents/12"), + ), + ) + def test_get_application_consents_path(self, in_params, path, base_absolut_url): + resource = self.get_resource(base_absolut_url) + assert resource.get_application_consents_path(**in_params) == path + + @pytest.mark.parametrize( + "in_params, request_params", + ( + ( + {}, + { + "identifier": None, + "orderBy": None, + "limit": 25, + "offset": 0, + }, + ), + ( + { + "identifier": "example-application", + "order_by": Sorting( + [ + SortingRule( + ListApplicationConsentsOrderBy.CREATED_AT, + SortingOrder.DESC, + ) + ] + ), + "limit": 10, + "offset": 2, + }, + { + "identifier": "example-application", + "orderBy": Sorting( + [ + SortingRule( + ListApplicationConsentsOrderBy.CREATED_AT, + SortingOrder.DESC, + ) + ] + ), + "limit": 10, + "offset": 2, + }, + ), + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_list_application_consents( + self, m_request, in_params, request_params, base_absolut_url + ): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.list_application_consents(**in_params) == "response" + m_request.assert_called_once_with( + method="get", + path=resource.get_application_consents_path(), + params=request_params, + ) + + @pytest.mark.parametrize( + "request_data", + ( + { + "identifier": "example-application", + "installedBy": 12, + "status": ApplicationConsentStatus.GRANTED, + "scopes": ["project", "tm"], + }, + { + "identifier": "example-application", + "installedBy": 12, + "status": ApplicationConsentStatus.DENIED, + "scopes": None, + }, + ), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_add_application_consent(self, m_request, request_data, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.add_application_consent(request_data) == "response" + m_request.assert_called_once_with( + method="post", + path=resource.get_application_consents_path(), + request_data=request_data, + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_edit_application_consent(self, m_request, base_absolut_url): + m_request.return_value = "response" + request_data = [ + { + "op": PatchOperation.REPLACE, + "path": ApplicationConsentPatchPath.STATUS, + "value": ApplicationConsentStatus.DENIED, + }, + { + "op": PatchOperation.REPLACE, + "path": ApplicationConsentPatchPath.SCOPES, + "value": ["project"], + }, + ] + + resource = self.get_resource(base_absolut_url) + assert resource.edit_application_consent(12, request_data) == "response" + m_request.assert_called_once_with( + method="patch", + path=resource.get_application_consents_path(consent_id=12), + request_data=request_data, + ) + + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_delete_application_consent(self, m_request, base_absolut_url): + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + assert resource.delete_application_consent(12) == "response" + m_request.assert_called_once_with( + method="delete", + path=resource.get_application_consents_path(consent_id=12), + ) + @mock.patch("crowdin_api.requester.APIRequester.request") def test_get_application_data(self, m_request, base_absolut_url): m_request.return_value = "response" diff --git a/crowdin_api/api_resources/application/types.py b/crowdin_api/api_resources/application/types.py index 28f2d8d..53604db 100644 --- a/crowdin_api/api_resources/application/types.py +++ b/crowdin_api/api_resources/application/types.py @@ -1,6 +1,10 @@ -from typing import Iterable +from typing import Any, Iterable, Optional + +from crowdin_api.api_resources.enums import PatchOperation from crowdin_api.typing import TypedDict from crowdin_api.api_resources.application.enums import ( + ApplicationConsentPatchPath, + ApplicationConsentStatus, UserPermissions, ProjectPermissions, ) @@ -25,3 +29,16 @@ class ApplicationInstallationPatchRequest(TypedDict): op: str path: str value: str + + +class AddApplicationConsentRequest(TypedDict): + identifier: str + installedBy: int + status: ApplicationConsentStatus + scopes: Optional[Iterable[str]] + + +class ApplicationConsentPatchRequest(TypedDict): + op: PatchOperation + path: ApplicationConsentPatchPath + value: Any