From 8d986a64b9eabbf3d80688e6ccd9c01108abce01 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Mon, 10 Jun 2024 08:58:35 +0000 Subject: [PATCH 1/2] feat: add support for bitbucket --- .../connected_services/api.spec.yaml | 1 + .../connected_services/apispec.py | 3 +- .../33561ece81e5_add_bitbucket_support.py | 77 +++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 components/renku_data_services/migrations/versions/33561ece81e5_add_bitbucket_support.py diff --git a/components/renku_data_services/connected_services/api.spec.yaml b/components/renku_data_services/connected_services/api.spec.yaml index 05d4f90b7..9f1541f74 100644 --- a/components/renku_data_services/connected_services/api.spec.yaml +++ b/components/renku_data_services/connected_services/api.spec.yaml @@ -312,6 +312,7 @@ components: enum: - "gitlab" - "github" + - "bitbucket" example: "gitlab" ClientId: description: | diff --git a/components/renku_data_services/connected_services/apispec.py b/components/renku_data_services/connected_services/apispec.py index 35492c9c8..bc4d6f3c4 100644 --- a/components/renku_data_services/connected_services/apispec.py +++ b/components/renku_data_services/connected_services/apispec.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: api.spec.yaml -# timestamp: 2024-06-03T07:31:23+00:00 +# timestamp: 2024-06-10T08:17:29+00:00 from __future__ import annotations @@ -14,6 +14,7 @@ class ProviderKind(Enum): gitlab = "gitlab" github = "github" + bitbucket = "bitbucket" class ConnectionStatus(Enum): diff --git a/components/renku_data_services/migrations/versions/33561ece81e5_add_bitbucket_support.py b/components/renku_data_services/migrations/versions/33561ece81e5_add_bitbucket_support.py new file mode 100644 index 000000000..ee950818f --- /dev/null +++ b/components/renku_data_services/migrations/versions/33561ece81e5_add_bitbucket_support.py @@ -0,0 +1,77 @@ +"""add bitbucket support + +Revision ID: 33561ece81e5 +Revises: c0631477aea4 +Create Date: 2024-06-10 08:30:48.029894 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "33561ece81e5" +down_revision = "c0631477aea4" +branch_labels = None +depends_on = None + +# Update the "ProviderKind" enum +old_kinds = ["gitlab", "github"] +new_kinds = list(old_kinds) + ["bitbucket"] + +old_type = sa.Enum(*old_kinds, name="providerkind") +new_type = sa.Enum(*new_kinds, name="providerkind") +temp_type = sa.Enum(*new_kinds, name="providerkind_tmp") +temp_type_downgrade = sa.Enum(*old_kinds, name="providerkind_tmp") + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + temp_type.create(op.get_bind()) + op.alter_column( + "oauth2_clients", + "kind", + type_=temp_type, + postgresql_using="kind::text::providerkind_tmp", + schema="connected_services", + ) + op.execute("DROP TYPE providerkind") + new_type.create(op.get_bind()) + op.alter_column( + "oauth2_clients", + "kind", + type_=new_type, + postgresql_using="kind::text::providerkind", + schema="connected_services", + ) + op.execute("DROP TYPE providerkind_tmp") + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + + # Update rows where kind = "bitbucket" + table = sa.sql.table("oauth2_clients", sa.Column("kind", new_type), schema="connected_services") + op.execute(table.update().where(table.columns.kind == "bitbucket").values(kind="gitlab")) + + temp_type_downgrade.create(op.get_bind()) + op.alter_column( + "oauth2_clients", + "kind", + type_=temp_type_downgrade, + postgresql_using="kind::text::providerkind_tmp", + schema="connected_services", + ) + op.execute("DROP TYPE providerkind") + old_type.create(op.get_bind()) + op.alter_column( + "oauth2_clients", + "kind", + type_=old_type, + postgresql_using="kind::text::providerkind", + schema="connected_services", + ) + op.execute("DROP TYPE providerkind_tmp") + pass + # ### end Alembic commands ### From 9ff3e8a6c6c74dd65c120ddea956f2f8cd098869 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Mon, 10 Jun 2024 09:33:30 +0000 Subject: [PATCH 2/2] add draft for adapter --- .../connected_services/provider_adapters.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/components/renku_data_services/connected_services/provider_adapters.py b/components/renku_data_services/connected_services/provider_adapters.py index 415fee094..a66504cdb 100644 --- a/components/renku_data_services/connected_services/provider_adapters.py +++ b/components/renku_data_services/connected_services/provider_adapters.py @@ -102,9 +102,35 @@ def api_validate_account_response(self, response: Response) -> models.ConnectedA return external_models.GitHubConnectedAccount.model_validate(response.json()).to_connected_account() +class BitBucketAdapter(ProviderAdapter): + """Adapter for BitBucket OAuth2 clients.""" + + @property + def authorization_url(self) -> str: + """The authorization URL for the OAuth2 protocol.""" + return urljoin(self.client_url, "site/oauth2/authorize") + + @property + def token_endpoint_url(self) -> str: + """The token endpoint URL for the OAuth2 protocol.""" + return urljoin(self.client_url, "site/oauth2/access_token") + + @property + def api_url(self) -> str: + """The URL used for API calls on the Resource Server.""" + url = urlparse(self.client_url) + url = url._replace(netloc=f"api.{url.netloc}") + return urljoin(urlunparse(url), "2.0") + + def api_validate_account_response(self, response: Response) -> models.ConnectedAccount: + """Validates and returns the connected account response from the Resource Server.""" + raise NotImplementedError() + + _adapter_map: dict[ProviderKind, type[ProviderAdapter]] = { ProviderKind.gitlab: GitLabAdapter, ProviderKind.github: GitHubAdapter, + ProviderKind.bitbucket: BitBucketAdapter, }