|
1 | | -from typing import List |
| 1 | +from typing import Dict |
| 2 | +from typing import Optional |
2 | 3 |
|
| 4 | +from lib.core.conditions import Condition |
| 5 | +from lib.core.conditions import CONDITION_EQ as EQ |
3 | 6 | from lib.core.entities import FolderEntity |
4 | 7 | from lib.core.entities import IntegrationEntity |
5 | 8 | from lib.core.entities import ProjectEntity |
| 9 | +from lib.core.entities.integrations import IntegrationTypeEnum |
| 10 | +from lib.core.enums import ProjectType |
6 | 11 | from lib.core.exceptions import AppException |
7 | 12 | from lib.core.reporter import Reporter |
8 | 13 | from lib.core.response import Response |
@@ -33,46 +38,133 @@ def __init__( |
33 | 38 | service_provider: BaseServiceProvider, |
34 | 39 | integration: IntegrationEntity, |
35 | 40 | folder_path: str = None, |
| 41 | + query: Optional[str] = None, |
| 42 | + item_name_column: Optional[str] = None, |
| 43 | + custom_item_name: Optional[str] = None, |
| 44 | + component_mapping: Optional[Dict[str, str]] = None, |
36 | 45 | ): |
37 | | - |
38 | 46 | super().__init__(reporter) |
39 | 47 | self._project = project |
40 | 48 | self._folder = folder |
41 | 49 | self._integration = integration |
42 | 50 | self._service_provider = service_provider |
43 | 51 | self._folder_path = folder_path |
| 52 | + self._query = query |
| 53 | + self._item_name_column = item_name_column |
| 54 | + self._custom_item_name = custom_item_name |
| 55 | + self._component_mapping = component_mapping |
44 | 56 |
|
45 | 57 | @property |
46 | 58 | def _upload_path(self): |
47 | 59 | return f"{self._project.name}{f'/{self._folder.name}' if self._folder.name != 'root' else ''}" |
48 | 60 |
|
49 | 61 | def execute(self) -> Response: |
50 | | - integrations: List[ |
51 | | - IntegrationEntity |
52 | | - ] = self._service_provider.integrations.list().data.integrations |
53 | | - integration_name_lower = self._integration.name.lower() |
54 | | - integration = next( |
55 | | - (i for i in integrations if i.name.lower() == integration_name_lower), None |
56 | | - ) |
57 | | - if integration: |
58 | | - self.reporter.log_info( |
59 | | - "Attaching file(s) from " |
60 | | - f"{integration.root}{f'/{self._folder_path}' if self._folder_path else ''} " |
61 | | - f"to {self._upload_path}. This may take some time." |
| 62 | + # TODO add support in next iterations |
| 63 | + if self._integration.type == IntegrationTypeEnum.SNOWFLAKE: |
| 64 | + raise AppException( |
| 65 | + "Attaching items is not supported with Snowflake integration." |
62 | 66 | ) |
63 | | - attached = self._service_provider.integrations.attach_items( |
64 | | - project=self._project, |
65 | | - folder=self._folder, |
66 | | - integration=integration, |
67 | | - folder_name=self._folder_path, |
| 67 | + |
| 68 | + options = {} # using only for Databricks and Snowflake |
| 69 | + multimodal_integrations = [ |
| 70 | + IntegrationTypeEnum.DATABRICKS, |
| 71 | + IntegrationTypeEnum.SNOWFLAKE, |
| 72 | + ] |
| 73 | + if self._integration.type in multimodal_integrations: |
| 74 | + if self._project.type != ProjectType.MULTIMODAL: |
| 75 | + raise AppException( |
| 76 | + f"{self._integration.name} integration is supported only for Multimodal projects." |
| 77 | + ) |
| 78 | + |
| 79 | + if self._item_name_column and self._custom_item_name: |
| 80 | + raise AppException( |
| 81 | + "‘item_name_column and custom_item_name cannot be used simultaneously." |
| 82 | + ) |
| 83 | + |
| 84 | + if not self._item_name_column and not self._custom_item_name: |
| 85 | + raise AppException( |
| 86 | + "Either item_name_column or custom_item_name is required." |
| 87 | + ) |
| 88 | + |
| 89 | + if not all((self._query, self._component_mapping)): |
| 90 | + raise AppException( |
| 91 | + f"{self._integration.name} integration requires both a query and component_mapping." |
| 92 | + ) |
| 93 | + |
| 94 | + category_setting: bool = bool( |
| 95 | + next( |
| 96 | + ( |
| 97 | + setting.value |
| 98 | + for setting in self._service_provider.projects.list_settings( |
| 99 | + self._project |
| 100 | + ).data |
| 101 | + if setting.attribute == "CategorizeItems" |
| 102 | + ), |
| 103 | + None, |
| 104 | + ) |
68 | 105 | ) |
69 | | - if not attached: |
70 | | - self._response.errors = AppException( |
71 | | - f"An error occurred for {self._integration.name}. Please make sure: " |
72 | | - "\n - The bucket exists." |
73 | | - "\n - The connection is valid." |
74 | | - "\n - The path to a specified directory is correct." |
| 106 | + if ( |
| 107 | + not category_setting |
| 108 | + and "_item_category" in self._component_mapping.values() |
| 109 | + ): |
| 110 | + raise AppException( |
| 111 | + "Item Category must be enabled for a project to use _item_category" |
75 | 112 | ) |
76 | | - else: |
77 | | - self._response.errors = AppException("Integration not found.") |
| 113 | + |
| 114 | + item_category_column = next( |
| 115 | + ( |
| 116 | + k |
| 117 | + for k, v in self._component_mapping.items() |
| 118 | + if v == "_item_category" |
| 119 | + ), |
| 120 | + None, |
| 121 | + ) |
| 122 | + if item_category_column: |
| 123 | + self._component_mapping.pop(item_category_column) |
| 124 | + |
| 125 | + sa_components = [ |
| 126 | + c.name.lower() |
| 127 | + for c in self._service_provider.annotation_classes.list( |
| 128 | + condition=Condition("project_id", self._project.id, EQ) |
| 129 | + ).data |
| 130 | + ] |
| 131 | + |
| 132 | + for i in self._component_mapping.values(): |
| 133 | + if i.lower() not in sa_components: |
| 134 | + raise AppException( |
| 135 | + f"Component mapping contains invalid component ID: `{i}`" |
| 136 | + ) |
| 137 | + |
| 138 | + options["query"] = self._query |
| 139 | + options["item_name"] = ( |
| 140 | + self._custom_item_name |
| 141 | + if self._custom_item_name |
| 142 | + else self._item_name_column |
| 143 | + ) |
| 144 | + options["prefix"] = True if self._custom_item_name else False |
| 145 | + options["column_class_map"] = self._component_mapping |
| 146 | + if item_category_column: |
| 147 | + options["item_category"] = item_category_column |
| 148 | + |
| 149 | + self.reporter.log_info( |
| 150 | + "Attaching file(s) from " |
| 151 | + f"{self._integration.root}{f'/{self._folder_path}' if self._folder_path else ''} " |
| 152 | + f"to {self._upload_path}. This may take some time." |
| 153 | + ) |
| 154 | + attache_response = self._service_provider.integrations.attach_items( |
| 155 | + project=self._project, |
| 156 | + folder=self._folder, |
| 157 | + integration=self._integration, |
| 158 | + folder_name=self._folder_path |
| 159 | + if self._integration.type not in multimodal_integrations |
| 160 | + else None, |
| 161 | + options=options if options else None, |
| 162 | + ) |
| 163 | + if not attache_response.ok: |
| 164 | + self._response.errors = AppException( |
| 165 | + f"An error occurred for {self._integration.name}. Please make sure: " |
| 166 | + "\n - The bucket exists." |
| 167 | + "\n - The connection is valid." |
| 168 | + "\n - The path to a specified directory is correct." |
| 169 | + ) |
78 | 170 | return self._response |
0 commit comments