quillsql 1.6__py3-none-any.whl → 1.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
quillsql/core.py CHANGED
@@ -17,8 +17,11 @@ DEFAULT_CACHE_TTL = 24 * 60 * 60
17
17
 
18
18
  # A connection pool with a cache in front.
19
19
  class CachedPool:
20
- def __init__(self, config, cache_config):
21
- self.pool = psycopg2.connect(config)
20
+ def __init__(self, config, cache_config, psycopg2_connection=None):
21
+ if psycopg2_connection:
22
+ self.pool = psycopg2_connection
23
+ else:
24
+ self.pool = psycopg2.connect(config)
22
25
  self.cache = self.get_cache(cache_config)
23
26
  self.ttl = cache_config and cache_config.get("ttl") or DEFAULT_CACHE_TTL
24
27
  self.cur = self.pool.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
@@ -337,12 +340,20 @@ def handleDeleteTask(org_id, metadata, private_key, target_pool):
337
340
 
338
341
  ## Quill - Fullstack API Platform for Dashboards and Reporting.
339
342
  class Quill:
340
- def __init__(self, private_key, database_connection_string, cache=None):
343
+ def __init__(
344
+ self,
345
+ private_key,
346
+ database_connection_string="",
347
+ psycopg2_connection=None,
348
+ cache=None,
349
+ ):
341
350
  # Handles both dsn-style connection strings (eg. "dbname=test password=secret" )
342
351
  # as well as url-style connection strings (eg. "postgres://foo@db.com")
343
352
  to_dsn = lambda conn: make_dsn(conn) if "://" in conn else conn
344
353
  self.database_connection_string = to_dsn(database_connection_string)
345
- self.main_pool = CachedPool(database_connection_string, cache)
354
+ self.main_pool = CachedPool(
355
+ database_connection_string, cache, psycopg2_connection
356
+ )
346
357
  self.private_key = private_key
347
358
 
348
359
  def query(self, org_id, data):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quillsql
3
- Version: 1.6
3
+ Version: 1.8
4
4
  Summary: Quill SDK for Python.
5
5
  Home-page: https://github.com/quill-sql/quill-python
6
6
  Author: Quill
@@ -0,0 +1,12 @@
1
+ quillsql/__init__.py,sha256=IgJnPRLx5GcenVt82mMQo0ydvXYB_-39Ft4Of5sEEGA,39
2
+ quillsql/core.py,sha256=AJFzeFfSCVmFeGgfBFotPPDRXKPL3MbQz12E48LHlwg,12362
3
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ tests/cache_test.py,sha256=5MvAAyGF37wiRejOwG8m4uG9jxnEOseV5GtXTtjAk-8,11092
5
+ tests/external_test.py,sha256=PxHB3Kg-V3lDHHfZVUour3aVYGPS3P1bMKqfajt9oFA,8063
6
+ tests/simple_test.py,sha256=-ibAbgHtcwYHzL4VxQFoPmqX8_OlKfTzlkJvEbJRy3Y,7967
7
+ tests/staging_test.py,sha256=zdDV6BvJRyNKCy80bxDNfK1F1oqM6oIv7ul7JPKi51Q,4962
8
+ quillsql-1.8.dist-info/LICENSE,sha256=f2-T2fNvArbqfNW8RwsOu2IWEwpsaRX6G-AJvLJKuzg,1068
9
+ quillsql-1.8.dist-info/METADATA,sha256=auI6BMo1-n9ObCk65rlRtpC7td8yhgBZauKUvouEe1c,570
10
+ quillsql-1.8.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
11
+ quillsql-1.8.dist-info/top_level.txt,sha256=DCsUgdO5twxI1s5sw-euqMt1NDDtIGMR-t9A68P3gZs,15
12
+ quillsql-1.8.dist-info/RECORD,,
tests/external_test.py ADDED
@@ -0,0 +1,296 @@
1
+ import psycopg2
2
+ import pytest
3
+ from quillsql import Quill
4
+ import os
5
+ from dotenv import load_dotenv
6
+
7
+ load_dotenv()
8
+
9
+ # Tests using an external db connection
10
+ db = psycopg2.connect(os.environ.get("POSTGRES_STAGING_READ"))
11
+ quill = Quill(
12
+ private_key=os.environ.get("QUILL_PRIVATE_KEY"),
13
+ psycopg2_connection=db
14
+ )
15
+
16
+
17
+ def test_config_gets_metadata():
18
+ res = quill.query(
19
+ org_id="2", data={"metadata": {"task": "config", "name": "spend"}}
20
+ )
21
+
22
+ assert res is not None
23
+ assert "sections" in res
24
+ assert "newQueries" in res
25
+ assert "filters" in res
26
+ assert res["filters"] == []
27
+ assert "fieldToRemove" in res
28
+ assert res["fieldToRemove"] == "customer_id"
29
+
30
+ for query in res["newQueries"]:
31
+ assert "columns" in query
32
+ assert any(
33
+ column["field"] == "spend"
34
+ and column["label"] == "spend"
35
+ and column["format"] == "dollar_amount"
36
+ for column in query["columns"]
37
+ )
38
+ assert any(
39
+ column["field"] == "month"
40
+ and column["label"] == "month"
41
+ and column["format"] == "MMM_yyyy"
42
+ for column in query["columns"]
43
+ )
44
+
45
+
46
+ def test_config_gets_admin_metadata():
47
+ res = quill.query(
48
+ org_id="2",
49
+ data={
50
+ "metadata": {
51
+ "id": "65962cf6cfce31000b53c51c",
52
+ "orgId": "2",
53
+ "clientId": "62cda15d7c9fcca7bc0a3689",
54
+ "task": "item",
55
+ "filters": [],
56
+ }
57
+ },
58
+ )
59
+
60
+ assert res is not None
61
+ for row in res["rows"]:
62
+ assert row["id"] is not "id"
63
+ assert "customer_id" not in row
64
+
65
+
66
+ def test_handles_empty_client_id():
67
+ res = quill.query(
68
+ org_id="2",
69
+ data={
70
+ "metadata": {
71
+ "task": "item",
72
+ "id": "6580d3aea2caa9000b1c1b06",
73
+ "client_id": None,
74
+ "filters": [],
75
+ "query": "select * from transactions",
76
+ }
77
+ },
78
+ )
79
+
80
+ assert res["chartType"] == "line"
81
+ assert res["clientId"] == "62cda15d7c9fcca7bc0a3689"
82
+ assert res["name"] == "Total Spend Test"
83
+ assert res["dashboardName"] == "spend"
84
+
85
+
86
+ def test_gets_item():
87
+ res = quill.query(
88
+ org_id="2",
89
+ data={
90
+ "metadata": {
91
+ "task": "item",
92
+ "id": "6580d3aea2caa9000b1c1b06",
93
+ "client_id": "62cda15d7c9fcca7bc0a3689",
94
+ "filters": [],
95
+ "query": "select * from transactions",
96
+ }
97
+ },
98
+ )
99
+
100
+ assert res["chartType"] == "line"
101
+ assert res["clientId"] == "62cda15d7c9fcca7bc0a3689"
102
+ assert res["name"] == "Total Spend Test"
103
+ assert res["dashboardName"] == "spend"
104
+
105
+
106
+ def test_query_for_data():
107
+ res = quill.query(
108
+ org_id="2",
109
+ data={
110
+ "metadata": {
111
+ "task": "query",
112
+ "id": "6580d48f457d7b000b7bee2c",
113
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
114
+ }
115
+ },
116
+ )
117
+
118
+ assert "rows" in res
119
+ assert "fields" in res
120
+
121
+
122
+ def test_creates_a_chart():
123
+ res = quill.query(
124
+ org_id="2",
125
+ data={
126
+ "metadata": {
127
+ "task": "create",
128
+ "dateField": {"table": "transactions", "field": "created_at"},
129
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
130
+ "name": "Total Spend Test",
131
+ "clientId": "62cda15d7c9fcca7bc0a3689",
132
+ "customerId": "2",
133
+ "xAxisField": "month",
134
+ "xAxisLabel": "month",
135
+ "yAxisFields": [
136
+ {"field": "spend", "label": "spend", "format": "dollar_amount"}
137
+ ],
138
+ "yAxisLabel": "spend",
139
+ "chartType": "line",
140
+ "dashboardName": "spend",
141
+ "xAxisFormat": "MMM_yyyy",
142
+ "columns": [
143
+ {"field": "spend", "label": "spend", "format": "dollar_amount"},
144
+ {"field": "month", "label": "month", "format": "MMM_yyyy"},
145
+ ],
146
+ }
147
+ },
148
+ )
149
+
150
+ assert res is not None
151
+
152
+
153
+ def test_returns_different_configs_for_different_org_ids():
154
+ data = {
155
+ "metadata": {
156
+ "task": "config",
157
+ "name": "spend",
158
+ }
159
+ }
160
+ res1 = quill.query(org_id="1", data=data)
161
+ res2 = quill.query(org_id="2", data=data)
162
+ assert res1 != res2
163
+
164
+
165
+ def test_returns_different_items_for_different_org_ids():
166
+ data = {
167
+ "metadata": {
168
+ "task": "item",
169
+ "id": "6580d3aea2caa9000b1c1b06",
170
+ "filters": [],
171
+ "client_id": "62cda15d7c9fcca7bc0a3689",
172
+ "query": "select * from transactions",
173
+ }
174
+ }
175
+ res1 = quill.query(org_id="1", data=data)
176
+ res2 = quill.query(org_id="2", data=data)
177
+ assert res1 != res2
178
+
179
+
180
+ def test_returns_different_query_data_for_different_org_ids():
181
+ data = {
182
+ "metadata": {
183
+ "task": "query",
184
+ "id": "6580d48f457d7b000b7bee2c",
185
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
186
+ }
187
+ }
188
+ res1 = quill.query(org_id="1", data=data)
189
+ res2 = quill.query(org_id="2", data=data)
190
+ assert res1 != res2
191
+
192
+
193
+ def test_orgs_fails_with_no_client_id():
194
+ res = quill.query(
195
+ org_id="2",
196
+ data={
197
+ "metadata": {
198
+ "task": "orgs",
199
+ "id": "6580d3aea2caa9000b1c1b06",
200
+ }
201
+ },
202
+ )
203
+
204
+ assert res is not None
205
+ assert "errorMessage" in res
206
+
207
+
208
+ def test_orgs_works_correctly_with_client_id():
209
+ res = quill.query(
210
+ org_id="2",
211
+ data={
212
+ "metadata": {
213
+ "task": "orgs",
214
+ "id": "6580d3aea2caa9000b1c1b06",
215
+ "clientId": "62cda15d7c9fcca7bc0a3689",
216
+ },
217
+ },
218
+ )
219
+
220
+ assert res is not None
221
+ assert "errorMessage" not in res
222
+ assert "orgs" in res
223
+
224
+
225
+ def test_orgs_works_without_id():
226
+ res = quill.query(
227
+ org_id="2",
228
+ data={
229
+ "metadata": {
230
+ "task": "orgs",
231
+ "clientId": "62cda15d7c9fcca7bc0a3689",
232
+ }
233
+ },
234
+ )
235
+
236
+ assert res is not None
237
+ assert "errorMessage" not in res
238
+ assert "orgs" in res
239
+
240
+
241
+ def test_view_fails_with_no_clientId():
242
+ res = quill.query(
243
+ org_id="2",
244
+ data={
245
+ "metadata": {
246
+ "task": "view",
247
+ "id": "6580d3aea2caa9000b1c1b06",
248
+ },
249
+ },
250
+ )
251
+
252
+ assert res is not None
253
+ assert "errorMessage" in res
254
+
255
+
256
+ def test_view_works_correctly_with_clientId():
257
+ res = quill.query(
258
+ org_id="2",
259
+ data={
260
+ "metadata": {
261
+ "task": "view",
262
+ "id": "6580d3aea2caa9000b1c1b06",
263
+ "clientId": "62cda15d7c9fcca7bc0a3689",
264
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
265
+ "name": "Total Spend Test",
266
+ "deleted": False,
267
+ },
268
+ },
269
+ )
270
+
271
+ print(res)
272
+ assert res is not None
273
+ assert "errorMessage" not in res
274
+ assert "code" in res
275
+ assert "message" in res
276
+ assert res["code"] == 201
277
+ assert res["message"] == "OK"
278
+
279
+
280
+ # TODO: flaky vvvv
281
+ # def test_delete_works_correctly_with_clientId():
282
+ # res = quill.query(
283
+ # org_id="2",
284
+ # data={
285
+ # "metadata": {
286
+ # "task": "delete",
287
+ # "id": "6580d3aea2caa9000b1c1b06",
288
+ # "clientId": "62cda15d7c9fcca7bc0a3689",
289
+ # },
290
+ # },
291
+ # )
292
+
293
+ # # TODO: lifecycle tests
294
+ # # returns '' if not found
295
+ # assert res is not None
296
+ # assert "errorMessage" not in res
tests/simple_test.py CHANGED
@@ -61,23 +61,23 @@ def test_config_gets_admin_metadata():
61
61
  assert "customer_id" not in row
62
62
 
63
63
 
64
- def test_config_gets_admin_metadata_2():
65
- res = quill.query(
66
- org_id="2",
67
- data={
68
- "metadata": {
69
- "id": "65895d225cd2e8cb2bd8542d",
70
- "orgId": "2",
71
- "clientId": "6579031b3e41c378aa8180ec",
72
- "task": "item",
73
- "filters": [],
74
- }
75
- },
76
- )
64
+ # def test_config_gets_admin_metadata_2():
65
+ # res = quill.query(
66
+ # org_id="2",
67
+ # data={
68
+ # "metadata": {
69
+ # "id": "65895d225cd2e8cb2bd8542d",
70
+ # "orgId": "2",
71
+ # "clientId": "6579031b3e41c378aa8180ec",
72
+ # "task": "item",
73
+ # "filters": [],
74
+ # }
75
+ # },
76
+ # )
77
77
 
78
- print(res)
79
- assert res is not None
80
- assert "rows" in res
78
+ # print(res)
79
+ # assert res is not None
80
+ # assert "rows" in res
81
81
 
82
82
 
83
83
  def test_handles_empty_client_id():
@@ -239,6 +239,22 @@ def test_orgs_works_correctly_with_client_id():
239
239
  assert "orgs" in res
240
240
 
241
241
 
242
+ def test_orgs_works_without_id():
243
+ res = quill.query(
244
+ org_id="2",
245
+ data={
246
+ "metadata": {
247
+ "task": "orgs",
248
+ "clientId": "62cda15d7c9fcca7bc0a3689",
249
+ }
250
+ },
251
+ )
252
+
253
+ assert res is not None
254
+ assert "errorMessage" not in res
255
+ assert "orgs" in res
256
+
257
+
242
258
  def test_view_fails_with_no_clientId():
243
259
  res = quill.query(
244
260
  org_id="2",
@@ -276,21 +292,3 @@ def test_view_works_correctly_with_clientId():
276
292
  assert "message" in res
277
293
  assert res["code"] == 201
278
294
  assert res["message"] == "OK"
279
-
280
-
281
- # def test_delete_works_correctly_with_clientId():
282
- # res = quill.query(
283
- # org_id="2",
284
- # data={
285
- # "metadata": {
286
- # "task": "delete",
287
- # "id": "6580d3aea2caa9000b1c1b06",
288
- # "clientId": "62cda15d7c9fcca7bc0a3689",
289
- # },
290
- # },
291
- # )
292
-
293
- # # TODO: lifecycle tests
294
- # # returns '' if not found
295
- # assert res is not None
296
- # assert "errorMessage" not in res
@@ -1,11 +0,0 @@
1
- quillsql/__init__.py,sha256=IgJnPRLx5GcenVt82mMQo0ydvXYB_-39Ft4Of5sEEGA,39
2
- quillsql/core.py,sha256=q78yz2ZYvOfpBFB1QJQrCt9fjPGzGO8BfVR5wBibBh8,12123
3
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- tests/cache_test.py,sha256=5MvAAyGF37wiRejOwG8m4uG9jxnEOseV5GtXTtjAk-8,11092
5
- tests/simple_test.py,sha256=ohB-dqnzxZFytXMNN_ve9TbirGPGZisBBiCo4B8H93A,8066
6
- tests/staging_test.py,sha256=zdDV6BvJRyNKCy80bxDNfK1F1oqM6oIv7ul7JPKi51Q,4962
7
- quillsql-1.6.dist-info/LICENSE,sha256=f2-T2fNvArbqfNW8RwsOu2IWEwpsaRX6G-AJvLJKuzg,1068
8
- quillsql-1.6.dist-info/METADATA,sha256=Atk0K3akhFk4IEi6zRPhdFuLHR79EiUD5iqs2N-bdXg,570
9
- quillsql-1.6.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
10
- quillsql-1.6.dist-info/top_level.txt,sha256=DCsUgdO5twxI1s5sw-euqMt1NDDtIGMR-t9A68P3gZs,15
11
- quillsql-1.6.dist-info/RECORD,,
File without changes