onehouse-cli 0.1.0__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.
- onehouse_cli-0.1.0/PKG-INFO +83 -0
- onehouse_cli-0.1.0/README.md +63 -0
- onehouse_cli-0.1.0/onehouse_cli/__init__.py +10 -0
- onehouse_cli-0.1.0/onehouse_cli/cli.py +390 -0
- onehouse_cli-0.1.0/onehouse_cli/configure.py +327 -0
- onehouse_cli-0.1.0/onehouse_cli.egg-info/PKG-INFO +83 -0
- onehouse_cli-0.1.0/onehouse_cli.egg-info/SOURCES.txt +11 -0
- onehouse_cli-0.1.0/onehouse_cli.egg-info/dependency_links.txt +1 -0
- onehouse_cli-0.1.0/onehouse_cli.egg-info/entry_points.txt +3 -0
- onehouse_cli-0.1.0/onehouse_cli.egg-info/requires.txt +1 -0
- onehouse_cli-0.1.0/onehouse_cli.egg-info/top_level.txt +1 -0
- onehouse_cli-0.1.0/pyproject.toml +33 -0
- onehouse_cli-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: onehouse-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A command-line tool for executing SQL commands against the Onehouse API
|
|
5
|
+
Author-email: Sagar Lakshmipathy <18vidhyasagar@gmail.com>
|
|
6
|
+
License: Other/Proprietary
|
|
7
|
+
Keywords: onehouse,cli,sql
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Requires-Python: >=3.7
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: requests
|
|
20
|
+
|
|
21
|
+
# Onehouse CLI
|
|
22
|
+
|
|
23
|
+
A command-line tool for executing SQL commands against the Onehouse API with synchronous responses.
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
1. **Create a virtual environment and install dependencies:**
|
|
27
|
+
```bash
|
|
28
|
+
python3 -m venv venv
|
|
29
|
+
source venv/bin/activate
|
|
30
|
+
pip install -r requirements.txt
|
|
31
|
+
```
|
|
32
|
+
2. **Configure credentials:**
|
|
33
|
+
From withing the `onehouse-cli` directory, run:
|
|
34
|
+
```bash
|
|
35
|
+
./onehouse-cli-configure
|
|
36
|
+
```
|
|
37
|
+
Prompts for: Profile name, Account UID, Project UID, API Key, API Secret, Project Region, User ID, Request ID, Environment (production or staging)
|
|
38
|
+
|
|
39
|
+
**Note:** The tool will ask if you want to automatically add the CLI tools to your shell PATH for global access.
|
|
40
|
+
|
|
41
|
+
3. **Credentials are stored in:** `~/.onehouse/credentials`
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
**Execute SQL commands:**
|
|
46
|
+
```bash
|
|
47
|
+
onehouse-cli --command "SHOW LAKES" --profile myprofile
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**List available profiles:**
|
|
51
|
+
```bash
|
|
52
|
+
onehouse-cli --list-profiles
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Custom timeout (default: 10 minutes):**
|
|
56
|
+
```bash
|
|
57
|
+
onehouse-cli --command "ALTER OCU SET LIMIT = '3'" --profile myprofile --timeout 5
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- **Synchronous execution**: Automatically polls for completion (1s intervals)
|
|
63
|
+
- **Multiple profiles**: Store and switch between different API credentials
|
|
64
|
+
- **Error handling**: Clear error messages with headers and response details
|
|
65
|
+
- **Timeout control**: Configurable wait time for long-running operations
|
|
66
|
+
- **Status checking**: Check the status of an existing request by providing its request ID
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Configure first profile
|
|
72
|
+
./onehouse-cli-configure
|
|
73
|
+
|
|
74
|
+
# Execute queries
|
|
75
|
+
onehouse-cli --command "SHOW LAKES" --profile apex
|
|
76
|
+
onehouse-cli --command "DELETE CLUSTER managed_cluster_retail" --profile apex
|
|
77
|
+
|
|
78
|
+
# Manage profiles
|
|
79
|
+
onehouse-cli --list-profiles
|
|
80
|
+
|
|
81
|
+
# Check status of an existing request
|
|
82
|
+
onehouse-cli --check-status "<REQUEST_ID>" --profile myprofile
|
|
83
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Onehouse CLI
|
|
2
|
+
|
|
3
|
+
A command-line tool for executing SQL commands against the Onehouse API with synchronous responses.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
1. **Create a virtual environment and install dependencies:**
|
|
7
|
+
```bash
|
|
8
|
+
python3 -m venv venv
|
|
9
|
+
source venv/bin/activate
|
|
10
|
+
pip install -r requirements.txt
|
|
11
|
+
```
|
|
12
|
+
2. **Configure credentials:**
|
|
13
|
+
From withing the `onehouse-cli` directory, run:
|
|
14
|
+
```bash
|
|
15
|
+
./onehouse-cli-configure
|
|
16
|
+
```
|
|
17
|
+
Prompts for: Profile name, Account UID, Project UID, API Key, API Secret, Project Region, User ID, Request ID, Environment (production or staging)
|
|
18
|
+
|
|
19
|
+
**Note:** The tool will ask if you want to automatically add the CLI tools to your shell PATH for global access.
|
|
20
|
+
|
|
21
|
+
3. **Credentials are stored in:** `~/.onehouse/credentials`
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
**Execute SQL commands:**
|
|
26
|
+
```bash
|
|
27
|
+
onehouse-cli --command "SHOW LAKES" --profile myprofile
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**List available profiles:**
|
|
31
|
+
```bash
|
|
32
|
+
onehouse-cli --list-profiles
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Custom timeout (default: 10 minutes):**
|
|
36
|
+
```bash
|
|
37
|
+
onehouse-cli --command "ALTER OCU SET LIMIT = '3'" --profile myprofile --timeout 5
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Synchronous execution**: Automatically polls for completion (1s intervals)
|
|
43
|
+
- **Multiple profiles**: Store and switch between different API credentials
|
|
44
|
+
- **Error handling**: Clear error messages with headers and response details
|
|
45
|
+
- **Timeout control**: Configurable wait time for long-running operations
|
|
46
|
+
- **Status checking**: Check the status of an existing request by providing its request ID
|
|
47
|
+
|
|
48
|
+
## Examples
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Configure first profile
|
|
52
|
+
./onehouse-cli-configure
|
|
53
|
+
|
|
54
|
+
# Execute queries
|
|
55
|
+
onehouse-cli --command "SHOW LAKES" --profile apex
|
|
56
|
+
onehouse-cli --command "DELETE CLUSTER managed_cluster_retail" --profile apex
|
|
57
|
+
|
|
58
|
+
# Manage profiles
|
|
59
|
+
onehouse-cli --list-profiles
|
|
60
|
+
|
|
61
|
+
# Check status of an existing request
|
|
62
|
+
onehouse-cli --check-status "<REQUEST_ID>" --profile myprofile
|
|
63
|
+
```
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Onehouse CLI Tool
|
|
3
|
+
Execute SQL commands against Onehouse API using stored credentials.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
import argparse
|
|
10
|
+
import requests
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_credentials():
|
|
16
|
+
"""Load credentials from ~/.onehouse/credentials file."""
|
|
17
|
+
credentials_file = Path.home() / ".onehouse" / "credentials"
|
|
18
|
+
if not credentials_file.exists():
|
|
19
|
+
print("Error: No credentials found. Run 'onehouse-cli-configure' first to set up credentials.")
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
with open(credentials_file, 'r') as f:
|
|
24
|
+
return json.load(f)
|
|
25
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
26
|
+
print(f"Error reading credentials file: {e}")
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_profile_credentials(credentials, profile_name):
|
|
31
|
+
"""Get credentials for a specific profile."""
|
|
32
|
+
if profile_name not in credentials:
|
|
33
|
+
available_profiles = list(credentials.keys())
|
|
34
|
+
print(f"Error: Profile '{profile_name}' not found.")
|
|
35
|
+
if available_profiles:
|
|
36
|
+
print(f"Available profiles: {', '.join(available_profiles)}")
|
|
37
|
+
else:
|
|
38
|
+
print("No profiles found. Run 'onehouse-cli-configure' to create one.")
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
return credentials[profile_name]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def check_request_status(profile_creds, request_id):
|
|
45
|
+
"""Check the status of a request using the status endpoint."""
|
|
46
|
+
api_endpoint = profile_creds.get('api_endpoint', 'https://api.onehouse.ai')
|
|
47
|
+
url = f"{api_endpoint}/v1/status/{request_id}"
|
|
48
|
+
|
|
49
|
+
headers = {
|
|
50
|
+
'x-onehouse-account-uid': profile_creds['account_uid'],
|
|
51
|
+
'x-onehouse-project-uid': profile_creds['project_uid'],
|
|
52
|
+
'x-onehouse-api-key': profile_creds['api_key'],
|
|
53
|
+
'x-onehouse-api-secret': profile_creds['api_secret'],
|
|
54
|
+
'x-onehouse-region': profile_creds['project_region'],
|
|
55
|
+
'x-onehouse-uuid': profile_creds['user_id']
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
response = requests.get(url, headers=headers)
|
|
60
|
+
|
|
61
|
+
if response.status_code == 200:
|
|
62
|
+
result = response.json()
|
|
63
|
+
return result
|
|
64
|
+
else:
|
|
65
|
+
print(f"Status check failed with status {response.status_code}")
|
|
66
|
+
try:
|
|
67
|
+
error_data = response.json()
|
|
68
|
+
print("Error response:", json.dumps(error_data, indent=2))
|
|
69
|
+
except json.JSONDecodeError:
|
|
70
|
+
print("Error response:", response.text)
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
except requests.exceptions.RequestException as e:
|
|
74
|
+
print(f"Status check request failed: {e}")
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def check_status_async(profile_creds, request_id):
|
|
79
|
+
"""Check the status of a request asynchronously without timeout."""
|
|
80
|
+
print(f"Checking status for request ID: {request_id}")
|
|
81
|
+
|
|
82
|
+
status_result = check_request_status(profile_creds, request_id)
|
|
83
|
+
|
|
84
|
+
if status_result is None:
|
|
85
|
+
print("Failed to check status.")
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
# Try different possible status field names
|
|
89
|
+
status = (status_result.get('status') or
|
|
90
|
+
status_result.get('state') or
|
|
91
|
+
status_result.get('jobStatus') or
|
|
92
|
+
status_result.get('requestStatus') or
|
|
93
|
+
status_result.get('apiStatus') or '').lower()
|
|
94
|
+
|
|
95
|
+
print(f"Current Status: {status}")
|
|
96
|
+
|
|
97
|
+
if status in ['completed', 'success', 'finished', 'done', 'succeed', 'api_operation_status_success']:
|
|
98
|
+
print("✅ Request completed successfully!")
|
|
99
|
+
|
|
100
|
+
# Only show result if it's not empty
|
|
101
|
+
result_to_show = status_result.get('apiResponse', status_result)
|
|
102
|
+
if result_to_show and result_to_show != {}:
|
|
103
|
+
print("Result:")
|
|
104
|
+
print(json.dumps(result_to_show, indent=2))
|
|
105
|
+
return True
|
|
106
|
+
elif status in ['failed', 'error', 'failure', 'api_operation_status_failed', 'api_operation_status_error']:
|
|
107
|
+
print("❌ Request failed!")
|
|
108
|
+
print("Error details:")
|
|
109
|
+
print(json.dumps(status_result, indent=2))
|
|
110
|
+
return False
|
|
111
|
+
elif status in ['running', 'processing', 'in_progress', 'pending', 'submitted', 'queued', 'api_operation_status_pending']:
|
|
112
|
+
print("⏳ Request is still in progress...")
|
|
113
|
+
print("Full status details:")
|
|
114
|
+
print(json.dumps(status_result, indent=2))
|
|
115
|
+
return True
|
|
116
|
+
else:
|
|
117
|
+
print(f"⚠️ Request has status: {status}")
|
|
118
|
+
print("Full status details:")
|
|
119
|
+
print(json.dumps(status_result, indent=2))
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def wait_for_completion(profile_creds, request_id, timeout_minutes=10):
|
|
124
|
+
"""Poll the status endpoint until completion or timeout."""
|
|
125
|
+
timeout_seconds = timeout_minutes * 60
|
|
126
|
+
start_time = time.time()
|
|
127
|
+
last_status = None
|
|
128
|
+
progress_shown = False
|
|
129
|
+
|
|
130
|
+
while time.time() - start_time < timeout_seconds:
|
|
131
|
+
status_result = check_request_status(profile_creds, request_id)
|
|
132
|
+
|
|
133
|
+
if status_result is None:
|
|
134
|
+
print("Failed to check status, retrying in 1 second...")
|
|
135
|
+
time.sleep(1)
|
|
136
|
+
continue
|
|
137
|
+
|
|
138
|
+
# Try different possible status field names
|
|
139
|
+
status = (status_result.get('status') or
|
|
140
|
+
status_result.get('state') or
|
|
141
|
+
status_result.get('jobStatus') or
|
|
142
|
+
status_result.get('requestStatus') or
|
|
143
|
+
status_result.get('apiStatus') or '').lower()
|
|
144
|
+
|
|
145
|
+
# Only print status if it changed or it's the first time
|
|
146
|
+
if status != last_status:
|
|
147
|
+
print(f"Status: {status}")
|
|
148
|
+
last_status = status
|
|
149
|
+
|
|
150
|
+
if status in ['completed', 'success', 'finished', 'done', 'succeed', 'api_operation_status_success']:
|
|
151
|
+
print("✅ Command completed successfully!")
|
|
152
|
+
|
|
153
|
+
# Only show result if it's not empty
|
|
154
|
+
result_to_show = status_result.get('apiResponse', status_result)
|
|
155
|
+
if result_to_show and result_to_show != {}:
|
|
156
|
+
print("Final Result:")
|
|
157
|
+
print(json.dumps(result_to_show, indent=2))
|
|
158
|
+
return True
|
|
159
|
+
elif status in ['failed', 'error', 'failure', 'api_operation_status_failed', 'api_operation_status_error']:
|
|
160
|
+
print("❌ Command failed!")
|
|
161
|
+
print("Error details:")
|
|
162
|
+
print(json.dumps(status_result, indent=2))
|
|
163
|
+
return False
|
|
164
|
+
elif status in ['running', 'processing', 'in_progress', 'pending', 'submitted', 'queued', 'api_operation_status_pending']:
|
|
165
|
+
if not progress_shown:
|
|
166
|
+
print("⏳ Request is in progress...")
|
|
167
|
+
progress_shown = True
|
|
168
|
+
time.sleep(1)
|
|
169
|
+
else:
|
|
170
|
+
if not progress_shown:
|
|
171
|
+
print(f"⏳ Request status is '{status}', monitoring progress...")
|
|
172
|
+
progress_shown = True
|
|
173
|
+
time.sleep(1)
|
|
174
|
+
|
|
175
|
+
print(f"⏰ Timeout reached after {timeout_minutes} minutes")
|
|
176
|
+
print(f"You can check the status later using: onehouse-cli --check-status {request_id} --profile {profile_creds.get('name', 'your-profile')}")
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def execute_command(profile_creds, command, timeout_minutes=10):
|
|
181
|
+
"""Execute a command against the Onehouse API and wait for completion."""
|
|
182
|
+
api_endpoint = profile_creds.get('api_endpoint', 'https://api.onehouse.ai')
|
|
183
|
+
url = f"{api_endpoint}/v1/resource/"
|
|
184
|
+
|
|
185
|
+
headers = {
|
|
186
|
+
'x-onehouse-account-uid': profile_creds['account_uid'],
|
|
187
|
+
'x-onehouse-project-uid': profile_creds['project_uid'],
|
|
188
|
+
'x-onehouse-api-key': profile_creds['api_key'],
|
|
189
|
+
'x-onehouse-api-secret': profile_creds['api_secret'],
|
|
190
|
+
'Content-Type': 'application/json',
|
|
191
|
+
'x-onehouse-region': profile_creds['project_region'],
|
|
192
|
+
'x-onehouse-uuid': profile_creds['user_id']
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# Add required request_id header
|
|
196
|
+
headers['x-onehouse-link-uid'] = profile_creds['request_id']
|
|
197
|
+
|
|
198
|
+
payload = {
|
|
199
|
+
"statement": command
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
print(f"Executing command: {command}")
|
|
204
|
+
print("Making API request...")
|
|
205
|
+
|
|
206
|
+
response = requests.post(url, headers=headers, json=payload)
|
|
207
|
+
|
|
208
|
+
print(f"Response Status: {response.status_code}")
|
|
209
|
+
|
|
210
|
+
if response.status_code == 200:
|
|
211
|
+
try:
|
|
212
|
+
result = response.json()
|
|
213
|
+
print("Initial Response:")
|
|
214
|
+
print(json.dumps(result, indent=2))
|
|
215
|
+
|
|
216
|
+
# Extract request ID from response if available
|
|
217
|
+
async_request_id = result.get('requestId') or result.get('request_id') or result.get('id')
|
|
218
|
+
|
|
219
|
+
if async_request_id:
|
|
220
|
+
print(f"Request ID: {async_request_id}")
|
|
221
|
+
return wait_for_completion(profile_creds, async_request_id, timeout_minutes)
|
|
222
|
+
else:
|
|
223
|
+
print("No request ID found in response - command may have completed immediately")
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
except json.JSONDecodeError:
|
|
227
|
+
print("Response (raw text):")
|
|
228
|
+
print(response.text)
|
|
229
|
+
return True
|
|
230
|
+
else:
|
|
231
|
+
print("Error Response:")
|
|
232
|
+
print(f"Status Code: {response.status_code}")
|
|
233
|
+
print("Headers:")
|
|
234
|
+
for header, value in response.headers.items():
|
|
235
|
+
print(f" {header}: {value}")
|
|
236
|
+
print("Body:")
|
|
237
|
+
try:
|
|
238
|
+
error_data = response.json()
|
|
239
|
+
print(json.dumps(error_data, indent=2))
|
|
240
|
+
except json.JSONDecodeError:
|
|
241
|
+
print(response.text if response.text else "(empty)")
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
except requests.exceptions.RequestException as e:
|
|
245
|
+
print(f"Request failed: {e}")
|
|
246
|
+
return False
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def list_profiles(credentials):
|
|
250
|
+
"""List all available profiles."""
|
|
251
|
+
if not credentials:
|
|
252
|
+
print("No profiles found.")
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
default_profile = credentials.get('_default', None)
|
|
256
|
+
|
|
257
|
+
print("Available profiles:")
|
|
258
|
+
for profile_name, profile_data in credentials.items():
|
|
259
|
+
# Skip the _default entry
|
|
260
|
+
if profile_name == '_default':
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
account_uid = profile_data.get('account_uid', 'N/A')
|
|
264
|
+
project_uid = profile_data.get('project_uid', 'N/A')
|
|
265
|
+
region = profile_data.get('project_region', 'N/A')
|
|
266
|
+
environment = profile_data.get('environment', 'production')
|
|
267
|
+
|
|
268
|
+
default_indicator = " (default)" if profile_name == default_profile else ""
|
|
269
|
+
print(f" - {profile_name}{default_indicator}")
|
|
270
|
+
print(f" Account: {account_uid}")
|
|
271
|
+
print(f" Project: {project_uid}")
|
|
272
|
+
print(f" Region: {region}")
|
|
273
|
+
print(f" Environment: {environment}")
|
|
274
|
+
print()
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def main():
|
|
278
|
+
"""Main function to run the CLI tool."""
|
|
279
|
+
parser = argparse.ArgumentParser(
|
|
280
|
+
description='Execute SQL commands against Onehouse API',
|
|
281
|
+
epilog='Examples:\n'
|
|
282
|
+
' Execute command: onehouse-cli --command "ALTER OCU SET LIMIT = \'3\'" --profile myprofile\n'
|
|
283
|
+
' Check status: onehouse-cli --check-status REQUEST_ID --profile myprofile',
|
|
284
|
+
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
parser.add_argument(
|
|
288
|
+
'--command', '-c',
|
|
289
|
+
type=str,
|
|
290
|
+
help='SQL command to execute (backticks around job IDs will be preserved automatically)'
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
parser.add_argument(
|
|
294
|
+
'--profile', '-p',
|
|
295
|
+
type=str,
|
|
296
|
+
help='Profile name to use for authentication (uses default if not specified)'
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
parser.add_argument(
|
|
300
|
+
'--list-profiles', '-l',
|
|
301
|
+
action='store_true',
|
|
302
|
+
help='List all available profiles'
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
parser.add_argument(
|
|
306
|
+
'--timeout', '-t',
|
|
307
|
+
type=int,
|
|
308
|
+
default=10,
|
|
309
|
+
help='Timeout in minutes for command completion (default: 10)'
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
parser.add_argument(
|
|
313
|
+
'--check-status',
|
|
314
|
+
type=str,
|
|
315
|
+
help='Check the status of an existing request by providing its request ID'
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
args = parser.parse_args()
|
|
319
|
+
|
|
320
|
+
# Load credentials
|
|
321
|
+
credentials = load_credentials()
|
|
322
|
+
if not credentials:
|
|
323
|
+
return 1
|
|
324
|
+
|
|
325
|
+
# List profiles if requested
|
|
326
|
+
if args.list_profiles:
|
|
327
|
+
list_profiles(credentials)
|
|
328
|
+
return 0
|
|
329
|
+
|
|
330
|
+
# Handle check-status if requested
|
|
331
|
+
if args.check_status:
|
|
332
|
+
# Handle profile selection for status check
|
|
333
|
+
profile_name = args.profile
|
|
334
|
+
if not profile_name:
|
|
335
|
+
# Check if there's a default profile set
|
|
336
|
+
if '_default' in credentials:
|
|
337
|
+
profile_name = credentials['_default']
|
|
338
|
+
print(f"Using default profile: {profile_name}")
|
|
339
|
+
else:
|
|
340
|
+
print("Error: --profile is required for status check")
|
|
341
|
+
print("\nUse --list-profiles to see available profiles")
|
|
342
|
+
print("Or run 'onehouse-cli-configure' to set up a default profile")
|
|
343
|
+
return 1
|
|
344
|
+
|
|
345
|
+
# Get profile credentials
|
|
346
|
+
profile_creds = get_profile_credentials(credentials, profile_name)
|
|
347
|
+
if not profile_creds:
|
|
348
|
+
return 1
|
|
349
|
+
|
|
350
|
+
# Store profile name for better error messages
|
|
351
|
+
profile_creds['name'] = profile_name
|
|
352
|
+
|
|
353
|
+
# Check status asynchronously
|
|
354
|
+
success = check_status_async(profile_creds, args.check_status)
|
|
355
|
+
return 0 if success else 1
|
|
356
|
+
|
|
357
|
+
# Validate required arguments for command execution
|
|
358
|
+
if not args.command:
|
|
359
|
+
print("Error: --command is required (unless using --check-status)")
|
|
360
|
+
parser.print_help()
|
|
361
|
+
return 1
|
|
362
|
+
|
|
363
|
+
# Handle profile selection
|
|
364
|
+
profile_name = args.profile
|
|
365
|
+
if not profile_name:
|
|
366
|
+
# Check if there's a default profile set
|
|
367
|
+
if '_default' in credentials:
|
|
368
|
+
profile_name = credentials['_default']
|
|
369
|
+
print(f"Using default profile: {profile_name}")
|
|
370
|
+
else:
|
|
371
|
+
print("Error: --profile is required")
|
|
372
|
+
print("\nUse --list-profiles to see available profiles")
|
|
373
|
+
print("Or run 'onehouse-cli-configure' to set up a default profile")
|
|
374
|
+
return 1
|
|
375
|
+
|
|
376
|
+
# Get profile credentials
|
|
377
|
+
profile_creds = get_profile_credentials(credentials, profile_name)
|
|
378
|
+
if not profile_creds:
|
|
379
|
+
return 1
|
|
380
|
+
|
|
381
|
+
# Store profile name for better error messages
|
|
382
|
+
profile_creds['name'] = profile_name
|
|
383
|
+
|
|
384
|
+
# Execute the command
|
|
385
|
+
success = execute_command(profile_creds, args.command, args.timeout)
|
|
386
|
+
return 0 if success else 1
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
if __name__ == "__main__":
|
|
390
|
+
sys.exit(main())
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Onehouse CLI Configuration Tool
|
|
3
|
+
Collects and stores Onehouse API credentials for future use.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import json
|
|
8
|
+
import getpass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_user_input(prompt, secure=False):
|
|
13
|
+
"""Get input from user with optional secure input for secrets."""
|
|
14
|
+
if secure:
|
|
15
|
+
return getpass.getpass(prompt)
|
|
16
|
+
return input(prompt).strip()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def create_config_directory():
|
|
20
|
+
"""Create the ~/.onehouse directory if it doesn't exist."""
|
|
21
|
+
config_dir = Path.home() / ".onehouse"
|
|
22
|
+
config_dir.mkdir(exist_ok=True)
|
|
23
|
+
return config_dir
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def load_existing_credentials():
|
|
27
|
+
"""Load existing credentials file or return empty dict."""
|
|
28
|
+
credentials_file = Path.home() / ".onehouse" / "credentials"
|
|
29
|
+
if credentials_file.exists():
|
|
30
|
+
try:
|
|
31
|
+
with open(credentials_file, 'r') as f:
|
|
32
|
+
return json.load(f)
|
|
33
|
+
except (json.JSONDecodeError, IOError):
|
|
34
|
+
return {}
|
|
35
|
+
return {}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def save_credentials(credentials):
|
|
39
|
+
"""Save credentials to ~/.onehouse/credentials file."""
|
|
40
|
+
config_dir = create_config_directory()
|
|
41
|
+
credentials_file = config_dir / "credentials"
|
|
42
|
+
|
|
43
|
+
with open(credentials_file, 'w') as f:
|
|
44
|
+
json.dump(credentials, f, indent=2)
|
|
45
|
+
|
|
46
|
+
# Set restrictive permissions (readable only by owner)
|
|
47
|
+
os.chmod(credentials_file, 0o600)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def collect_credentials():
|
|
51
|
+
"""Collect all required credentials from user."""
|
|
52
|
+
print("Onehouse CLI Configuration")
|
|
53
|
+
print("=" * 30)
|
|
54
|
+
print("Please provide your Onehouse API credentials:")
|
|
55
|
+
print()
|
|
56
|
+
|
|
57
|
+
credentials = {}
|
|
58
|
+
|
|
59
|
+
# Profile name
|
|
60
|
+
profile_name = get_user_input("Profile name: ")
|
|
61
|
+
if not profile_name:
|
|
62
|
+
print("Error: Profile name is required")
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
# Load existing credentials to check if profile exists
|
|
66
|
+
existing_credentials = load_existing_credentials()
|
|
67
|
+
existing_profile = existing_credentials.get(profile_name, {})
|
|
68
|
+
|
|
69
|
+
if profile_name in existing_credentials:
|
|
70
|
+
print(f"\n⚠️ Profile '{profile_name}' already exists:")
|
|
71
|
+
print(f" Account: {existing_profile.get('account_uid', 'N/A')}")
|
|
72
|
+
print(f" Project: {existing_profile.get('project_uid', 'N/A')}")
|
|
73
|
+
print(f" Region: {existing_profile.get('project_region', 'N/A')}")
|
|
74
|
+
print(f" Environment: {existing_profile.get('environment', 'production')}")
|
|
75
|
+
|
|
76
|
+
# Show API Key/Secret previews in the summary
|
|
77
|
+
existing_key = existing_profile.get('api_key', '')
|
|
78
|
+
existing_secret = existing_profile.get('api_secret', '')
|
|
79
|
+
key_preview = existing_key[:4] + '...' if len(existing_key) > 4 else existing_key
|
|
80
|
+
secret_preview = existing_secret[:4] + '...' if len(existing_secret) > 4 else existing_secret
|
|
81
|
+
if existing_key:
|
|
82
|
+
print(f" API Key: {key_preview}")
|
|
83
|
+
if existing_secret:
|
|
84
|
+
print(f" API Secret: {secret_preview}")
|
|
85
|
+
|
|
86
|
+
print("\nPress Enter to keep existing values or enter new values:")
|
|
87
|
+
|
|
88
|
+
# Account UID
|
|
89
|
+
existing_account = existing_profile.get('account_uid', '')
|
|
90
|
+
prompt = f"Account UID ({existing_account}): " if existing_account else "Account UID: "
|
|
91
|
+
account_uid = get_user_input(prompt).strip()
|
|
92
|
+
if not account_uid:
|
|
93
|
+
if existing_account:
|
|
94
|
+
account_uid = existing_account
|
|
95
|
+
else:
|
|
96
|
+
print("Error: Account UID is required")
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
# Project UID
|
|
100
|
+
existing_project = existing_profile.get('project_uid', '')
|
|
101
|
+
prompt = f"Project UID ({existing_project}): " if existing_project else "Project UID: "
|
|
102
|
+
project_uid = get_user_input(prompt).strip()
|
|
103
|
+
if not project_uid:
|
|
104
|
+
if existing_project:
|
|
105
|
+
project_uid = existing_project
|
|
106
|
+
else:
|
|
107
|
+
print("Error: Project UID is required")
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
# API Key
|
|
111
|
+
existing_key = existing_profile.get('api_key', '')
|
|
112
|
+
key_preview = existing_key[:4] + '...' if len(existing_key) > 4 else existing_key
|
|
113
|
+
prompt = f"API Key ({key_preview}) (input masked): " if existing_key else "API Key (input masked): "
|
|
114
|
+
api_key = get_user_input(prompt, secure=True)
|
|
115
|
+
if not api_key:
|
|
116
|
+
if existing_key:
|
|
117
|
+
api_key = existing_key
|
|
118
|
+
else:
|
|
119
|
+
print("Error: API Key is required")
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
# API Secret
|
|
123
|
+
existing_secret = existing_profile.get('api_secret', '')
|
|
124
|
+
secret_preview = existing_secret[:4] + '...' if len(existing_secret) > 4 else existing_secret
|
|
125
|
+
prompt = f"API Secret ({secret_preview}) (input masked): " if existing_secret else "API Secret (input masked): "
|
|
126
|
+
api_secret = get_user_input(prompt, secure=True)
|
|
127
|
+
if not api_secret:
|
|
128
|
+
if existing_secret:
|
|
129
|
+
api_secret = existing_secret
|
|
130
|
+
else:
|
|
131
|
+
print("Error: API Secret is required")
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
# Project Region
|
|
135
|
+
existing_region = existing_profile.get('project_region', '')
|
|
136
|
+
prompt = f"Project Region ({existing_region}): " if existing_region else "Project Region: "
|
|
137
|
+
project_region = get_user_input(prompt).strip()
|
|
138
|
+
if not project_region:
|
|
139
|
+
if existing_region:
|
|
140
|
+
project_region = existing_region
|
|
141
|
+
else:
|
|
142
|
+
print("Error: Project Region is required")
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
# User ID
|
|
146
|
+
existing_user = existing_profile.get('user_id', '')
|
|
147
|
+
prompt = f"User ID ({existing_user}): " if existing_user else "User ID: "
|
|
148
|
+
user_id = get_user_input(prompt).strip()
|
|
149
|
+
if not user_id:
|
|
150
|
+
if existing_user:
|
|
151
|
+
user_id = existing_user
|
|
152
|
+
else:
|
|
153
|
+
print("Error: User ID is required")
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
# Request ID
|
|
157
|
+
existing_request = existing_profile.get('request_id', '')
|
|
158
|
+
prompt = f"Request ID ({existing_request}): " if existing_request else "Request ID: "
|
|
159
|
+
request_id = get_user_input(prompt).strip()
|
|
160
|
+
if not request_id:
|
|
161
|
+
if existing_request:
|
|
162
|
+
request_id = existing_request
|
|
163
|
+
else:
|
|
164
|
+
print("Error: Request ID is required")
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
# Environment selection
|
|
168
|
+
existing_env = existing_profile.get('environment', 'production')
|
|
169
|
+
print()
|
|
170
|
+
print("Environment Selection:")
|
|
171
|
+
print("1. Production (api.onehouse.ai)")
|
|
172
|
+
print("2. Staging (staging-api.onehouse.ai)")
|
|
173
|
+
prompt = f"Choose environment (1 or 2) ({existing_env}): " if existing_env else "Choose environment (1 or 2): "
|
|
174
|
+
env_choice = get_user_input(prompt).strip()
|
|
175
|
+
|
|
176
|
+
if not env_choice and existing_env:
|
|
177
|
+
environment = existing_env
|
|
178
|
+
api_endpoint = existing_profile.get('api_endpoint',
|
|
179
|
+
'https://staging-api.onehouse.ai' if existing_env == 'staging'
|
|
180
|
+
else 'https://api.onehouse.ai')
|
|
181
|
+
elif env_choice == "2":
|
|
182
|
+
environment = "staging"
|
|
183
|
+
api_endpoint = "https://staging-api.onehouse.ai"
|
|
184
|
+
else:
|
|
185
|
+
environment = "production"
|
|
186
|
+
api_endpoint = "https://api.onehouse.ai"
|
|
187
|
+
|
|
188
|
+
print(f"Selected: {environment} environment")
|
|
189
|
+
|
|
190
|
+
credentials[profile_name] = {
|
|
191
|
+
"account_uid": account_uid,
|
|
192
|
+
"project_uid": project_uid,
|
|
193
|
+
"api_key": api_key,
|
|
194
|
+
"api_secret": api_secret,
|
|
195
|
+
"project_region": project_region,
|
|
196
|
+
"user_id": user_id,
|
|
197
|
+
"request_id": request_id,
|
|
198
|
+
"environment": environment,
|
|
199
|
+
"api_endpoint": api_endpoint
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# Ask if this should be the default profile
|
|
203
|
+
print()
|
|
204
|
+
set_as_default = get_user_input("Set this as the default profile? (y/n): ").strip().lower()
|
|
205
|
+
if set_as_default in ['y', 'yes']:
|
|
206
|
+
credentials['_default'] = profile_name
|
|
207
|
+
|
|
208
|
+
return credentials, profile_name
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def is_already_in_shell_profile():
|
|
212
|
+
"""Check if onehouse-cli is already added to shell profile."""
|
|
213
|
+
cli_dir = Path(__file__).parent.absolute()
|
|
214
|
+
|
|
215
|
+
# Detect shell and profile file
|
|
216
|
+
shell_profiles = [
|
|
217
|
+
Path.home() / ".zshrc",
|
|
218
|
+
Path.home() / ".bashrc",
|
|
219
|
+
Path.home() / ".bash_profile"
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
for profile_file in shell_profiles:
|
|
223
|
+
if profile_file.exists():
|
|
224
|
+
try:
|
|
225
|
+
with open(profile_file, 'r') as f:
|
|
226
|
+
content = f.read()
|
|
227
|
+
if str(cli_dir) in content and "onehouse-cli" in content:
|
|
228
|
+
return True
|
|
229
|
+
except IOError:
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def add_to_shell_profile():
|
|
236
|
+
"""Add onehouse-cli tools to the user's shell profile."""
|
|
237
|
+
import subprocess
|
|
238
|
+
|
|
239
|
+
# Get the directory where the CLI tools are located
|
|
240
|
+
cli_dir = Path(__file__).parent.absolute()
|
|
241
|
+
path_export = f'export PATH="{cli_dir}:$PATH"'
|
|
242
|
+
|
|
243
|
+
# Detect shell and profile file
|
|
244
|
+
shell_profiles = [
|
|
245
|
+
Path.home() / ".zshrc",
|
|
246
|
+
Path.home() / ".bashrc",
|
|
247
|
+
Path.home() / ".bash_profile"
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
profile_file = None
|
|
251
|
+
for profile in shell_profiles:
|
|
252
|
+
if profile.exists():
|
|
253
|
+
profile_file = profile
|
|
254
|
+
break
|
|
255
|
+
|
|
256
|
+
if not profile_file:
|
|
257
|
+
print("Could not find shell profile file (.zshrc, .bashrc, .bash_profile)")
|
|
258
|
+
print(f"Manually add this to your shell profile: {path_export}")
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
# Check if already added
|
|
262
|
+
try:
|
|
263
|
+
with open(profile_file, 'r') as f:
|
|
264
|
+
content = f.read()
|
|
265
|
+
if str(cli_dir) in content and "onehouse-cli" in content:
|
|
266
|
+
print(f"✓ Already added to {profile_file}")
|
|
267
|
+
return
|
|
268
|
+
except IOError:
|
|
269
|
+
pass
|
|
270
|
+
|
|
271
|
+
# Add to profile
|
|
272
|
+
try:
|
|
273
|
+
with open(profile_file, 'a') as f:
|
|
274
|
+
f.write(f"\n# Add onehouse-cli tools to PATH\n{path_export}\n")
|
|
275
|
+
print(f"✓ Added to {profile_file}")
|
|
276
|
+
print("Please run 'source ~/.zshrc' (or your shell profile) to apply changes")
|
|
277
|
+
except IOError as e:
|
|
278
|
+
print(f"Failed to write to {profile_file}: {e}")
|
|
279
|
+
print(f"Manually add this to your shell profile: {path_export}")
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def main():
|
|
283
|
+
"""Main function to run the configuration tool."""
|
|
284
|
+
try:
|
|
285
|
+
# Load existing credentials
|
|
286
|
+
existing_credentials = load_existing_credentials()
|
|
287
|
+
|
|
288
|
+
# Collect new credentials
|
|
289
|
+
result = collect_credentials()
|
|
290
|
+
if not result:
|
|
291
|
+
return 1
|
|
292
|
+
|
|
293
|
+
new_credentials, profile_name = result
|
|
294
|
+
|
|
295
|
+
# Merge with existing credentials
|
|
296
|
+
existing_credentials.update(new_credentials)
|
|
297
|
+
|
|
298
|
+
# Save credentials
|
|
299
|
+
save_credentials(existing_credentials)
|
|
300
|
+
|
|
301
|
+
print()
|
|
302
|
+
print(f"✓ Profile '{profile_name}' configured successfully!")
|
|
303
|
+
print(f"Credentials saved to: {Path.home() / '.onehouse' / 'credentials'}")
|
|
304
|
+
print()
|
|
305
|
+
|
|
306
|
+
# Check if already in shell profile, only ask if not already added
|
|
307
|
+
if is_already_in_shell_profile():
|
|
308
|
+
print("✓ onehouse-cli tools already in shell PATH")
|
|
309
|
+
else:
|
|
310
|
+
add_to_profile = input("Add onehouse-cli tools to your shell PATH? (y/n): ").strip().lower()
|
|
311
|
+
if add_to_profile in ['y', 'yes']:
|
|
312
|
+
add_to_shell_profile()
|
|
313
|
+
|
|
314
|
+
print("You can now use this profile with future Onehouse CLI commands.")
|
|
315
|
+
|
|
316
|
+
return 0
|
|
317
|
+
|
|
318
|
+
except KeyboardInterrupt:
|
|
319
|
+
print("\n\nConfiguration cancelled.")
|
|
320
|
+
return 1
|
|
321
|
+
except Exception as e:
|
|
322
|
+
print(f"\nError: {e}")
|
|
323
|
+
return 1
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
if __name__ == "__main__":
|
|
327
|
+
exit(main())
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: onehouse-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A command-line tool for executing SQL commands against the Onehouse API
|
|
5
|
+
Author-email: Sagar Lakshmipathy <18vidhyasagar@gmail.com>
|
|
6
|
+
License: Other/Proprietary
|
|
7
|
+
Keywords: onehouse,cli,sql
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Requires-Python: >=3.7
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: requests
|
|
20
|
+
|
|
21
|
+
# Onehouse CLI
|
|
22
|
+
|
|
23
|
+
A command-line tool for executing SQL commands against the Onehouse API with synchronous responses.
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
1. **Create a virtual environment and install dependencies:**
|
|
27
|
+
```bash
|
|
28
|
+
python3 -m venv venv
|
|
29
|
+
source venv/bin/activate
|
|
30
|
+
pip install -r requirements.txt
|
|
31
|
+
```
|
|
32
|
+
2. **Configure credentials:**
|
|
33
|
+
From withing the `onehouse-cli` directory, run:
|
|
34
|
+
```bash
|
|
35
|
+
./onehouse-cli-configure
|
|
36
|
+
```
|
|
37
|
+
Prompts for: Profile name, Account UID, Project UID, API Key, API Secret, Project Region, User ID, Request ID, Environment (production or staging)
|
|
38
|
+
|
|
39
|
+
**Note:** The tool will ask if you want to automatically add the CLI tools to your shell PATH for global access.
|
|
40
|
+
|
|
41
|
+
3. **Credentials are stored in:** `~/.onehouse/credentials`
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
**Execute SQL commands:**
|
|
46
|
+
```bash
|
|
47
|
+
onehouse-cli --command "SHOW LAKES" --profile myprofile
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**List available profiles:**
|
|
51
|
+
```bash
|
|
52
|
+
onehouse-cli --list-profiles
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Custom timeout (default: 10 minutes):**
|
|
56
|
+
```bash
|
|
57
|
+
onehouse-cli --command "ALTER OCU SET LIMIT = '3'" --profile myprofile --timeout 5
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- **Synchronous execution**: Automatically polls for completion (1s intervals)
|
|
63
|
+
- **Multiple profiles**: Store and switch between different API credentials
|
|
64
|
+
- **Error handling**: Clear error messages with headers and response details
|
|
65
|
+
- **Timeout control**: Configurable wait time for long-running operations
|
|
66
|
+
- **Status checking**: Check the status of an existing request by providing its request ID
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Configure first profile
|
|
72
|
+
./onehouse-cli-configure
|
|
73
|
+
|
|
74
|
+
# Execute queries
|
|
75
|
+
onehouse-cli --command "SHOW LAKES" --profile apex
|
|
76
|
+
onehouse-cli --command "DELETE CLUSTER managed_cluster_retail" --profile apex
|
|
77
|
+
|
|
78
|
+
# Manage profiles
|
|
79
|
+
onehouse-cli --list-profiles
|
|
80
|
+
|
|
81
|
+
# Check status of an existing request
|
|
82
|
+
onehouse-cli --check-status "<REQUEST_ID>" --profile myprofile
|
|
83
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
onehouse_cli/__init__.py
|
|
4
|
+
onehouse_cli/cli.py
|
|
5
|
+
onehouse_cli/configure.py
|
|
6
|
+
onehouse_cli.egg-info/PKG-INFO
|
|
7
|
+
onehouse_cli.egg-info/SOURCES.txt
|
|
8
|
+
onehouse_cli.egg-info/dependency_links.txt
|
|
9
|
+
onehouse_cli.egg-info/entry_points.txt
|
|
10
|
+
onehouse_cli.egg-info/requires.txt
|
|
11
|
+
onehouse_cli.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
onehouse_cli
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "onehouse-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A command-line tool for executing SQL commands against the Onehouse API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
license = {text = "Other/Proprietary"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Sagar Lakshmipathy", email = "18vidhyasagar@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["onehouse", "cli", "sql"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.7",
|
|
22
|
+
"Programming Language :: Python :: 3.8",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"requests",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
onehouse-cli = "onehouse_cli.cli:main"
|
|
33
|
+
onehouse-cli-configure = "onehouse_cli.configure:main"
|