Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/source/user_guide/pipeline-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,9 @@ Examples (CLI):
#### URL component catalog

The URL component catalog connector provides access to components that are stored on the web:
- The specified URL must be retrievable using an anonymous HTTP `GET` request.
- You can specify one or more URLs.
- You can specify one or more URL resources.
- The specified URLs must be retrievable using an HTTP `GET` request.
- If the resources are secured, provide credentials, such as a user id and password or API key.

Examples (GUI):
- `https://raw.githubusercontent.com/elyra-ai/examples/main/component-catalog-connectors/kfp-example-components-connector/kfp_examples_connector/resources/filter_text_using_shell_and_grep.yaml`
Expand Down
24 changes: 23 additions & 1 deletion elyra/metadata/schemas/url-catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,29 @@
"format": "uri"
},
"uihints": {
"category": "Configuration"
"category": "Configuration",
"items": {
"ui:placeholder": "https://host:port/path/component_file"
}
}
},
"auth_id": {
"title": "User Id",
"description": "User id that has read access for the specified URL resources",
"type": "string",
"minLength": 1,
"uihints": {
"category": "Source credentials"
}
},
"auth_password": {
"title": "Password",
"description": "Password or API key for the specified user id",
"type": "string",
"minLength": 1,
"uihints": {
"ui:field": "password",
"category": "Source credentials"
}
}
},
Expand Down
39 changes: 35 additions & 4 deletions elyra/pipeline/catalog_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@

from deprecation import deprecated
from jupyter_core.paths import ENV_JUPYTER_PATH
import requests
from requests import get
from requests.auth import HTTPBasicAuth
from traitlets.config import LoggingConfigurable
from traitlets.traitlets import default
from traitlets.traitlets import Integer
Expand Down Expand Up @@ -607,6 +608,8 @@ class UrlComponentCatalogConnector(ComponentCatalogConnector):
Read a singular component definition from a url
"""

REQUEST_TIMEOUT = 30

def get_catalog_entries(self, catalog_metadata: Dict[str, Any]) -> List[Dict[str, Any]]:
"""
Returns a list of catalog_entry_data dictionary instances, one per entry in the given catalog.
Expand All @@ -633,13 +636,41 @@ def get_entry_data(
individual catalog entries
"""
url = catalog_entry_data.get("url")

# determine whether authentication needs to be performed
auth_id = catalog_metadata.get("auth_id")
auth_password = catalog_metadata.get("auth_password")
if auth_id and auth_password:
auth = HTTPBasicAuth(auth_id, auth_password)
elif auth_id or auth_password:
self.log.error(
f"Error. URL catalog connector '{catalog_metadata.get('display_name')}' "
"is not configured properly. "
"Authentication requires a user id and password or API key."
)
return None
else:
auth = None

try:
res = requests.get(url)
res = get(
url,
timeout=UrlComponentCatalogConnector.REQUEST_TIMEOUT,
allow_redirects=True,
auth=auth,
)
except Exception as e:
self.log.warning(f"Failed to connect to URL for component: {url}: {e}")
self.log.error(
f"Error. The URL catalog connector '{catalog_metadata.get('display_name')}' "
f"encountered an issue downloading '{url}': {e} "
)
else:
if res.status_code != HTTPStatus.OK:
self.log.warning(f"Invalid location for component: {url} (HTTP code {res.status_code})")
self.log.error(
f"Error. The URL catalog connector '{catalog_metadata.get('display_name')}' "
f"encountered an issue downloading '{url}'. "
f"HTTP response code: {res.status_code}"
)
else:
return EntryData(definition=res.text)

Expand Down