Skip to content

Commit

Permalink
Merge pull request #3309 from mathesar-foundation/connections_model
Browse files Browse the repository at this point in the history
Connections API
  • Loading branch information
mathemancer authored Nov 24, 2023
2 parents de9c611 + af0946e commit 704c125
Show file tree
Hide file tree
Showing 62 changed files with 285 additions and 400 deletions.
2 changes: 1 addition & 1 deletion mathesar/api/db/viewsets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from mathesar.api.db.viewsets.columns import ColumnViewSet # noqa
from mathesar.api.db.viewsets.constraints import ConstraintViewSet # noqa
from mathesar.api.db.viewsets.data_files import DataFileViewSet # noqa
from mathesar.api.db.viewsets.databases import DatabaseViewSet # noqa
from mathesar.api.db.viewsets.databases import ConnectionViewSet # noqa
from mathesar.api.db.viewsets.records import RecordViewSet # noqa
from mathesar.api.db.viewsets.schemas import SchemaViewSet # noqa
from mathesar.api.db.viewsets.table_settings import TableSettingsViewSet # noqa
Expand Down
27 changes: 3 additions & 24 deletions mathesar/api/db/viewsets/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from mathesar.api.dj_filters import DatabaseFilter
from mathesar.api.pagination import DefaultLimitOffsetPagination

from mathesar.api.serializers.databases import DatabaseSerializer
from mathesar.api.serializers.databases import ConnectionSerializer

from db.functions.operations.check_support import get_supported_db_functions
from mathesar.api.serializers.functions import DBFunctionSerializer
Expand All @@ -18,8 +18,8 @@
from mathesar.api.serializers.db_types import DBTypeSerializer


class DatabaseViewSet(AccessViewSetMixin, viewsets.ModelViewSet):
serializer_class = DatabaseSerializer
class ConnectionViewSet(AccessViewSetMixin, viewsets.ModelViewSet):
serializer_class = ConnectionSerializer
pagination_class = DefaultLimitOffsetPagination
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = DatabaseFilter
Expand All @@ -31,27 +31,6 @@ def get_queryset(self):
Database.objects.all().order_by('-created_at')
)

def create(self, request):
serializer = DatabaseSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
credentials = serializer.validated_data
Database.objects.create(
name=credentials['name'],
db_name=credentials['db_name'],
username=credentials['username'],
password=credentials['password'],
host=credentials['host'],
port=credentials['port']
).save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

def partial_update(self, request, pk=None):
db_object = self.get_object()
serializer = DatabaseSerializer(db_object, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)

def destroy(self, request, pk=None):
db_object = self.get_object()
if request.query_params.get('del_msar_schemas'):
Expand Down
35 changes: 6 additions & 29 deletions mathesar/api/serializers/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
from mathesar.api.display_options import DISPLAY_OPTIONS_BY_UI_TYPE
from mathesar.api.exceptions.mixins import MathesarErrorMessageMixin
from mathesar.models.base import Database
from mathesar.api.utils import is_valid_pg_creds
from db.install import install_mathesar


class DatabaseSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):
class ConnectionSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):
supported_types_url = serializers.SerializerMethodField()
nickname = serializers.CharField(source='name')
database = serializers.CharField(source='db_name')

class Meta:
model = Database
fields = ['id', 'name', 'db_name', 'deleted', 'supported_types_url', 'username', 'password', 'host', 'port']
read_only_fields = ['id', 'deleted', 'supported_types_url']
fields = ['id', 'nickname', 'database', 'supported_types_url', 'username', 'password', 'host', 'port']
read_only_fields = ['id', 'supported_types_url']
extra_kwargs = {
'password': {'write_only': True}
}
Expand All @@ -23,33 +23,10 @@ def get_supported_types_url(self, obj):
if isinstance(obj, Database) and not self.partial:
# Only get records if we are serializing an existing table
request = self.context['request']
return request.build_absolute_uri(reverse('database-types', kwargs={'pk': obj.pk}))
return request.build_absolute_uri(reverse('connection-types', kwargs={'pk': obj.pk}))
else:
return None

def validate(self, credentials):
if self.partial:
db_model = self.instance
for attr, value in credentials.items():
setattr(db_model, attr, value)
credentials = {
'db_name': db_model.db_name,
'host': db_model.host,
'username': db_model.username,
'password': db_model.password,
'port': db_model.port
}
if is_valid_pg_creds(credentials):
install_mathesar(
database_name=credentials["db_name"],
hostname=credentials["host"],
username=credentials["username"],
password=credentials["password"],
port=credentials["port"],
skip_confirm=True
)
return super().validate(credentials)


class TypeSerializer(MathesarErrorMessageMixin, serializers.Serializer):
identifier = serializers.CharField()
Expand Down
2 changes: 1 addition & 1 deletion mathesar/api/ui/viewsets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from mathesar.api.ui.viewsets.databases import DatabaseViewSet # noqa
from mathesar.api.ui.viewsets.databases import ConnectionViewSet # noqa
from mathesar.api.ui.viewsets.users import * # noqa
from mathesar.api.ui.viewsets.version import VersionViewSet # noqa
from mathesar.api.ui.viewsets.records import RecordViewSet # noqa
Expand Down
6 changes: 3 additions & 3 deletions mathesar/api/ui/viewsets/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
from mathesar.api.dj_filters import DatabaseFilter
from mathesar.api.pagination import DefaultLimitOffsetPagination

from mathesar.api.serializers.databases import DatabaseSerializer, TypeSerializer
from mathesar.api.serializers.databases import ConnectionSerializer, TypeSerializer
from mathesar.api.serializers.filters import FilterSerializer

from mathesar.filters.base import get_available_filters


class DatabaseViewSet(viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):
serializer_class = DatabaseSerializer
class ConnectionViewSet(viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):
serializer_class = ConnectionSerializer
pagination_class = DefaultLimitOffsetPagination
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = DatabaseFilter
Expand Down
21 changes: 11 additions & 10 deletions mathesar/tests/api/test_database_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,14 @@ def test_database_reflection_delete_table(db_dj_model):

def check_database(database, response_database):
assert database.id == response_database['id']
assert database.name == response_database['name']
assert database.deleted == response_database['deleted']
assert database.name == response_database['nickname']
assert 'supported_types_url' in response_database
assert '/api/ui/v0/databases/' in response_database['supported_types_url']
assert '/api/ui/v0/connections/' in response_database['supported_types_url']
assert response_database['supported_types_url'].endswith('/types/')


def test_database_list(client, db_dj_model):
response = client.get('/api/db/v0/databases/')
response = client.get('/api/db/v0/connections/')
response_data = response.json()
assert response.status_code == 200
assert response_data['count'] == 1
Expand All @@ -120,21 +119,23 @@ def test_database_list_permissions(FUN_create_dj_db, get_uid, client, client_bob
db3 = FUN_create_dj_db(get_uid())
DatabaseRole.objects.create(user=user_bob, database=db3, role='editor')

response = client_bob.get('/api/db/v0/databases/')
response = client_bob.get('/api/db/v0/connections/')
response_data = response.json()
assert response.status_code == 200
assert response_data['count'] == 3

response = client_alice.get('/api/db/v0/databases/')
response = client_alice.get('/api/db/v0/connections/')
response_data = response.json()
assert response.status_code == 200
assert response_data['count'] == 2


def test_database_list_deleted(client, db_dj_model):
# Note that there is no longer a distinction between "deleted" and undeleted
# connections in the API.
_remove_db(db_dj_model.name)
cache.clear()
response = client.get('/api/db/v0/databases/')
response = client.get('/api/db/v0/connections/')
response_data = response.json()
assert response.status_code == 200
assert response_data['count'] == 1
Expand All @@ -143,7 +144,7 @@ def test_database_list_deleted(client, db_dj_model):


def test_database_detail(client, db_dj_model):
response = client.get(f'/api/db/v0/databases/{db_dj_model.id}/')
response = client.get(f'/api/db/v0/connections/{db_dj_model.id}/')
response_database = response.json()

assert response.status_code == 200
Expand All @@ -154,8 +155,8 @@ def test_database_detail_permissions(FUN_create_dj_db, get_uid, client_bob, clie
db1 = FUN_create_dj_db(get_uid())
DatabaseRole.objects.create(user=user_bob, database=db1, role='viewer')

response = client_bob.get(f'/api/db/v0/databases/{db1.id}/')
response = client_bob.get(f'/api/db/v0/connections/{db1.id}/')
assert response.status_code == 200

response = client_alice.get(f'/api/db/v0/databases/{db1.id}/')
response = client_alice.get(f'/api/db/v0/connections/{db1.id}/')
assert response.status_code == 404
2 changes: 1 addition & 1 deletion mathesar/tests/api/test_db_type_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
def test_db_type_list_well_formed(client, test_db_model):
database_id = test_db_model.id
response = client.get(f'/api/db/v0/databases/{database_id}/types/')
response = client.get(f'/api/db/v0/connections/{database_id}/types/')
assert response.status_code == 200
json_db_types = response.json()
assert isinstance(json_db_types, list)
Expand Down
6 changes: 3 additions & 3 deletions mathesar/tests/api/test_function_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

def test_function_list_well_formed(client, test_db_model):
database_id = test_db_model.id
response = client.get(f'/api/db/v0/databases/{database_id}/functions/')
response = client.get(f'/api/db/v0/connections/{database_id}/functions/')
assert response.status_code == 200
json_db_functions = response.json()
assert isinstance(json_db_functions, list)
Expand All @@ -17,8 +17,8 @@ def test_function_list_well_formed(client, test_db_model):
def test_function_list_permissions(FUN_create_dj_db, get_uid, client_bob, client_alice, user_bob, user_alice):
database = FUN_create_dj_db(get_uid())
DatabaseRole.objects.create(user=user_bob, database=database, role='viewer')
response = client_bob.get(f'/api/db/v0/databases/{database.id}/functions/')
response = client_bob.get(f'/api/db/v0/connections/{database.id}/functions/')
assert response.status_code == 200

response = client_alice.get(f'/api/db/v0/databases/{database.id}/functions/')
response = client_alice.get(f'/api/db/v0/connections/{database.id}/functions/')
assert response.status_code == 404
6 changes: 3 additions & 3 deletions mathesar/tests/api/test_ui_filters_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
def test_filter_list(client, test_db_name):
database = Database.objects.get(name=test_db_name)

response = client.get(f'/api/ui/v0/databases/{database.id}/filters/')
response = client.get(f'/api/ui/v0/connections/{database.id}/filters/')
response_data = response.json()
assert response.status_code == 200
for available_filter in response_data:
Expand All @@ -17,8 +17,8 @@ def test_filter_list(client, test_db_name):
def test_filter_list_permissions(FUN_create_dj_db, get_uid, client_bob, client_alice, user_bob, user_alice):
database = FUN_create_dj_db(get_uid())
DatabaseRole.objects.create(user=user_bob, database=database, role='viewer')
response = client_bob.get(f'/api/ui/v0/databases/{database.id}/filters/')
response = client_bob.get(f'/api/ui/v0/connections/{database.id}/filters/')
assert response.status_code == 200

response = client_alice.get(f'/api/ui/v0/databases/{database.id}/filters/')
response = client_alice.get(f'/api/ui/v0/connections/{database.id}/filters/')
assert response.status_code == 404
8 changes: 4 additions & 4 deletions mathesar/tests/api/test_ui_types_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
def test_type_list(client, test_db_name):
database = Database.objects.get(name=test_db_name)

response = client.get(f'/api/ui/v0/databases/{database.id}/types/')
response = client.get(f'/api/ui/v0/connections/{database.id}/types/')
response_data = response.json()
assert response.status_code == 200
assert len(response_data) == len(database.supported_ui_types)
Expand All @@ -24,12 +24,12 @@ def test_type_list(client, test_db_name):
def test_type_list_permissions(FUN_create_dj_db, get_uid, client_bob, client_alice, user_bob, user_alice):
database = FUN_create_dj_db(get_uid())
DatabaseRole.objects.create(user=user_bob, database=database, role='viewer')
response = client_bob.get(f'/api/ui/v0/databases/{database.id}/types/')
response = client_bob.get(f'/api/ui/v0/connections/{database.id}/types/')
response_data = response.json()
assert response.status_code == 200
assert len(response_data) == len(database.supported_ui_types)

response = client_alice.get(f'/api/ui/v0/databases/{database.id}/types/')
response = client_alice.get(f'/api/ui/v0/connections/{database.id}/types/')
assert response.status_code == 404


Expand Down Expand Up @@ -64,7 +64,7 @@ def test_database_types_installed(client, test_db_name):
]
default_database = Database.objects.get(name=test_db_name)

response = client.get(f'/api/ui/v0/databases/{default_database.id}/types/')
response = client.get(f'/api/ui/v0/connections/{default_database.id}/types/')
assert response.status_code == 200
actual_custom_types = response.json()

Expand Down
4 changes: 2 additions & 2 deletions mathesar/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
db_router.register(r'queries', db_viewsets.QueryViewSet, basename='query')
db_router.register(r'links', db_viewsets.LinkViewSet, basename='links')
db_router.register(r'schemas', db_viewsets.SchemaViewSet, basename='schema')
db_router.register(r'databases', db_viewsets.DatabaseViewSet, basename='database')
db_router.register(r'connections', db_viewsets.ConnectionViewSet, basename='connection')
db_router.register(r'data_files', db_viewsets.DataFileViewSet, basename='data-file')

db_table_router = routers.NestedSimpleRouter(db_router, r'tables', lookup='table')
Expand All @@ -25,7 +25,7 @@

ui_router = routers.DefaultRouter()
ui_router.register(r'version', ui_viewsets.VersionViewSet, basename='version')
ui_router.register(r'databases', ui_viewsets.DatabaseViewSet, basename='database')
ui_router.register(r'connections', ui_viewsets.ConnectionViewSet, basename='connection')
ui_router.register(r'users', ui_viewsets.UserViewSet, basename='user')
ui_router.register(r'database_roles', ui_viewsets.DatabaseRoleViewSet, basename='database_role')
ui_router.register(r'schema_roles', ui_viewsets.SchemaRoleViewSet, basename='schema_role')
Expand Down
Loading

0 comments on commit 704c125

Please sign in to comment.