Skip to content

Commit b2fb5f0

Browse files
author
Satvik
committed
add readme images
1 parent 24ad032 commit b2fb5f0

File tree

7 files changed

+190
-9
lines changed

7 files changed

+190
-9
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,5 @@ cython_debug/
160160
.idea/
161161
test/
162162

163-
*.sqlite
163+
*.sqlite
164+
examples/testDialect.py

README.md

Lines changed: 185 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,189 @@
1-
# REST-DB-API
21

3-
This is a plugin to be used with apache-superset, for integrating with REST APIs
2+
# REST-DB-API
3+
4+
This is a plugin to be used with apache-superset, for integrating with REST APIs.
45

6+
This converts REST APIs to be used as db-api in python, and builds on top of [shillelagh's](https://github.com/betodealmeida/shillelagh) generic json adapter.
57

8+
To get started, just do:
9+
```bash
10+
pip install rest-db-api
11+
```
612

7-
- [x] POST requests (request body)
8-
- [x] headers
9-
- [ ] making the adapter read-write (for PUT/DELETE requests)
13+
Motivation
14+
1. Generic json adapter did not support request bodies and headers.
15+
2. Independance to specify http/https
16+
4. `rest` dialect enables this adapter to be used with apache superset.
17+
5. Dialect also enables us to set a base URL, and query multiple endpoints with the same 'connection'.
18+
19+
20+
# Examples
21+
22+
#### GET requests
23+
Querying [weather api](https://www.weatherapi.com/)
24+
25+
Lets assume I am querying for 3 days weather foreacast for Bangalore. The response gives the results by hour. You can get a free key from
26+
```curl
27+
https://api.weatherapi.com/v1/forecast.json?key={{your_api_key}}&q=Bangalore&days=3&aqi=no&alerts=no
28+
```
29+
30+
You can refer [this file](http://somelink.com) to check the response structure.
31+
We can query this with rest-db-api:
32+
```python
33+
from sqlalchemy import create_engine
34+
from restDbApi.utils import get_virtual_table
35+
36+
engine = create_engine("rest://api.weatherapi.com?ishttps=1")
37+
38+
endpoint = '/v1/forecast.json'
39+
params = {
40+
'key': 'your_key',
41+
'q': 'Bangalore',
42+
'days': 5
43+
}
44+
jsonpath = "$.forecast.forecastday[*]"
45+
virtual_table = get_virtual_table(endpoint=endpoint,
46+
params=params,
47+
jsonpath=jsonpath)
48+
connection = engine.connect()
49+
for i in connection.execute(f'SELECT * FROM "{virtual_table}"'):
50+
print(i)
51+
```
52+
53+
The response should return an array of objects/primitives. If not, we need to specify where in the response the array is (using `jsonpath`). In this case that is at `$.forecast.forecastday[*]`
54+
55+
Now, as the shillelagh's `Adapter` class uses in memory storage - `sqllite` , we can query the data using `sqllite` syntax, like querying inside a nested JSON:
56+
```python
57+
query = f"""
58+
SELECT
59+
date,
60+
json_extract(day, "$.maxtemp_c") as max_temp_celsius,
61+
json_extract(hour, "$[6].temp_c") as six_am_celsius,
62+
json_extract(hour, "$[6].will_it_rain") as will_it_rain
63+
FROM
64+
"{virtual_table}"
65+
"""
66+
for i in connection.execute(query):
67+
print(i)
68+
```
69+
70+
#### POST request with headers and request body
71+
72+
Consider this sample request
73+
```javascript
74+
curl --location -g --request POST 'https://some.api.com/some/api/path?a=60&c=someQuery&b=-50#$[*]' \
75+
--header 'Content-Type: application/json' \
76+
--header 'IAM_ID: satvik' \
77+
--header 'ENVIRONMENT: staging:1.5.3' \
78+
--header 'NAME: MY-REST-SERVICE' \
79+
--data-raw '{
80+
"name": "satvik",
81+
"interests": [
82+
{
83+
"name": "badminton",
84+
"category": "sports",
85+
"stats": {
86+
"racket": "intermediate",
87+
"shuttle": "yonex mavis 500"
88+
}
89+
},
90+
{
91+
"name": "programming",
92+
"category": "computers",
93+
"stats": {
94+
"laptop": "yw",
95+
"mouse": "5D ergonomic",
96+
"keyboard": "broken"
97+
}
98+
}
99+
]
100+
}'
101+
```
102+
103+
To query this with db-api, follow the snippet:
104+
```python
105+
from sqlalchemy import create_engine
106+
from restDbApi.utils import get_virtual_table
107+
108+
engine = create_engine("rest://some.api.com?ishttps=1")
109+
110+
endpoint = '/some/api/path'
111+
112+
params = {
113+
"a": 60,
114+
"b": -50,
115+
"c": "someQuery"
116+
}
117+
118+
headers = {
119+
'Content-Type': 'application/json',
120+
'IAM_ID': 'satvik',
121+
'ENVIRONMENT': 'staging:1.5.3',
122+
'NAME': 'MY-REST-SERVICE',
123+
}
124+
125+
body = {
126+
"name": "satvik",
127+
"interests": [
128+
{
129+
"name": "badminton",
130+
"category": "sports",
131+
"stats": { "racket": "intermediate", "shuttle": "yonex mavis 500" }
132+
},
133+
{
134+
"name": "programming",
135+
"category": "computers",
136+
"stats": {
137+
"laptop": "mac book pro",
138+
"mouse": "5D ergonomic",
139+
"keyboard": "broken"
140+
}
141+
}
142+
]
143+
}
144+
145+
jsonpath = "#$[*]" # set this according to your response
146+
147+
virtual_table = get_virtual_table(endpoint=endpoint,
148+
params=params,
149+
headers=headers,
150+
body=body,
151+
jsonpath=jsonpath)
152+
153+
for i in connection.execute(f'SELECT * FROM "{virtual_table}"'):
154+
print(i)
155+
```
156+
157+
#### Usage with apache-superset
158+
1. Go to Connect database
159+
![img_1.png](img_1.png)
160+
2. Select Shillelagh
161+
3. add the connection string with `rest://` prefix
162+
eg: `rest://api.weatherapi.com?ishttps=1`
163+
4. Gice your connection a name: eg `Weather API`
164+
1. Click test connection and then add
165+
2. Go to SQL lab and select `Weather API` from database.
166+
3. You can leave schema empty and query directly!![img_2.png](img_2.png)
167+
168+
Query is:
169+
```SQL
170+
SELECT date,
171+
json_extract(day, "$.maxtemp_c") as max_temp_celsius,
172+
json_extract(hour, "$[6].temp_c") as six_am_celsius,
173+
json_extract(hour, "$[6].will_it_rain") as will_it_rain
174+
FROM "/v1/forecast.json?key={your_key}&q=Bangalore&days=5#$.forecast.forecastday[*]";
175+
```
176+
Tables and schema is empty, because there's no concept for tables in REST APIs.
177+
It returns a default message. That message is configured in `rest_api_dialect.py`
178+
179+
180+
### Getting the virtual table
181+
In the superset's SQL lab, we're directly using
182+
```python
183+
/v1/forecast.json?key={your_key}&q=Bangalore&days=5#$.forecast.forecastday[*]
184+
```
185+
To get the similar virtual table address for your endpoint (it may have headers or even body), use the utility `restDbApi.utils.get_virtual_table` and pass in your configs.
186+
187+
- [x] POST requests (request body)
188+
- [x] headers
189+
- [ ] adding write support to adapter (for PUT/DELETE requests)

examples/testDialectNew.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
engine = create_engine("rest://api.weatherapi.com?ishttps=1")
44
connection = engine.connect()
5-
key = 'f1f6d01fe66c4cd48de113426232402'
5+
key = 'your_key'
66
query = f'SELECT * FROM "/v1/forecast.json?key={key}&q=Bangalore&days=3#$.forecast.forecastday[*]"'
77
for i in connection.execute(query):
88
print(i)

img.png

103 KB
Loading

img_1.png

27.6 KB
Loading

img_2.png

112 KB
Loading

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
from setuptools import find_packages, setup
44

55
setup(
6-
name="restDbApi",
6+
name="rest-db-api",
77
version="1.0.0",
8-
description="rest db api designed to be used with apache superset",
8+
description="rest db api designed to be integrated with apache superset",
99
author="Satvik Nema",
1010
author_email="[email protected]",
1111
entry_points={

0 commit comments

Comments
 (0)