quillsql 1.6__tar.gz → 1.8__tar.gz

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.
@@ -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
@@ -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
@@ -10,4 +10,5 @@ quillsql.egg-info/requires.txt
10
10
  quillsql.egg-info/top_level.txt
11
11
  tests/__init__.py
12
12
  tests/cache_test.py
13
+ tests/external_test.py
13
14
  tests/simple_test.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="quillsql",
5
- version="1.6",
5
+ version="1.8", # v0.3.0 for parity
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  "psycopg2-binary",
@@ -1,3 +1,4 @@
1
+ import psycopg2
1
2
  import pytest
2
3
  from quillsql import Quill
3
4
  import os
@@ -5,10 +6,11 @@ from dotenv import load_dotenv
5
6
 
6
7
  load_dotenv()
7
8
 
8
-
9
+ # Tests using an external db connection
10
+ db = psycopg2.connect(os.environ.get("POSTGRES_STAGING_READ"))
9
11
  quill = Quill(
10
12
  private_key=os.environ.get("QUILL_PRIVATE_KEY"),
11
- database_connection_string=os.environ.get("POSTGRES_STAGING_READ"),
13
+ psycopg2_connection=db
12
14
  )
13
15
 
14
16
 
@@ -61,25 +63,6 @@ def test_config_gets_admin_metadata():
61
63
  assert "customer_id" not in row
62
64
 
63
65
 
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
-
78
- print(res)
79
- assert res is not None
80
- assert "rows" in res
81
-
82
-
83
66
  def test_handles_empty_client_id():
84
67
  res = quill.query(
85
68
  org_id="2",
@@ -239,6 +222,22 @@ def test_orgs_works_correctly_with_client_id():
239
222
  assert "orgs" in res
240
223
 
241
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
+
242
241
  def test_view_fails_with_no_clientId():
243
242
  res = quill.query(
244
243
  org_id="2",
@@ -278,6 +277,7 @@ def test_view_works_correctly_with_clientId():
278
277
  assert res["message"] == "OK"
279
278
 
280
279
 
280
+ # TODO: flaky vvvv
281
281
  # def test_delete_works_correctly_with_clientId():
282
282
  # res = quill.query(
283
283
  # org_id="2",
@@ -0,0 +1,294 @@
1
+ import pytest
2
+ from quillsql import Quill
3
+ import os
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+
9
+ quill = Quill(
10
+ private_key=os.environ.get("QUILL_PRIVATE_KEY"),
11
+ database_connection_string=os.environ.get("POSTGRES_STAGING_READ"),
12
+ )
13
+
14
+
15
+ def test_config_gets_metadata():
16
+ res = quill.query(
17
+ org_id="2", data={"metadata": {"task": "config", "name": "spend"}}
18
+ )
19
+
20
+ assert res is not None
21
+ assert "sections" in res
22
+ assert "newQueries" in res
23
+ assert "filters" in res
24
+ assert res["filters"] == []
25
+ assert "fieldToRemove" in res
26
+ assert res["fieldToRemove"] == "customer_id"
27
+
28
+ for query in res["newQueries"]:
29
+ assert "columns" in query
30
+ assert any(
31
+ column["field"] == "spend"
32
+ and column["label"] == "spend"
33
+ and column["format"] == "dollar_amount"
34
+ for column in query["columns"]
35
+ )
36
+ assert any(
37
+ column["field"] == "month"
38
+ and column["label"] == "month"
39
+ and column["format"] == "MMM_yyyy"
40
+ for column in query["columns"]
41
+ )
42
+
43
+
44
+ def test_config_gets_admin_metadata():
45
+ res = quill.query(
46
+ org_id="2",
47
+ data={
48
+ "metadata": {
49
+ "id": "65962cf6cfce31000b53c51c",
50
+ "orgId": "2",
51
+ "clientId": "62cda15d7c9fcca7bc0a3689",
52
+ "task": "item",
53
+ "filters": [],
54
+ }
55
+ },
56
+ )
57
+
58
+ assert res is not None
59
+ for row in res["rows"]:
60
+ assert row["id"] is not "id"
61
+ assert "customer_id" not in row
62
+
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
+ # )
77
+
78
+ # print(res)
79
+ # assert res is not None
80
+ # assert "rows" in res
81
+
82
+
83
+ def test_handles_empty_client_id():
84
+ res = quill.query(
85
+ org_id="2",
86
+ data={
87
+ "metadata": {
88
+ "task": "item",
89
+ "id": "6580d3aea2caa9000b1c1b06",
90
+ "client_id": None,
91
+ "filters": [],
92
+ "query": "select * from transactions",
93
+ }
94
+ },
95
+ )
96
+
97
+ assert res["chartType"] == "line"
98
+ assert res["clientId"] == "62cda15d7c9fcca7bc0a3689"
99
+ assert res["name"] == "Total Spend Test"
100
+ assert res["dashboardName"] == "spend"
101
+
102
+
103
+ def test_gets_item():
104
+ res = quill.query(
105
+ org_id="2",
106
+ data={
107
+ "metadata": {
108
+ "task": "item",
109
+ "id": "6580d3aea2caa9000b1c1b06",
110
+ "client_id": "62cda15d7c9fcca7bc0a3689",
111
+ "filters": [],
112
+ "query": "select * from transactions",
113
+ }
114
+ },
115
+ )
116
+
117
+ assert res["chartType"] == "line"
118
+ assert res["clientId"] == "62cda15d7c9fcca7bc0a3689"
119
+ assert res["name"] == "Total Spend Test"
120
+ assert res["dashboardName"] == "spend"
121
+
122
+
123
+ def test_query_for_data():
124
+ res = quill.query(
125
+ org_id="2",
126
+ data={
127
+ "metadata": {
128
+ "task": "query",
129
+ "id": "6580d48f457d7b000b7bee2c",
130
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
131
+ }
132
+ },
133
+ )
134
+
135
+ assert "rows" in res
136
+ assert "fields" in res
137
+
138
+
139
+ def test_creates_a_chart():
140
+ res = quill.query(
141
+ org_id="2",
142
+ data={
143
+ "metadata": {
144
+ "task": "create",
145
+ "dateField": {"table": "transactions", "field": "created_at"},
146
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
147
+ "name": "Total Spend Test",
148
+ "clientId": "62cda15d7c9fcca7bc0a3689",
149
+ "customerId": "2",
150
+ "xAxisField": "month",
151
+ "xAxisLabel": "month",
152
+ "yAxisFields": [
153
+ {"field": "spend", "label": "spend", "format": "dollar_amount"}
154
+ ],
155
+ "yAxisLabel": "spend",
156
+ "chartType": "line",
157
+ "dashboardName": "spend",
158
+ "xAxisFormat": "MMM_yyyy",
159
+ "columns": [
160
+ {"field": "spend", "label": "spend", "format": "dollar_amount"},
161
+ {"field": "month", "label": "month", "format": "MMM_yyyy"},
162
+ ],
163
+ }
164
+ },
165
+ )
166
+
167
+ assert res is not None
168
+
169
+
170
+ def test_returns_different_configs_for_different_org_ids():
171
+ data = {
172
+ "metadata": {
173
+ "task": "config",
174
+ "name": "spend",
175
+ }
176
+ }
177
+ res1 = quill.query(org_id="1", data=data)
178
+ res2 = quill.query(org_id="2", data=data)
179
+ assert res1 != res2
180
+
181
+
182
+ def test_returns_different_items_for_different_org_ids():
183
+ data = {
184
+ "metadata": {
185
+ "task": "item",
186
+ "id": "6580d3aea2caa9000b1c1b06",
187
+ "filters": [],
188
+ "client_id": "62cda15d7c9fcca7bc0a3689",
189
+ "query": "select * from transactions",
190
+ }
191
+ }
192
+ res1 = quill.query(org_id="1", data=data)
193
+ res2 = quill.query(org_id="2", data=data)
194
+ assert res1 != res2
195
+
196
+
197
+ def test_returns_different_query_data_for_different_org_ids():
198
+ data = {
199
+ "metadata": {
200
+ "task": "query",
201
+ "id": "6580d48f457d7b000b7bee2c",
202
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
203
+ }
204
+ }
205
+ res1 = quill.query(org_id="1", data=data)
206
+ res2 = quill.query(org_id="2", data=data)
207
+ assert res1 != res2
208
+
209
+
210
+ def test_orgs_fails_with_no_client_id():
211
+ res = quill.query(
212
+ org_id="2",
213
+ data={
214
+ "metadata": {
215
+ "task": "orgs",
216
+ "id": "6580d3aea2caa9000b1c1b06",
217
+ }
218
+ },
219
+ )
220
+
221
+ assert res is not None
222
+ assert "errorMessage" in res
223
+
224
+
225
+ def test_orgs_works_correctly_with_client_id():
226
+ res = quill.query(
227
+ org_id="2",
228
+ data={
229
+ "metadata": {
230
+ "task": "orgs",
231
+ "id": "6580d3aea2caa9000b1c1b06",
232
+ "clientId": "62cda15d7c9fcca7bc0a3689",
233
+ },
234
+ },
235
+ )
236
+
237
+ assert res is not None
238
+ assert "errorMessage" not in res
239
+ assert "orgs" in res
240
+
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
+
258
+ def test_view_fails_with_no_clientId():
259
+ res = quill.query(
260
+ org_id="2",
261
+ data={
262
+ "metadata": {
263
+ "task": "view",
264
+ "id": "6580d3aea2caa9000b1c1b06",
265
+ },
266
+ },
267
+ )
268
+
269
+ assert res is not None
270
+ assert "errorMessage" in res
271
+
272
+
273
+ def test_view_works_correctly_with_clientId():
274
+ res = quill.query(
275
+ org_id="2",
276
+ data={
277
+ "metadata": {
278
+ "task": "view",
279
+ "id": "6580d3aea2caa9000b1c1b06",
280
+ "clientId": "62cda15d7c9fcca7bc0a3689",
281
+ "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
282
+ "name": "Total Spend Test",
283
+ "deleted": False,
284
+ },
285
+ },
286
+ )
287
+
288
+ print(res)
289
+ assert res is not None
290
+ assert "errorMessage" not in res
291
+ assert "code" in res
292
+ assert "message" in res
293
+ assert res["code"] == 201
294
+ assert res["message"] == "OK"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes