From 77f08c3d03888836fe275fb15039a8f639f5f8fb Mon Sep 17 00:00:00 2001 From: Kiran Raju Date: Mon, 16 Oct 2023 20:50:52 -0500 Subject: [PATCH 1/4] Create Nylas credentials API --- nylas/models/credentials.py | 82 ++++++++++++++++++++++++++++++++++ nylas/resources/credentials.py | 44 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 nylas/models/credentials.py create mode 100644 nylas/resources/credentials.py diff --git a/nylas/models/credentials.py b/nylas/models/credentials.py new file mode 100644 index 00000000..ce6e370a --- /dev/null +++ b/nylas/models/credentials.py @@ -0,0 +1,82 @@ +from dataclasses import dataclass +from type import List, Any, Dict, Optional, Literal, Union, NotRequired + +from dataclasses_json import dataclass_json +from typing_extensions import TypedDict + + +CredentialType = Literal["adminconsent", "serviceaccount", "connector"] + +@dataclass_json +@dataclass +class Credential: + """ + Interface representing a Nylas Credential object. + Attributes + id: Globally unique object identifier; + name: Name of the credential + credential_type: The type of credential + hashed_data: Hashed value of the credential that you created + created_at: Timestamp of when the credential was created + updated_at: Timestamp of when the credential was updated; + """ + id: str + name: str + credential_type: Optional[CredentialType] + hashed_data: Optional[str] = None + created_at: Optional[int] = None + updated_at: Optional[int] = None + + +class MicrosoftAdminConsentSettings(TypedDict): + client_id: str + client_secret: str + + +class CreateCredentialBaseRequest(TypedDict): + """ + Interface representing a request to create a credential. + + Attributes: + provider: OAuth provider. + settings: Settings required to create a credential + credential_type: Type of credential you want to create. + credential_data: The data required to successfully create the credential object + """ + provider: str + name: Optional[str] + credential_type: CredentialType + + +class GoogleServiceAccountCredential(CreateCredentialBaseRequest): + credential_data: Dict[str, any] + + +class AdminConsentCredential(CreateCredentialBaseRequest): + credential_data: MicrosoftAdminConsentSettings + + +class ConnectorOverrideCredential(CreateCredentialBaseRequest): + credential_data: Dict[str, any] + + +class ListCredentialQueryParams(TypedDict): + """ + Interface representing the query parameters for credentials . + + Attributes: + offset: Offset results + sort_by: Sort entries by field name + order_by: Order results by the specified field. + Currently only start is supported. + limit: The maximum number of objects to return. + This field defaults to 50. The maximum allowed value is 200. + """ + + limit: NotRequired[int] + offset: NotRequired[int] + order_by: NotRequired[str] + sort_by: NotRequired[str] + + +CredentialRequest = Union[AdminConsentCredential, GoogleServiceAccountCredential, ConnectorOverrideCredential] diff --git a/nylas/resources/credentials.py b/nylas/resources/credentials.py new file mode 100644 index 00000000..58e56e78 --- /dev/null +++ b/nylas/resources/credentials.py @@ -0,0 +1,44 @@ +from nylas.handler.api_resources import ( + ListableApiResource, + FindableApiResource, + CreatableApiResource, + UpdatableApiResource, + DestroyableApiResource, +) +from nylas.models.auth import Provider +from nylas.models.credentials import ( + Credential, + CredentialRequest, ListCredentialQueryParams +) +from nylas.models.response import Response, ListResponse, DeleteResponse + + +class Credentials( + ListableApiResource, + FindableApiResource, + CreatableApiResource, + UpdatableApiResource, + DestroyableApiResource, +): + def list(self, provider: Provider, query_params: ListCredentialQueryParams) -> ListResponse[Credential]: + return super(Credentials, self).list(path=f"/v3/connectors/{provider}/creds", response_type=Credential, + query_params=query_params) + + def find(self, provider: Provider, credential_id: str) -> Response[Credential]: + return super(Credentials, self).find( + path=f"/v3/connectors/{provider}/creds/{credential_id}", response_type=Credential + ) + + def create(self, request_body: CredentialRequest) -> Response[Credential]: + return super(Credentials, self).create( + path=f"/v3/connectors/microsoft/creds", response_type=Credential, request_body=request_body + ) + + def update(self, credential_id: str, request_body: CredentialRequest) -> Response[Credential]: + return super(Credentials, self).update( + path=f"/v3/connectors/{request_body.get('provider')}/creds/{credential_id}", response_type=Credential, + request_body=request_body + ) + + def destroy(self, provider: Provider, credential_id: str) -> DeleteResponse[Credential]: + return super(Credentials, self).destroy(path=f"/v3/connectors/{provider}/creds/{credential_id}") From 78898fce3ebfe409205bc0927795bd27d502a675 Mon Sep 17 00:00:00 2001 From: Kiran Raju Date: Tue, 17 Oct 2023 08:17:26 -0500 Subject: [PATCH 2/4] Added comments and made it accessible on the client --- nylas/client.py | 11 +++++++ nylas/resources/credentials.py | 56 +++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/nylas/client.py b/nylas/client.py index 0b42e1c5..1296be14 100644 --- a/nylas/client.py +++ b/nylas/client.py @@ -3,6 +3,7 @@ from nylas.resources.applications import Applications from nylas.resources.auth import Auth from nylas.resources.calendars import Calendars +from nylas.resources.credentials import Credentials from nylas.resources.events import Events from nylas.resources.webhooks import Webhooks @@ -52,6 +53,16 @@ def applications(self) -> Applications: """ return Applications(self.http_client) + @property + def credentials(self) -> Credentials: + """ + Access the Credentials API. + + Returns: + The Credentials API. + """ + return Credentials(self.http_client) + @property def calendars(self) -> Calendars: """ diff --git a/nylas/resources/credentials.py b/nylas/resources/credentials.py index 58e56e78..3c14c93d 100644 --- a/nylas/resources/credentials.py +++ b/nylas/resources/credentials.py @@ -21,24 +21,78 @@ class Credentials( DestroyableApiResource, ): def list(self, provider: Provider, query_params: ListCredentialQueryParams) -> ListResponse[Credential]: + """ + Return all credentials for a particular provider. + + Args: + provider: The provider. + query_params: The query parameters to include in the request. + + Returns: + The list of credentials. + """ + return super(Credentials, self).list(path=f"/v3/connectors/{provider}/creds", response_type=Credential, query_params=query_params) def find(self, provider: Provider, credential_id: str) -> Response[Credential]: + """ + Return a credential. + + Args: + provider: The provider of the credential. + credential_id: The ID of the credential to retrieve. + + Returns: + The Credential. + """ + return super(Credentials, self).find( path=f"/v3/connectors/{provider}/creds/{credential_id}", response_type=Credential ) def create(self, request_body: CredentialRequest) -> Response[Credential]: + """ + Create a credential for a particular provider. + + Args: + request_body: The values to create the Credential with. + + Returns: + The created Credential. + """ + return super(Credentials, self).create( - path=f"/v3/connectors/microsoft/creds", response_type=Credential, request_body=request_body + path=f"/v3/connectors/{request_body.get('provider')}/creds", response_type=Credential, request_body=request_body ) def update(self, credential_id: str, request_body: CredentialRequest) -> Response[Credential]: + """ + Update a credential. + + Args: + credential_id: The ID of the credential to update. + request_body: The values to update the credential with. + + Returns: + The updated credential. + """ + return super(Credentials, self).update( path=f"/v3/connectors/{request_body.get('provider')}/creds/{credential_id}", response_type=Credential, request_body=request_body ) def destroy(self, provider: Provider, credential_id: str) -> DeleteResponse[Credential]: + """ + Delete a credential. + + Args: + provider: the provider for the grant + credential_id: The ID of the credential to delete. + + Returns: + The deletion response. + """ + return super(Credentials, self).destroy(path=f"/v3/connectors/{provider}/creds/{credential_id}") From bf54c06cf4a891576560cd721bf64696ba00f26d Mon Sep 17 00:00:00 2001 From: kraju3 <35513942+kraju3@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:21:41 -0500 Subject: [PATCH 3/4] Update nylas/models/credentials.py Co-authored-by: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Update nylas/models/credentials.py Changing typedDic to Protocol for AdminConsentSettings Co-authored-by: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Reformating The credential resource Co-authored-by: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Moving credentials to auth resource Removed credential Request Revert "Removed credential Request" This reverts commit 519250ccde80632f3d6e69a9280517726e185c1b. Removed base request Resolved comments --- nylas/client.py | 11 ----------- nylas/models/credentials.py | 35 ++++++++++++++-------------------- nylas/resources/auth.py | 12 ++++++++++++ nylas/resources/credentials.py | 10 ++++++---- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/nylas/client.py b/nylas/client.py index 1296be14..0b42e1c5 100644 --- a/nylas/client.py +++ b/nylas/client.py @@ -3,7 +3,6 @@ from nylas.resources.applications import Applications from nylas.resources.auth import Auth from nylas.resources.calendars import Calendars -from nylas.resources.credentials import Credentials from nylas.resources.events import Events from nylas.resources.webhooks import Webhooks @@ -53,16 +52,6 @@ def applications(self) -> Applications: """ return Applications(self.http_client) - @property - def credentials(self) -> Credentials: - """ - Access the Credentials API. - - Returns: - The Credentials API. - """ - return Credentials(self.http_client) - @property def calendars(self) -> Calendars: """ diff --git a/nylas/models/credentials.py b/nylas/models/credentials.py index ce6e370a..ba926837 100644 --- a/nylas/models/credentials.py +++ b/nylas/models/credentials.py @@ -2,11 +2,11 @@ from type import List, Any, Dict, Optional, Literal, Union, NotRequired from dataclasses_json import dataclass_json -from typing_extensions import TypedDict - +from typing_extensions import TypedDict, Protocol CredentialType = Literal["adminconsent", "serviceaccount", "connector"] + @dataclass_json @dataclass class Credential: @@ -28,36 +28,32 @@ class Credential: updated_at: Optional[int] = None -class MicrosoftAdminConsentSettings(TypedDict): +class MicrosoftAdminConsentSettings(Protocol): client_id: str client_secret: str -class CreateCredentialBaseRequest(TypedDict): +class GoogleServiceAccountCredential(Protocol): + private_key_id: str + private_key: str + client_email: str + + +CredentialData = Union[MicrosoftAdminConsentSettings, GoogleServiceAccountCredential, Dict[str, any]] + + +class CredentialRequest(TypedDict): """ Interface representing a request to create a credential. Attributes: - provider: OAuth provider. settings: Settings required to create a credential credential_type: Type of credential you want to create. credential_data: The data required to successfully create the credential object """ - provider: str name: Optional[str] credential_type: CredentialType - - -class GoogleServiceAccountCredential(CreateCredentialBaseRequest): - credential_data: Dict[str, any] - - -class AdminConsentCredential(CreateCredentialBaseRequest): - credential_data: MicrosoftAdminConsentSettings - - -class ConnectorOverrideCredential(CreateCredentialBaseRequest): - credential_data: Dict[str, any] + credential_data: CredentialData class ListCredentialQueryParams(TypedDict): @@ -77,6 +73,3 @@ class ListCredentialQueryParams(TypedDict): offset: NotRequired[int] order_by: NotRequired[str] sort_by: NotRequired[str] - - -CredentialRequest = Union[AdminConsentCredential, GoogleServiceAccountCredential, ConnectorOverrideCredential] diff --git a/nylas/resources/auth.py b/nylas/resources/auth.py index 2c6a90a9..1392235b 100644 --- a/nylas/resources/auth.py +++ b/nylas/resources/auth.py @@ -15,6 +15,7 @@ URLForAdminConsentConfig, ) from nylas.models.response import Response +from nylas.resources.credentials import Credentials from nylas.resources.grants import Grants from nylas.resources.resource import Resource @@ -57,6 +58,17 @@ def _build_query_with_admin_consent(config: dict) -> dict: class Auth(Resource): + + @property + def credentials(self) -> Credentials: + """ + Access the Credentials API. + + Returns: + The Credentials API. + """ + return Credentials(self.http_client) + @property def grants(self) -> Grants: """ diff --git a/nylas/resources/credentials.py b/nylas/resources/credentials.py index 3c14c93d..462040af 100644 --- a/nylas/resources/credentials.py +++ b/nylas/resources/credentials.py @@ -51,11 +51,12 @@ def find(self, provider: Provider, credential_id: str) -> Response[Credential]: path=f"/v3/connectors/{provider}/creds/{credential_id}", response_type=Credential ) - def create(self, request_body: CredentialRequest) -> Response[Credential]: + def create(self, provider: Provider, request_body: CredentialRequest) -> Response[Credential]: """ Create a credential for a particular provider. Args: + provider: The provider. request_body: The values to create the Credential with. Returns: @@ -63,14 +64,15 @@ def create(self, request_body: CredentialRequest) -> Response[Credential]: """ return super(Credentials, self).create( - path=f"/v3/connectors/{request_body.get('provider')}/creds", response_type=Credential, request_body=request_body + path=f"/v3/connectors/{provider}/creds", response_type=Credential, request_body=request_body ) - def update(self, credential_id: str, request_body: CredentialRequest) -> Response[Credential]: + def update(self, provider: Provider, credential_id: str, request_body: CredentialRequest) -> Response[Credential]: """ Update a credential. Args: + provider: The provider. credential_id: The ID of the credential to update. request_body: The values to update the credential with. @@ -79,7 +81,7 @@ def update(self, credential_id: str, request_body: CredentialRequest) -> Respons """ return super(Credentials, self).update( - path=f"/v3/connectors/{request_body.get('provider')}/creds/{credential_id}", response_type=Credential, + path=f"/v3/connectors/{provider}/creds/{credential_id}", response_type=Credential, request_body=request_body ) From aa11e0e55efa42d5b2509f85fc7ad36b4effc28d Mon Sep 17 00:00:00 2001 From: Kiran Raju Date: Tue, 17 Oct 2023 11:07:04 -0500 Subject: [PATCH 4/4] Fix a small typo --- nylas/resources/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nylas/resources/auth.py b/nylas/resources/auth.py index 1392235b..e1c4ecc2 100644 --- a/nylas/resources/auth.py +++ b/nylas/resources/auth.py @@ -67,7 +67,7 @@ def credentials(self) -> Credentials: Returns: The Credentials API. """ - return Credentials(self.http_client) + return Credentials(self._http_client) @property def grants(self) -> Grants: