diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 66b5369e..84394e94 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest services: stargate: - image: stargateio/stargate-dse-68:v1.0.22 + image: stargateio/stargate-dse-68:v1.0.26 env: CLUSTER_NAME: stargate CLUSTER_VERSION: 6.8 diff --git a/astrapy/client.py b/astrapy/client.py new file mode 100644 index 00000000..66d97c6f --- /dev/null +++ b/astrapy/client.py @@ -0,0 +1,40 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from astrapy.collections import create_client +from astrapy.endpoints.rest import AstraRest +from astrapy.endpoints.schemas import AstraSchemas +from astrapy.endpoints.ops import AstraOps +import logging + +logger = logging.getLogger(__name__) + + +class AstraClient(): + def __init__(self, astra_collections_client=None): + self.collections = astra_collections_client + self._rest_client = astra_collections_client.astra_client + self.rest = AstraRest(self._rest_client) + self.ops = AstraOps(self._rest_client) + self.schemas = AstraSchemas(self._rest_client) + + +def create_astra_client(astra_database_id=None, + astra_database_region=None, + astra_application_token=None, + debug=False): + astra_collections_client = create_client(astra_database_id=astra_database_id, + astra_database_region=astra_database_region, + astra_application_token=astra_application_token) + return AstraClient(astra_collections_client=astra_collections_client) diff --git a/astrapy/endpoints/__init__.py b/astrapy/endpoints/__init__.py new file mode 100644 index 00000000..33284e0a --- /dev/null +++ b/astrapy/endpoints/__init__.py @@ -0,0 +1,13 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. \ No newline at end of file diff --git a/astrapy/endpoints/ops.py b/astrapy/endpoints/ops.py new file mode 100644 index 00000000..6feb2e3a --- /dev/null +++ b/astrapy/endpoints/ops.py @@ -0,0 +1,127 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from astrapy.rest import http_methods + +PATH_PREFIX = "/v2" + + +class AstraOps(): + + def __init__(self, client=None): + self.client = client + + def get_databases(self): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/databases") + + def create_database(self, database_definition=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/databases", + json_data=database_definition) + + def get_database(self, database=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/databases/{database}") + + def create_keyspace(self, database="", keyspace=""): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/databases/{database}/keyspaces/{keyspace}") + + def terminate_database(self, database=""): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/databases/{database}/terminate") + + def park_database(self, database=""): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/databases/{database}/park") + + def unpark_database(self, database=""): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/databases/{database}/unpark") + + def resize_database(self, database="", options=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/databases/{database}/resize", + json_data=options) + + def reset_database_password(self, database="", options=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/databases/{database}/resetPassword", + json_data=options) + + def get_available_regions(self): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/availableRegions") + + def get_roles(self): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/organizations/roles") + + def create_role(self, role_definition=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/organizations/roles", + json_data=role_definition) + + def get_role(self, role=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/organizations/roles/{role}") + + def update_role(self, role="", role_definition=None): + return self.client.request(method=http_methods.PUT, + path=f"{PATH_PREFIX}/organizations/roles/{role}", + json_data=role_definition) + + def delete_role(self, role=""): + return self.client.request(method=http_methods.DELETE, + path=f"{PATH_PREFIX}/organizations/roles/{role}") + + def invite_user(self, user_definition=None): + return self.client.request(method=http_methods.PUT, + path=f"{PATH_PREFIX}/organizations/users", + json_data=user_definition) + + def get_users(self): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/organizations/users") + + def get_user(self, user=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/organizations/users/{user}") + + def remove_user(self, user=""): + return self.client.request(method=http_methods.DELETE, + path=f"{PATH_PREFIX}/organizations/users/{user}") + + def update_user_roles(self, user="", roles=None): + return self.client.request(method=http_methods.PUT, + path=f"{PATH_PREFIX}/organizations/users/{user}/roles", + json_data=roles) + + def get_clients(self): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/clientIdSecrets") + + def create_token(self, roles=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/clientIdSecrets", + json_data=roles) + + def delete_token(self, token=""): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/clientIdSecret/{token}") + + def get_organization(self): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/currentOrg") diff --git a/astrapy/endpoints/rest.py b/astrapy/endpoints/rest.py new file mode 100644 index 00000000..10b20a37 --- /dev/null +++ b/astrapy/endpoints/rest.py @@ -0,0 +1,58 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from astrapy.rest import http_methods + +DEFAULT_PAGE_SIZE = 20 +PATH_PREFIX = "/api/rest/v2/keyspaces" + + +class AstraRest(): + + def __init__(self, client=None): + self.client = client + + def search_table(self, keyspace="", table="", query=None, options=None): + options = {} if options is None else options + request_params = {"where": json.dumps( + query), "page-size": DEFAULT_PAGE_SIZE} + request_params.update(options) + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/{keyspace}/{table}", + url_params=request_params) + + def add_row(self, keyspace="", table="", row=""): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/{keyspace}/{table}", + json_options=row) + + def get_row(self, keyspace="", table="", key_path="", options=None): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/{keyspace}/{table}/{key_path}", + json_data=options) + + def replace_row(self, keyspace="", table="", key_path="", row=""): + return self.client.request(method=http_methods.PUT, + path=f"{PATH_PREFIX}/{keyspace}/{table}/{key_path}", + json_data=row) + + def update_row(self, keyspace="", table="", key_path="", row=""): + return self.client.request(method=http_methods.PATCH, + path=f"{PATH_PREFIX}/{keyspace}/{table}/{key_path}", + json_data=row) + + def delete_row(self, keyspace="", table="", key_path=""): + return self.client.request(method=http_methods.DELETE, + path=f"{PATH_PREFIX}/{keyspace}/{table}/{key_path}") diff --git a/astrapy/endpoints/schemas.py b/astrapy/endpoints/schemas.py new file mode 100644 index 00000000..d836b6bb --- /dev/null +++ b/astrapy/endpoints/schemas.py @@ -0,0 +1,88 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from astrapy.rest import http_methods + +PATH_PREFIX = "/api/rest/v2/schemas" + + +class AstraSchemas(): + + def __init__(self, client=None): + self.client = client + + def get_keyspaces(self): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/keyspaces") + + def get_keyspace(self, keyspace=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}") + + def create_table(self, keyspace="", table_definition=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables", + json_data=table_definition) + + def get_tables(self, keyspace=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables") + + def get_table(self, keyspace="", table=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}") + + def update_table(self, keyspace="", table="", table_definition=None): + return self.client.request(method=http_methods.PUT, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}", + json_data=table_definition) + + def delete_table(self, keyspace="", table=""): + return self.client.request(method=http_methods.DELETE, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}") + + def create_column(self, keyspace="", table="", column_definition=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}", + json_data=column_definition) + + def get_columns(self, keyspace="", table=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}/columns") + + def get_column(self, keyspace="", table="", column=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}/columns/{column}") + + def update_column(self, keyspace="", table="", column="", column_definition=None): + return self.client.request(method=http_methods.PUT, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}/columns/{column}", + json_data=column_definition) + + def delete_column(self, keyspace="", table="", column=""): + return self.client.request(method=http_methods.DELETE, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}/columns/{column}") + + def get_indexes(self, keyspace="", table=""): + return self.client.request(method=http_methods.GET, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}/indexes") + + def create_index(self, keyspace="", table="", index_definition=None): + return self.client.request(method=http_methods.POST, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}/indexes", + json_data=index_definition) + + def delete_index(self, keyspace="", table="", index=""): + return self.client.request(method=http_methods.DELETE, + path=f"{PATH_PREFIX}/keyspaces/{keyspace}/tables/{table}/indexes/{index}") diff --git a/tests/astrapy/test_client.py b/tests/astrapy/test_client.py new file mode 100644 index 00000000..b40a17fc --- /dev/null +++ b/tests/astrapy/test_client.py @@ -0,0 +1,38 @@ +# Copyright DataStax, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from astrapy.client import create_astra_client, AstraClient +import pytest +import logging +import os +import uuid + +logger = logging.getLogger(__name__) + +ASTRA_DB_ID = os.environ.get('ASTRA_DB_ID') +ASTRA_DB_REGION = os.environ.get('ASTRA_DB_REGION') +ASTRA_DB_APPLICATION_TOKEN = os.environ.get('ASTRA_DB_APPLICATION_TOKEN') +ASTRA_DB_KEYSPACE = os.environ.get('ASTRA_DB_KEYSPACE') + + +@pytest.fixture +def astra_client(): + return create_astra_client(astra_database_id=ASTRA_DB_ID, + astra_database_region=ASTRA_DB_REGION, + astra_application_token=ASTRA_DB_APPLICATION_TOKEN) + + +@pytest.mark.it('should initialize an AstraDB REST Client') +def test_connect(astra_client): + assert type(astra_client) is AstraClient