Skip to content

Commit

Permalink
#45 Added a helper getting the connection parameters (#46)
Browse files Browse the repository at this point in the history
* #45 Added get_connection_params

* #45 Got mypy shut up

* #45 Fixed the scope of the database_name fixture

* #45 Removed the hack from the api __init__.py

* #45 Moved the connection tests to a separate file

* Prepare release 0.6.0
  • Loading branch information
ahsimb authored May 22, 2024
1 parent 8d2a507 commit 7d4a78a
Show file tree
Hide file tree
Showing 10 changed files with 597 additions and 261 deletions.
2 changes: 2 additions & 0 deletions doc/changes/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changes

* [unreleased](unreleased.md)
* [0.6.0](changes_0.6.0.md)
* [0.5.0](changes_0.5.0.md)
* [0.4.0](changes_0.4.0.md)
* [0.3.0](changes_0.3.0.md)
Expand All @@ -13,6 +14,7 @@
hidden:
---
unreleased
changes_0.6.0
changes_0.5.0
changes_0.4.0
changes_0.3.0
Expand Down
2 changes: 1 addition & 1 deletion doc/changes/changes_0.5.0.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# 0.5.0 - 2024-05-16

# Bugfixes
## Bugfixes

* Fixed typehint for optional argument to enable usage with python 3.9
13 changes: 13 additions & 0 deletions doc/changes/changes_0.6.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 0.6.0 - 2024-05-21

## Feature

* #45 Added a helper function to assemble DB connection parameters.

## Bugfixes

* #44 Fixed the return value of the operational_saas_database_id fixture.

## Refactoring

* #19: Removed slack notifications for events other than `schedule`
6 changes: 0 additions & 6 deletions doc/changes/unreleased.md
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
# Unreleased

This release removes slack notifications for events other than `schedule`.

## Refactoring

* #19: Removed slack notifications for events other than `schedule`
73 changes: 71 additions & 2 deletions exasol/saas/client/api_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import time

from typing import Iterable, List, Optional
from typing import Iterable, List, Optional, Any
from contextlib import contextmanager
import datetime as dt
from datetime import datetime, timedelta
Expand Down Expand Up @@ -32,6 +32,7 @@
add_allowed_ip,
delete_allowed_ip,
)
from exasol.saas.client.openapi.types import UNSET


LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -76,6 +77,75 @@ def create_saas_client(
)


def _get_database_id(
account_id: str,
client: openapi.AuthenticatedClient,
database_name: str,
) -> str:
"""
Finds the database id, given an optional database name. If the name is not
provided returns an id of any non-deleted database. The latter option may be
useful for testing.
"""
dbs = list_databases.sync(account_id, client=client)
dbs = list(filter(lambda db: (db.name == database_name) and # type: ignore
(db.deleted_at is UNSET) and # type: ignore
(db.deleted_by is UNSET), dbs)) # type: ignore
if not dbs:
raise RuntimeError(f'SaaS database {database_name} was not found.')
return dbs[0].id


def get_connection_params(
host: str,
account_id: str,
pat: str,
database_id: str | None = None,
database_name: str | None = None,
) -> dict[str, Any]:
"""
Gets the database connection parameters, such as those required by pyexasol:
- dns
- user
- password.
Returns the parameters in a dictionary that can be used as kwargs when
creating a connection, like in the code below:
connection_params = get_connection_params(...)
connection = pyexasol.connect(**connection_params)
Args:
host: SaaS service URL.
account_id: User account ID
pat: Personal Access Token.
database_id: Database ID, id known.
database_name: Database name, in case the id is unknown.
"""

with create_saas_client(host, pat) as client:
if not database_id:
if not database_name:
raise ValueError(('To get SaaS connection parameters, '
'either database name or database id must be provided.'))
database_id = _get_database_id(account_id, client, database_name=database_name)
clusters = list_clusters.sync(account_id,
database_id,
client=client)
cluster_id = next(filter(lambda cl: cl.main_cluster, clusters)).id # type: ignore
connections = get_cluster_connection.sync(account_id,
database_id,
cluster_id,
client=client)
if connections is None:
raise RuntimeError('Failed to get the SaaS connection data.')
connection_params = {
'dsn': f'{connections.dns}:{connections.port}',
'user': connections.db_username,
'password': pat
}
return connection_params


class OpenApiAccess:
"""
This class is meant to be used only in the context of the API
Expand Down Expand Up @@ -190,7 +260,6 @@ def poll_status():
if poll_status() not in success:
raise DatabaseStartupFailure()


def clusters(
self,
database_id: str,
Expand Down
709 changes: 461 additions & 248 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pytest = "^7.1.1"
pytest-mock = "^3.7.0"
exasol-toolbox = "^0.9.0"
openapi-python-client = "^0.19.1"
pyexasol = ">=0.25.0"


[tool.pytest.ini_options]
Expand Down
14 changes: 12 additions & 2 deletions test/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,24 @@ def saas_database(api_access, database_name) -> openapi.models.database.Database
def operational_saas_database_id(api_access, database_name) -> str:
with api_access.database(database_name) as db:
api_access.wait_until_running(db.id)
yield db
yield db.id


@pytest.fixture(scope="session")
def project_short_tag():
return os.environ.get("PROJECT_SHORT_TAG")


@pytest.fixture
@pytest.fixture(scope="session")
def database_name(project_short_tag):
return timestamp_name(project_short_tag)


@pytest.fixture(scope="session")
def allow_connection(api_access) -> None:
"""
This fixture allows communication with the SaaS from the ci
for the duration of the test.
"""
with api_access.allowed_ip():
yield
34 changes: 34 additions & 0 deletions test/integration/connection_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import pyexasol
from exasol.saas.client.api_access import get_connection_params


def test_get_connection_params_with_id(saas_host, saas_pat, saas_account_id,
operational_saas_database_id,
allow_connection):
"""
This integration test checks that opening a pyexasol connection to a SaaS DB with
known id and executing a query works.
"""
connection_params = get_connection_params(host=saas_host,
account_id=saas_account_id,
pat=saas_pat,
database_id=operational_saas_database_id)
with pyexasol.connect(**connection_params) as pyconn:
result = pyconn.execute('SELECT 1;').fetchall()
assert result == [(1,)]


def test_get_connection_params_with_name(saas_host, saas_pat, saas_account_id,
operational_saas_database_id, database_name,
allow_connection):
"""
This integration test checks that opening a pyexasol connection to a SaaS DB with
known name and executing a query works.
"""
connection_params = get_connection_params(host=saas_host,
account_id=saas_account_id,
pat=saas_pat,
database_name=database_name)
with pyexasol.connect(**connection_params) as pyconn:
result = pyconn.execute('SELECT 1;').fetchall()
assert result == [(1,)]
4 changes: 2 additions & 2 deletions test/integration/databases_test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import pytest

from exasol.saas.client import openapi, PROMISING_STATES
from exasol.saas.client import PROMISING_STATES
from tenacity import RetryError
from datetime import datetime, timedelta

from exasol.saas.client.api_access import wait_for_delete_clearance
from exasol.saas.client.api_access import wait_for_delete_clearance, get_connection_params


def test_lifecycle(api_access, database_name):
Expand Down

0 comments on commit 7d4a78a

Please sign in to comment.