|
4 | 4 | import shutil |
5 | 5 | import sys |
6 | 6 | import tempfile |
7 | | -from json import JSONDecodeError |
8 | 7 | from pathlib import Path |
9 | 8 | from typing import TYPE_CHECKING, cast |
10 | 9 |
|
11 | 10 | import requests |
12 | 11 | import yaml |
| 12 | +from requests import HTTPError |
13 | 13 | from rich import print |
14 | 14 |
|
15 | 15 | from airbyte import exceptions as exc |
|
25 | 25 | from airbyte._executors.base import Executor |
26 | 26 |
|
27 | 27 |
|
| 28 | +def _try_get_source_manifest(source_name: str, manifest_url: str | None) -> dict: |
| 29 | + """Try to get a source manifest from a URL. |
| 30 | +
|
| 31 | + If the URL is not provided, we'll try a couple of default URLs. |
| 32 | + We can remove/refactor this once manifests are available in GCS connector registry. |
| 33 | + """ |
| 34 | + if manifest_url: |
| 35 | + response = requests.get(url=manifest_url) |
| 36 | + response.raise_for_status() # Raise HTTPError exception if the download failed |
| 37 | + try: |
| 38 | + return cast(dict, yaml.safe_load(response.text)) |
| 39 | + except yaml.YAMLError as ex: |
| 40 | + raise exc.AirbyteConnectorInstallationError( |
| 41 | + message="Failed to parse the connector manifest YAML.", |
| 42 | + connector_name=source_name, |
| 43 | + context={ |
| 44 | + "manifest_url": manifest_url, |
| 45 | + }, |
| 46 | + ) from ex |
| 47 | + |
| 48 | + # No manifest URL was provided. We'll try a couple of default URLs. |
| 49 | + |
| 50 | + try: |
| 51 | + # First try the new URL format (language='manifest-only'): |
| 52 | + result_1 = _try_get_source_manifest( |
| 53 | + source_name=source_name, |
| 54 | + manifest_url=( |
| 55 | + f"https://raw.githubusercontent.com/airbytehq/airbyte/master/airbyte-integrations" |
| 56 | + f"/connectors/{source_name}/manifest.yaml" |
| 57 | + ), |
| 58 | + ) |
| 59 | + except HTTPError as ex_1: |
| 60 | + # If the new URL path was not found, try the old URL format (language='low-code'): |
| 61 | + try: |
| 62 | + result_2 = _try_get_source_manifest( |
| 63 | + source_name=source_name, |
| 64 | + manifest_url=( |
| 65 | + f"https://raw.githubusercontent.com/airbytehq/airbyte/master/airbyte-integrations" |
| 66 | + f"/connectors/{source_name}/{source_name.replace('-', '_')}/manifest.yaml" |
| 67 | + ), |
| 68 | + ) |
| 69 | + except HTTPError: |
| 70 | + # Raise the first exception, since that represents the new default URL |
| 71 | + raise ex_1 from None |
| 72 | + else: |
| 73 | + # Old URL path was found (no exceptions raised). |
| 74 | + return result_2 |
| 75 | + else: |
| 76 | + # New URL path was found (no exceptions raised). |
| 77 | + return result_1 |
| 78 | + |
| 79 | + |
28 | 80 | def get_connector_executor( # noqa: PLR0912, PLR0913, PLR0915 # Too complex |
29 | 81 | name: str, |
30 | 82 | *, |
@@ -143,49 +195,23 @@ def get_connector_executor( # noqa: PLR0912, PLR0913, PLR0915 # Too complex |
143 | 195 | ) |
144 | 196 |
|
145 | 197 | if source_manifest: |
146 | | - if source_manifest is True: |
147 | | - # Auto-set the manifest to a valid http address URL string |
148 | | - source_manifest = ( |
149 | | - "https://raw.githubusercontent.com/airbytehq/airbyte/master/airbyte-integrations" |
150 | | - f"/connectors/{name}/{name.replace('-', '_')}/manifest.yaml" |
| 198 | + if isinstance(source_manifest, (dict, Path)): |
| 199 | + return DeclarativeExecutor( |
| 200 | + name=name, |
| 201 | + manifest=source_manifest, |
151 | 202 | ) |
152 | | - if isinstance(source_manifest, str): |
153 | | - print("Installing connector from YAML manifest:", source_manifest) |
154 | | - # Download the manifest file |
155 | | - response = requests.get(url=source_manifest) |
156 | | - response.raise_for_status() # Raise an exception if the download failed |
157 | | - |
158 | | - if "class_name:" in response.text: |
159 | | - raise exc.AirbyteConnectorInstallationError( |
160 | | - message=( |
161 | | - "The provided manifest requires additional code files (`class_name` key " |
162 | | - "detected). This feature is not compatible with the declarative YAML " |
163 | | - "executor. To use this executor, please try again with the Python " |
164 | | - "executor." |
165 | | - ), |
166 | | - connector_name=name, |
167 | | - context={ |
168 | | - "manifest_url": source_manifest, |
169 | | - }, |
170 | | - ) |
171 | 203 |
|
172 | | - try: |
173 | | - source_manifest = cast(dict, yaml.safe_load(response.text)) |
174 | | - except JSONDecodeError as ex: |
175 | | - raise exc.AirbyteConnectorInstallationError( |
176 | | - connector_name=name, |
177 | | - context={ |
178 | | - "manifest_url": source_manifest, |
179 | | - }, |
180 | | - ) from ex |
181 | | - |
182 | | - if isinstance(source_manifest, Path): |
183 | | - source_manifest = cast(dict, yaml.safe_load(source_manifest.read_text())) |
184 | | - |
185 | | - # Source manifest is a dict at this point |
186 | | - return DeclarativeExecutor( |
187 | | - manifest=source_manifest, |
188 | | - ) |
| 204 | + if isinstance(source_manifest, (str, bool)): |
| 205 | + # Source manifest is either a URL or a boolean (True) |
| 206 | + source_manifest = _try_get_source_manifest( |
| 207 | + source_name=name, |
| 208 | + manifest_url=None if source_manifest is True else source_manifest, |
| 209 | + ) |
| 210 | + |
| 211 | + return DeclarativeExecutor( |
| 212 | + name=name, |
| 213 | + manifest=source_manifest, |
| 214 | + ) |
189 | 215 |
|
190 | 216 | # else: we are installing a connector in a Python virtual environment: |
191 | 217 |
|
|
0 commit comments