data-product-portal-plugins 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data_product_portal_plugins-0.8.0/PKG-INFO +15 -0
  2. data_product_portal_plugins-0.8.0/portal_plugins/agno/agno-logo.svg +4 -0
  3. data_product_portal_plugins-0.8.0/portal_plugins/agno/schema.py +33 -0
  4. data_product_portal_plugins-0.8.0/portal_plugins/azure_blob/azure-storage-account-logo.svg +22 -0
  5. data_product_portal_plugins-0.8.0/portal_plugins/azure_blob/model.py +20 -0
  6. data_product_portal_plugins-0.8.0/portal_plugins/azure_blob/schema.py +119 -0
  7. data_product_portal_plugins-0.8.0/portal_plugins/azure_blob/versions/azure_blob_0001_baseline.py +37 -0
  8. data_product_portal_plugins-0.8.0/portal_plugins/conveyor/conveyor-logo.svg +17 -0
  9. data_product_portal_plugins-0.8.0/portal_plugins/conveyor/schema.py +35 -0
  10. data_product_portal_plugins-0.8.0/portal_plugins/databricks/databricks-logo.svg +14 -0
  11. data_product_portal_plugins-0.8.0/portal_plugins/databricks/model.py +22 -0
  12. data_product_portal_plugins-0.8.0/portal_plugins/databricks/schema.py +151 -0
  13. data_product_portal_plugins-0.8.0/portal_plugins/databricks/versions/databricks_0001_baseline.py +41 -0
  14. data_product_portal_plugins-0.8.0/portal_plugins/github/github-logo.svg +3 -0
  15. data_product_portal_plugins-0.8.0/portal_plugins/github/schema.py +35 -0
  16. data_product_portal_plugins-0.8.0/portal_plugins/glue/glue-logo.svg +18 -0
  17. data_product_portal_plugins-0.8.0/portal_plugins/glue/mcp_instructions.py +42 -0
  18. data_product_portal_plugins-0.8.0/portal_plugins/glue/mcp_tools.py +397 -0
  19. data_product_portal_plugins-0.8.0/portal_plugins/glue/model.py +22 -0
  20. data_product_portal_plugins-0.8.0/portal_plugins/glue/schema.py +153 -0
  21. data_product_portal_plugins-0.8.0/portal_plugins/glue/versions/glue_0001_baseline.py +41 -0
  22. data_product_portal_plugins-0.8.0/portal_plugins/osi_sem_model/model.py +17 -0
  23. data_product_portal_plugins-0.8.0/portal_plugins/osi_sem_model/osi-logo.svg +28 -0
  24. data_product_portal_plugins-0.8.0/portal_plugins/osi_sem_model/schema.py +69 -0
  25. data_product_portal_plugins-0.8.0/portal_plugins/osi_sem_model/versions/osi_sem_model_0001_baseline.py +36 -0
  26. data_product_portal_plugins-0.8.0/portal_plugins/parameter_store/model.py +17 -0
  27. data_product_portal_plugins-0.8.0/portal_plugins/parameter_store/parameter-store-logo.svg +1 -0
  28. data_product_portal_plugins-0.8.0/portal_plugins/parameter_store/schema.py +106 -0
  29. data_product_portal_plugins-0.8.0/portal_plugins/parameter_store/versions/parameter_store_0001_baseline.py +36 -0
  30. data_product_portal_plugins-0.8.0/portal_plugins/postgresql/model.py +19 -0
  31. data_product_portal_plugins-0.8.0/portal_plugins/postgresql/postgresql-logo.svg +2 -0
  32. data_product_portal_plugins-0.8.0/portal_plugins/postgresql/schema.py +136 -0
  33. data_product_portal_plugins-0.8.0/portal_plugins/postgresql/versions/postgresql_0001_baseline.py +38 -0
  34. data_product_portal_plugins-0.8.0/portal_plugins/redshift/aws-redshift-logo.svg +1 -0
  35. data_product_portal_plugins-0.8.0/portal_plugins/redshift/model.py +22 -0
  36. data_product_portal_plugins-0.8.0/portal_plugins/redshift/schema.py +144 -0
  37. data_product_portal_plugins-0.8.0/portal_plugins/redshift/versions/redshift_0001_baseline.py +41 -0
  38. data_product_portal_plugins-0.8.0/portal_plugins/s3/model.py +18 -0
  39. data_product_portal_plugins-0.8.0/portal_plugins/s3/s3-logo.svg +1 -0
  40. data_product_portal_plugins-0.8.0/portal_plugins/s3/schema.py +103 -0
  41. data_product_portal_plugins-0.8.0/portal_plugins/s3/versions/s3_0001_baseline.py +37 -0
  42. data_product_portal_plugins-0.8.0/portal_plugins/snowflake/model.py +22 -0
  43. data_product_portal_plugins-0.8.0/portal_plugins/snowflake/schema.py +151 -0
  44. data_product_portal_plugins-0.8.0/portal_plugins/snowflake/snowflake-logo.svg +8 -0
  45. data_product_portal_plugins-0.8.0/portal_plugins/snowflake/versions/snowflake_0001_baseline.py +41 -0
  46. data_product_portal_plugins-0.8.0/pyproject.toml +43 -0
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: data-product-portal-plugins
3
+ Version: 0.8.0
4
+ Summary: First-party technical asset plugins for Data Product Portal
5
+ Requires-Python: >=3.13
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.13
8
+ Classifier: Programming Language :: Python :: 3.14
9
+ Requires-Dist: alembic (>=1.20.0)
10
+ Requires-Dist: boto3 (>=1.43.93)
11
+ Requires-Dist: data-product-portal (>=0.8.0)
12
+ Requires-Dist: fastapi (>=0.141.1)
13
+ Requires-Dist: fastmcp (>=3.4.7)
14
+ Requires-Dist: pydantic (>=2.13.5)
15
+ Requires-Dist: sqlalchemy (>=2.0.52)
@@ -0,0 +1,4 @@
1
+ <svg width="1em" height="1em" viewBox="16 9 56 48" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M52.4943 9.29102H29.5019V17.2933H46.8809L61.6912 56.3333H71.2585L52.4943 9.29102Z" fill="#ff4017"/>
3
+ <path d="M40.0389 48.3312H16.4961V56.3334H40.0389V48.3312Z" fill="#ff4017"/>
4
+ </svg>
@@ -0,0 +1,33 @@
1
+ from typing import ClassVar, Optional
2
+ from uuid import UUID
3
+
4
+ from sqlalchemy.orm import Session
5
+
6
+ from app.data_products.model import DataProduct as DataProductModel
7
+ from app.technical_asset_configuration.base_schema import (
8
+ PlatformMetadata,
9
+ TechnicalAssetPlugin,
10
+ )
11
+ from app.users.schema import User
12
+
13
+
14
+ class AgnoPlugin(TechnicalAssetPlugin):
15
+ name: ClassVar[str] = "AgnoPlugin"
16
+ version: ClassVar[str] = "1.0"
17
+
18
+ _platform_metadata = PlatformMetadata(
19
+ display_name="Agno",
20
+ icon_name="agno-logo.svg",
21
+ icon_package="portal_plugins.agno",
22
+ platform_key="agno",
23
+ has_environments=False,
24
+ show_in_form=False,
25
+ detailed_name="Agno Agent",
26
+ )
27
+
28
+ @classmethod
29
+ def get_url(
30
+ cls, id: UUID, db: Session, actor: User, environment: Optional[str] = None
31
+ ) -> str:
32
+ data_product = db.get(DataProductModel, id)
33
+ return f"https://os.agno.com/chat?type=agent&id={data_product.namespace}-agent"
@@ -0,0 +1,22 @@
1
+ <svg id="f2f04349-8aee-4413-84c9-a9053611b319" xmlns="http://www.w3.org/2000/svg" width="18" height="18"
2
+ viewBox="0 0 18 18">
3
+ <defs>
4
+ <linearGradient id="ad4c4f96-09aa-4f91-ba10-5cb8ad530f74" x1="9" y1="15.83" x2="9" y2="5.79"
5
+ gradientUnits="userSpaceOnUse">
6
+ <stop offset="0" stop-color="#b3b3b3"/>
7
+ <stop offset="0.26" stop-color="#c1c1c1"/>
8
+ <stop offset="1" stop-color="#e6e6e6"/>
9
+ </linearGradient>
10
+ </defs>
11
+ <title>Icon-storage-86</title>
12
+ <path d="M.5,5.79h17a0,0,0,0,1,0,0v9.48a.57.57,0,0,1-.57.57H1.07a.57.57,0,0,1-.57-.57V5.79A0,0,0,0,1,.5,5.79Z"
13
+ fill="url(#ad4c4f96-09aa-4f91-ba10-5cb8ad530f74)"/>
14
+ <path d="M1.07,2.17H16.93a.57.57,0,0,1,.57.57V5.79a0,0,0,0,1,0,0H.5a0,0,0,0,1,0,0V2.73A.57.57,0,0,1,1.07,2.17Z"
15
+ fill="#37c2b1"/>
16
+ <path d="M2.81,6.89H15.18a.27.27,0,0,1,.26.27v1.4a.27.27,0,0,1-.26.27H2.81a.27.27,0,0,1-.26-.27V7.16A.27.27,0,0,1,2.81,6.89Z"
17
+ fill="#fff"/>
18
+ <path d="M2.82,9.68H15.19a.27.27,0,0,1,.26.27v1.41a.27.27,0,0,1-.26.27H2.82a.27.27,0,0,1-.26-.27V10A.27.27,0,0,1,2.82,9.68Z"
19
+ fill="#37c2b1"/>
20
+ <path d="M2.82,12.5H15.19a.27.27,0,0,1,.26.27v1.41a.27.27,0,0,1-.26.27H2.82a.27.27,0,0,1-.26-.27V12.77A.27.27,0,0,1,2.82,12.5Z"
21
+ fill="#258277"/>
22
+ </svg>
@@ -0,0 +1,20 @@
1
+ from sqlalchemy import String
2
+ from sqlalchemy.orm import Mapped, mapped_column
3
+
4
+ from app.technical_asset_configuration.base_model import BaseTechnicalAssetConfiguration
5
+
6
+ NAME = "AzureBlobTechnicalAssetConfiguration"
7
+
8
+
9
+ class AzureBlobTechnicalAssetConfiguration(BaseTechnicalAssetConfiguration):
10
+ __tablename__ = "azure_blob_technical_asset_configurations"
11
+
12
+ domain: Mapped[str] = mapped_column(
13
+ String, nullable=False
14
+ ) # Required to support looking up the correct storage account
15
+ path: Mapped[str] = mapped_column(String, nullable=True)
16
+ container_name: Mapped[str] = mapped_column(String, nullable=False)
17
+
18
+ __mapper_args__ = {
19
+ "polymorphic_identity": NAME,
20
+ }
@@ -0,0 +1,119 @@
1
+ import logging
2
+ from typing import ClassVar, Optional
3
+ from uuid import UUID
4
+
5
+ from sqlalchemy.orm import Session
6
+
7
+ from app.configuration.environments.platform_service_configurations.schemas import (
8
+ AzureBlobConfig,
9
+ )
10
+ from portal_plugins.azure_blob.model import (
11
+ NAME,
12
+ )
13
+ from portal_plugins.azure_blob.model import (
14
+ AzureBlobTechnicalAssetConfiguration as AzureBlobTechnicalAssetConfigurationModel,
15
+ )
16
+ from app.technical_asset_configuration.base_schema import (
17
+ PluginContext,
18
+ PlatformMetadata,
19
+ TechnicalAssetPlugin,
20
+ UIElementMetadata,
21
+ UIElementString,
22
+ )
23
+ from app.technical_asset_configuration.enums import UIElementType
24
+ from app.users.schema import User
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class AzureBlobTechnicalAssetConfiguration(TechnicalAssetPlugin):
30
+ name: ClassVar[str] = NAME
31
+ version: ClassVar[str] = "1.0"
32
+
33
+ domain: str = ""
34
+ path: str = ""
35
+ container_name: str
36
+
37
+ _platform_metadata = PlatformMetadata(
38
+ result_string_template="platform_provided_storage_account/{container_name}/{path}",
39
+ technical_info_template="https://{storage_account}.blob.core.windows.net/{container_name}/{path}",
40
+ display_name="Blob",
41
+ icon_name="azure-storage-account-logo.svg",
42
+ icon_package="portal_plugins.azure_blob",
43
+ platform_key="azureblob",
44
+ parent_platform="azure",
45
+ result_label="Resulting path",
46
+ result_tooltip="The path you can access through this technical asset",
47
+ detailed_name="Path",
48
+ )
49
+
50
+ class Meta:
51
+ orm_model = AzureBlobTechnicalAssetConfigurationModel
52
+
53
+ def validate_configuration(self, context: PluginContext, db: Session):
54
+ self.domain = context.domain.name
55
+
56
+ @classmethod
57
+ def get_url(
58
+ cls, id: UUID, db: Session, actor: User, environment: Optional[str] = None
59
+ ) -> str:
60
+ return "https://portal.azure.com/"
61
+
62
+ def get_configuration(
63
+ self, configs: list[AzureBlobConfig]
64
+ ) -> Optional[AzureBlobConfig]:
65
+ """
66
+ No filtering on blob configuration as there should only be 1 map per environment to translate domain -> storage account
67
+ """
68
+ return next((config for config in configs), None)
69
+
70
+ def render_template(self, template, **context):
71
+ """
72
+ Example template: https://{storage_account}.blob.core.windows.net/{container_name}/{path}
73
+ Render the template with the provided context, handling storage account resolution.
74
+ Supports 2 different template formats for resolving the technical information:
75
+ - The platform env configuration contains storage account names by domain. We fetch the storage account name for the domain and use it in the template.
76
+ - There is only one storage account name assigned to the default key in the platform env configuration, we use it in the template.
77
+ """
78
+ logger.info("Start rendering template")
79
+ if "{storage_account}" in template:
80
+ storage_account_names = context.get("storage_account_names", {})
81
+ if storage_account_names and self.domain in storage_account_names:
82
+ context["storage_account"] = storage_account_names[self.domain]
83
+ logger.info(f"storage account name is {context['storage_account']}")
84
+ elif len(storage_account_names) == 1 and "default" in storage_account_names:
85
+ context["storage_account"] = list(storage_account_names.values())[0]
86
+ logger.info(f"storage account name is {context['storage_account']}")
87
+ return super().render_template(template, **context)
88
+ else:
89
+ return super().render_template(template, **context)
90
+
91
+ @classmethod
92
+ def get_ui_metadata(cls, db: Session) -> list[UIElementMetadata]:
93
+ base_metadata = super().get_ui_metadata(db)
94
+ base_metadata += [
95
+ UIElementMetadata(
96
+ name="domain",
97
+ label="domain",
98
+ type=UIElementType.String,
99
+ required=False,
100
+ hidden=True,
101
+ string=UIElementString(initial_value=""),
102
+ ),
103
+ UIElementMetadata(
104
+ name="container_name",
105
+ label="Container",
106
+ required=True,
107
+ type=UIElementType.String,
108
+ string=UIElementString(initial_value=""),
109
+ ),
110
+ UIElementMetadata(
111
+ name="path",
112
+ label="Path",
113
+ required=False,
114
+ type=UIElementType.String,
115
+ tooltip="The name of the path to give write access to",
116
+ string=UIElementString(initial_value=""),
117
+ ),
118
+ ]
119
+ return base_metadata
@@ -0,0 +1,37 @@
1
+ from typing import Sequence, Union
2
+
3
+ import sqlalchemy as sa
4
+ from alembic import op
5
+ from sqlalchemy.dialects import postgresql
6
+
7
+ from app.shared.model import utcnow
8
+
9
+ revision: str = "azure_blob_0001_baseline"
10
+ down_revision: Union[str, None] = None
11
+ branch_labels: Union[str, Sequence[str], None] = None
12
+ depends_on: Union[str, Sequence[str], None] = "5477aa0d86f0"
13
+
14
+ TABLE = "azure_blob_technical_asset_configurations"
15
+
16
+
17
+ def upgrade() -> None:
18
+ op.create_table(
19
+ TABLE,
20
+ sa.Column(
21
+ "id",
22
+ postgresql.UUID(as_uuid=True),
23
+ sa.ForeignKey("data_output_configurations.id", ondelete="CASCADE"),
24
+ primary_key=True,
25
+ ),
26
+ sa.Column("domain", sa.String(), nullable=False),
27
+ sa.Column("container_name", sa.String(), nullable=False),
28
+ sa.Column("path", sa.String(), nullable=True),
29
+ sa.Column("created_on", sa.DateTime(timezone=False), server_default=utcnow()),
30
+ sa.Column("updated_on", sa.DateTime(timezone=False), onupdate=utcnow()),
31
+ sa.Column("deleted_at", sa.DateTime(timezone=False), nullable=True),
32
+ if_not_exists=True,
33
+ )
34
+
35
+
36
+ def downgrade() -> None:
37
+ op.drop_table(TABLE, if_exists=True)
@@ -0,0 +1,17 @@
1
+ <svg width="1em" height="1em" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_662_6260)">
3
+ <path d="M10.0412 7.6997H3.06342C2.12356 7.6997 1.36151 6.94183 1.36151 6.00712C1.36151 5.07242 2.12356 4.31454 3.06342 4.31454H10.0412C11.263 4.31454 12.2537 3.32926 12.2537 2.11418C12.2537 0.899104 11.263 -0.0861816 10.0412 -0.0861816H5.78646C4.56469 -0.0861816 3.57399 0.899104 3.57399 2.11418H4.9355C4.9355 1.64662 5.31633 1.26789 5.78646 1.26789H10.0412C10.5113 1.26789 10.8922 1.64662 10.8922 2.11418C10.8922 2.58173 10.5113 2.96047 10.0412 2.96047H3.06342C1.37153 2.96047 0 4.32449 0 6.00712C0 7.68975 1.37153 9.05374 3.06342 9.05374H10.0412C10.5113 9.05374 10.8922 9.43249 10.8922 9.90003C10.8922 10.3677 10.5113 10.7463 10.0412 10.7463H5.78646C5.31633 10.7463 4.9355 10.3677 4.9355 9.90003H3.57399C3.57399 11.1151 4.56469 12.1004 5.78646 12.1004H10.0412C11.263 12.1004 12.2537 11.1151 12.2537 9.90003C12.2537 8.68499 11.263 7.6997 10.0412 7.6997Z"
4
+ fill="#29006B"/>
5
+ <path d="M3.40368 6.85335C3.87364 6.85335 4.25463 6.47445 4.25463 6.00706C4.25463 5.53967 3.87364 5.16077 3.40368 5.16077C2.93372 5.16077 2.55273 5.53967 2.55273 6.00706C2.55273 6.47445 2.93372 6.85335 3.40368 6.85335Z"
6
+ fill="#29006B"/>
7
+ <path d="M6.63708 6.85335C7.10704 6.85335 7.48803 6.47445 7.48803 6.00706C7.48803 5.53967 7.10704 5.16077 6.63708 5.16077C6.16712 5.16077 5.78613 5.53967 5.78613 6.00706C5.78613 6.47445 6.16712 6.85335 6.63708 6.85335Z"
8
+ fill="#29006B"/>
9
+ <path d="M9.87048 6.85335C10.3404 6.85335 10.7215 6.47445 10.7215 6.00706C10.7215 5.53967 10.3404 5.16077 9.87048 5.16077C9.40054 5.16077 9.01953 5.53967 9.01953 6.00706C9.01953 6.47445 9.40054 6.85335 9.87048 6.85335Z"
10
+ fill="#29006B"/>
11
+ </g>
12
+ <defs>
13
+ <clipPath id="clip0_662_6260">
14
+ <rect width="12" height="12" fill="white"/>
15
+ </clipPath>
16
+ </defs>
17
+ </svg>
@@ -0,0 +1,35 @@
1
+ from typing import ClassVar, Optional
2
+ from uuid import UUID
3
+
4
+ from sqlalchemy.orm import Session
5
+
6
+ from app.core.conveyor.notebook_builder import CONVEYOR_SERVICE
7
+ from app.data_products.model import DataProduct as DataProductModel
8
+ from app.technical_asset_configuration.base_schema import (
9
+ PlatformMetadata,
10
+ TechnicalAssetPlugin,
11
+ )
12
+ from app.users.schema import User
13
+
14
+
15
+ class ConveyorPlugin(TechnicalAssetPlugin):
16
+ name: ClassVar[str] = "ConveyorPlugin"
17
+ version: ClassVar[str] = "1.0"
18
+
19
+ _platform_metadata = PlatformMetadata(
20
+ display_name="Conveyor",
21
+ icon_name="conveyor-logo.svg",
22
+ icon_package="portal_plugins.conveyor",
23
+ platform_key="conveyor",
24
+ parent_platform=None,
25
+ has_environments=False,
26
+ detailed_name="Conveyor",
27
+ show_in_form=False,
28
+ )
29
+
30
+ @classmethod
31
+ def get_url(
32
+ cls, id: UUID, db: Session, actor: User, environment: Optional[str] = None
33
+ ) -> str:
34
+ data_product = db.get(DataProductModel, id)
35
+ return CONVEYOR_SERVICE.generate_ide_url(data_product.namespace)
@@ -0,0 +1,14 @@
1
+ <svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2
+ <g clip-path="url(#clip0_854_6322)">
3
+ <path d="M33 -1H0V34H33V-1Z" fill="url(#pattern0_854_6322)"/>
4
+ </g>
5
+ <defs>
6
+ <pattern id="pattern0_854_6322" patternContentUnits="objectBoundingBox" width="1" height="1">
7
+ <use xlink:href="#image0_854_6322" transform="matrix(0.00338462 0 0 0.00314562 -1.50536 -0.144402)"/>
8
+ </pattern>
9
+ <clipPath id="clip0_854_6322">
10
+ <rect width="33" height="33" fill="white"/>
11
+ </clipPath>
12
+ <image id="image0_854_6322" width="1200" height="630" xlink:href="data:image/png;base64,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"/>
13
+ </defs>
14
+ </svg>
@@ -0,0 +1,22 @@
1
+ from sqlalchemy import String
2
+ from sqlalchemy.orm import Mapped, mapped_column
3
+
4
+ from app.technical_asset_configuration.base_model import BaseTechnicalAssetConfiguration
5
+
6
+ NAME = "DatabricksTechnicalAssetConfiguration"
7
+
8
+
9
+ class DatabricksTechnicalAssetConfiguration(BaseTechnicalAssetConfiguration):
10
+ __tablename__ = "databricks_technical_asset_configurations"
11
+
12
+ catalog: Mapped[str] = mapped_column(String, nullable=True)
13
+ schema: Mapped[str] = mapped_column(String, nullable=True)
14
+ bucket_identifier: Mapped[str] = mapped_column(String, nullable=True)
15
+ catalog_path: Mapped[str] = mapped_column(String, nullable=True)
16
+ table: Mapped[str] = mapped_column(String, nullable=True)
17
+ table_path: Mapped[str] = mapped_column(String, nullable=True)
18
+ access_granularity: Mapped[str] = mapped_column(String, nullable=True)
19
+
20
+ __mapper_args__ = {
21
+ "polymorphic_identity": NAME,
22
+ }
@@ -0,0 +1,151 @@
1
+ import json
2
+ from typing import ClassVar, Optional, Self
3
+
4
+ from fastapi import HTTPException, status
5
+ from pydantic import model_validator
6
+ from sqlalchemy.orm import Session
7
+
8
+ from app.configuration.environments.platform_configurations.service import (
9
+ EnvironmentPlatformConfigurationService,
10
+ )
11
+ from app.configuration.environments.platform_service_configurations.schemas import (
12
+ DatabricksConfig,
13
+ )
14
+ from app.data_products.model import DataProduct as DataProductModel
15
+ from app.technical_asset_configuration.base_schema import (
16
+ PluginContext,
17
+ FieldDependency,
18
+ PlatformMetadata,
19
+ SelectOption,
20
+ TechnicalAssetPlugin,
21
+ UIElementMetadata,
22
+ UIElementRadio,
23
+ UIElementSelect,
24
+ UIElementString,
25
+ )
26
+ from portal_plugins.databricks.model import (
27
+ NAME,
28
+ )
29
+ from portal_plugins.databricks.model import (
30
+ DatabricksTechnicalAssetConfiguration as DatabricksTechnicalAssetConfigurationModel,
31
+ )
32
+ from app.technical_asset_configuration.enums import AccessGranularity, UIElementType
33
+
34
+
35
+ class DatabricksTechnicalAssetConfiguration(TechnicalAssetPlugin):
36
+ name: ClassVar[str] = NAME
37
+ version: ClassVar[str] = "1.0"
38
+
39
+ catalog: str
40
+ schema: str = ""
41
+ table: str = "*"
42
+ bucket_identifier: str = ""
43
+ catalog_path: str = ""
44
+ table_path: str = ""
45
+ access_granularity: AccessGranularity
46
+
47
+ _platform_metadata = PlatformMetadata(
48
+ result_string_template="{catalog}.{schema}.{table}",
49
+ display_name="Databricks",
50
+ icon_name="databricks-logo.svg",
51
+ icon_package="portal_plugins.databricks",
52
+ platform_key="databricks",
53
+ parent_platform=None,
54
+ result_label="Resulting table",
55
+ result_tooltip="The table you can access through this technical asset",
56
+ detailed_name="Schema",
57
+ )
58
+
59
+ class Meta:
60
+ orm_model = DatabricksTechnicalAssetConfigurationModel
61
+
62
+ @model_validator(mode="after")
63
+ def validate_paths(self) -> Self:
64
+ if not self.catalog_path:
65
+ self.catalog_path = self.catalog
66
+ if not self.table_path:
67
+ self.table_path = self.table
68
+ if self.access_granularity == AccessGranularity.Schema:
69
+ self.table = "*"
70
+ return self
71
+
72
+ def validate_configuration(self, context: PluginContext, db: Session):
73
+ # If product aligned
74
+ if not self.catalog.startswith(context.data_product.namespace):
75
+ raise ValueError("Invalid catalog specified")
76
+
77
+ def get_configuration(
78
+ self, configs: list[DatabricksConfig]
79
+ ) -> Optional[DatabricksConfig]:
80
+ return next(
81
+ (config for config in configs if config.identifier == self.catalog), None
82
+ )
83
+
84
+ @classmethod
85
+ def get_url(cls, id, db, actor, environment=None) -> str:
86
+ platform_config = EnvironmentPlatformConfigurationService(
87
+ db
88
+ ).get_env_platform_config(environment, "Databricks")
89
+ data_product = db.get(DataProductModel, id)
90
+ config = json.loads(platform_config)["workspace_urls"]
91
+ if str(data_product.domain_id) not in config:
92
+ raise HTTPException(
93
+ status_code=status.HTTP_404_NOT_FOUND,
94
+ detail=(
95
+ f"Workspace not configured for domain {data_product.domain.name}"
96
+ ),
97
+ )
98
+ return config[str(data_product.domain_id)]
99
+
100
+ @classmethod
101
+ def get_ui_metadata(cls, db: Session) -> list[UIElementMetadata]:
102
+ base_metadata = super().get_ui_metadata(db)
103
+ base_metadata += [
104
+ UIElementMetadata(
105
+ name="catalog",
106
+ label="Catalog",
107
+ type=UIElementType.Select,
108
+ required=True,
109
+ use_namespace_when_not_source_aligned=True,
110
+ select=UIElementSelect(options=cls.get_platform_options(db)),
111
+ ),
112
+ UIElementMetadata(
113
+ name="schema",
114
+ type=UIElementType.String,
115
+ label="Schema",
116
+ tooltip="The name of the schema to give write access to. Defaults to data product namespace",
117
+ required=True,
118
+ ),
119
+ UIElementMetadata(
120
+ name="access_granularity",
121
+ label="Access granularity",
122
+ type=UIElementType.Radio,
123
+ tooltip="Give write access to the entire schema or a single table",
124
+ required=True,
125
+ radio=UIElementRadio(
126
+ initial_value=AccessGranularity.Schema,
127
+ options=[
128
+ SelectOption(
129
+ label="Schema level", value=AccessGranularity.Schema
130
+ ),
131
+ SelectOption(
132
+ label="Table level", value=AccessGranularity.Table
133
+ ),
134
+ ],
135
+ ),
136
+ ),
137
+ UIElementMetadata(
138
+ name="table",
139
+ label="Table",
140
+ type=UIElementType.String,
141
+ tooltip="The name of the table to give write access to",
142
+ required=True,
143
+ string=UIElementString(initial_value="*"),
144
+ depends_on=[
145
+ FieldDependency(
146
+ field_name="access_granularity", value=AccessGranularity.Table
147
+ )
148
+ ],
149
+ ),
150
+ ]
151
+ return base_metadata
@@ -0,0 +1,41 @@
1
+ from typing import Sequence, Union
2
+
3
+ import sqlalchemy as sa
4
+ from alembic import op
5
+ from sqlalchemy.dialects import postgresql
6
+
7
+ from app.shared.model import utcnow
8
+
9
+ revision: str = "databricks_0001_baseline"
10
+ down_revision: Union[str, None] = None
11
+ branch_labels: Union[str, Sequence[str], None] = None
12
+ depends_on: Union[str, Sequence[str], None] = "5477aa0d86f0"
13
+
14
+ TABLE = "databricks_technical_asset_configurations"
15
+
16
+
17
+ def upgrade() -> None:
18
+ op.create_table(
19
+ TABLE,
20
+ sa.Column(
21
+ "id",
22
+ postgresql.UUID(as_uuid=True),
23
+ sa.ForeignKey("data_output_configurations.id", ondelete="CASCADE"),
24
+ primary_key=True,
25
+ ),
26
+ sa.Column("catalog", sa.String(), nullable=True),
27
+ sa.Column("schema", sa.String(), nullable=True),
28
+ sa.Column("bucket_identifier", sa.String(), nullable=True),
29
+ sa.Column("catalog_path", sa.String(), nullable=True),
30
+ sa.Column("table", sa.String(), nullable=True),
31
+ sa.Column("table_path", sa.String(), nullable=True),
32
+ sa.Column("access_granularity", sa.String(), nullable=True),
33
+ sa.Column("created_on", sa.DateTime(timezone=False), server_default=utcnow()),
34
+ sa.Column("updated_on", sa.DateTime(timezone=False), onupdate=utcnow()),
35
+ sa.Column("deleted_at", sa.DateTime(timezone=False), nullable=True),
36
+ if_not_exists=True,
37
+ )
38
+
39
+
40
+ def downgrade() -> None:
41
+ op.drop_table(TABLE, if_exists=True)
@@ -0,0 +1,3 @@
1
+ <svg width="1em" height="1em" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z" fill="#090B0B"/>
3
+ </svg>