Skip to content

Commit 5e6ee17

Browse files
Completed support for PostgresSQL without JDBC (#59)
* Completed support for PostgresSQL without JDBC * Completed coverage for postgresql with no JDBC * Fix format and lint Co-authored-by: Alessandro Bertini <alessandro.bertini@besharp.it>
1 parent 19e9274 commit 5e6ee17

File tree

6 files changed

+82
-6
lines changed

6 files changed

+82
-6
lines changed

local_data_api/models.py

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def from_value(cls, value: Any) -> Field:
3131
return cls(booleanValue=value)
3232
elif isinstance(value, str):
3333
return cls(stringValue=value)
34+
elif isinstance(value, datetime):
35+
return cls(stringValue=str(value))
3436
elif isinstance(value, int):
3537
return cls(longValue=value)
3638
elif isinstance(value, float):

local_data_api/resources/postgres.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
3+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
44

55
import psycopg2
6+
from psycopg2._psycopg import Column
67
from sqlalchemy.dialects import postgresql
78

89
from local_data_api.models import ColumnMetadata
@@ -12,12 +13,31 @@
1213
from local_data_api.resources.resource import ConnectionMaker, Cursor
1314

1415

16+
def create_column_metadata(field_descriptor_packet: Column) -> ColumnMetadata:
17+
return ColumnMetadata(
18+
arrayBaseColumnType=0,
19+
isAutoIncrement=False,
20+
isCaseSensitive=False,
21+
isCurrency=False,
22+
isSigned=False,
23+
label=field_descriptor_packet.name,
24+
name=field_descriptor_packet.name,
25+
nullable=None,
26+
precision=None, # TODO: Implement
27+
scale=field_descriptor_packet.scale,
28+
schema=None,
29+
tableName=field_descriptor_packet.name,
30+
type=None, # JDBC Type unsupported
31+
typeName=None, # JDBC TypeName unsupported
32+
)
33+
34+
1535
@register_resource_type
1636
class PostgresSQL(Resource):
1737
def create_column_metadata_set(
1838
self, cursor: Cursor
1939
) -> List[ColumnMetadata]: # pragma: no cover
20-
raise NotImplementedError
40+
return [create_column_metadata(f) for f in getattr(cursor, 'description')]
2141

2242
DIALECT = postgresql.dialect(paramstyle='named')
2343

local_data_api/settings.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import os
4+
from typing import Optional
45

56
from pydantic import BaseModel
67

@@ -20,13 +21,13 @@ class DBSetting(BaseModel):
2021
PORT: int
2122
USER: str
2223
PASSWORD: str
23-
JAR_PATH: str
24+
JAR_PATH: Optional[str]
2425

2526

2627
def setup() -> None:
2728
# TODO: implement to create custom resource
2829
engine: str = os.environ.get('ENGINE', 'MySQLJDBC')
29-
if engine.upper().startswith('MYSQL'):
30+
if engine == 'MySQLJDBC':
3031
db_setting: DBSetting = DBSetting(
3132
HOST=os.environ.get('MYSQL_HOST', '127.0.0.1'),
3233
PORT=os.environ.get('MYSQL_PORT', '3306'),
@@ -36,7 +37,7 @@ def setup() -> None:
3637
'MYSQL_JDBC_JAR_PATH', '/usr/lib/jvm/mariadb-java-client.jar'
3738
),
3839
)
39-
else:
40+
elif engine == 'PostgreSQLJDBC':
4041
db_setting = DBSetting(
4142
HOST=os.environ.get('POSTGRES_HOST', '127.0.0.1'),
4243
PORT=os.environ.get('POSTGRES_PORT', '5432'),
@@ -46,6 +47,15 @@ def setup() -> None:
4647
'POSTGRES_JDBC_JAR_PATH', '/usr/lib/jvm/postgresql-java-client.jar'
4748
),
4849
)
50+
elif engine == 'PostgresSQL':
51+
db_setting = DBSetting(
52+
HOST=os.environ.get('POSTGRES_HOST', '127.0.0.1'),
53+
PORT=os.environ.get('POSTGRES_PORT', '5432'),
54+
USER=os.environ.get('POSTGRES_USER', 'postgres'),
55+
PASSWORD=os.environ.get('POSTGRES_PASSWORD', 'example'),
56+
)
57+
else:
58+
raise NotImplementedError("Engine not already implemented")
4959

5060
register_secret(db_setting.USER, db_setting.PASSWORD, SECRET_ARN)
5161
register_resource(

tests/test_models.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import datetime
22
from base64 import b64encode
3-
from decimal import Decimal
3+
from datetime import datetime
44

55
import pytest
66

@@ -15,6 +15,7 @@ def test_valid_field() -> None:
1515
assert SqlParameter(name='abc', value=Field(longValue=123)).valid_value == 123
1616
assert SqlParameter(name='abc', value=Field(longValue=123)).valid_value == 123
1717
assert SqlParameter(name='abc', value=Field()).valid_value is None
18+
1819
assert (
1920
SqlParameter(
2021
name='abc', value=Field(stringValue='123456789'), typeHint='DECIMAL'
@@ -51,6 +52,9 @@ def test_from_value() -> None:
5152
assert Field.from_value(False) == Field(booleanValue=False)
5253
assert Field.from_value(b'bytes') == Field(blobValue=b64encode(b'bytes'))
5354
assert Field.from_value(None) == Field(isNull=True)
55+
assert Field.from_value(datetime(2019, 5, 18, 15, 17, 8)) == Field(
56+
stringValue='2019-05-18 15:17:08'
57+
)
5458

5559
class JavaUUID:
5660
def __init__(self, val: str):

tests/test_resource/test_postgres.py

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from psycopg2._psycopg import Column
4+
35
from local_data_api.resources import PostgresSQL
46

57

@@ -26,3 +28,15 @@ def test_create_connection_maker(mocker):
2628
connection_maker = PostgresSQL.create_connection_maker()
2729
connection_maker()
2830
mock_connect.assert_called_once_with()
31+
32+
33+
def test_create_column_metadata(mocker):
34+
connection_mock = mocker.Mock()
35+
cursor_mock = mocker.Mock()
36+
37+
connection_mock.cursor.side_effect = [cursor_mock]
38+
39+
cursor_mock.description = [Column(name="mock", scale=1)]
40+
dummy = PostgresSQL(connection_mock)
41+
dummy.create_column_metadata_set(cursor_mock)
42+
assert True

tests/test_settings.py

+26
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
def test_setup_mysql(mocker) -> None:
55
mock_register_secret = mocker.patch('local_data_api.settings.register_secret')
66
mock_register_resource = mocker.patch('local_data_api.settings.register_resource')
7+
import os
8+
9+
os.environ['ENGINE'] = 'MySQLJDBC'
710
setup()
811
mock_register_secret.assert_called_with(
912
'root', 'example', 'arn:aws:secretsmanager:us-east-1:123456789012:secret:dummy'
@@ -40,3 +43,26 @@ def test_setup_postgres(mocker) -> None:
4043
'example',
4144
{'JAR_PATH': '/usr/lib/jvm/postgresql-java-client.jar'},
4245
)
46+
47+
48+
def test_setup_postgres_no_jdbc(mocker) -> None:
49+
mock_register_secret = mocker.patch('local_data_api.settings.register_secret')
50+
mock_register_resource = mocker.patch('local_data_api.settings.register_resource')
51+
import os
52+
53+
os.environ['ENGINE'] = 'PostgresSQL'
54+
setup()
55+
mock_register_secret.assert_called_with(
56+
'postgres',
57+
'example',
58+
'arn:aws:secretsmanager:us-east-1:123456789012:secret:dummy',
59+
)
60+
mock_register_resource.assert_called_with(
61+
'arn:aws:rds:us-east-1:123456789012:cluster:dummy',
62+
'PostgresSQL',
63+
'127.0.0.1',
64+
5432,
65+
'postgres',
66+
'example',
67+
{},
68+
)

0 commit comments

Comments
 (0)