adaptsapi 0.1.7__tar.gz → 0.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,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adaptsapi
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: CLI to enqueue triggers via internal API Gateway → SNS
5
5
  Home-page: https://github.com/adaptsai/adaptsapi
6
6
  Author: adapts
7
7
  Author-email: "VerifyAI Inc." <dev@adapts.ai>
8
8
  License-Expression: LicenseRef-Proprietary
9
- Project-URL: Homepage, https://github.com/adaptsai/adaptsapi
10
- Project-URL: Source, https://github.com/adaptsai/adaptsapi
9
+ Project-URL: Homepage, https://adapts.ai/
10
+ Project-URL: Source, https://github.com/AdaptsAI/adapts_api_client
11
11
  Keywords: adapts,api,cli,sns
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Operating System :: OS Independent
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "adaptsapi"
7
- version = "0.1.7"
7
+ version = "0.1.8"
8
8
  description = "CLI to enqueue triggers via internal API Gateway → SNS"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
 
@@ -27,8 +27,8 @@ classifiers = [
27
27
  ]
28
28
 
29
29
  [project.urls]
30
- "Homepage" = "https://github.com/adaptsai/adaptsapi"
31
- "Source" = "https://github.com/adaptsai/adaptsapi"
30
+ "Homepage" = "https://adapts.ai/"
31
+ "Source" = "https://github.com/AdaptsAI/adapts_api_client"
32
32
 
33
33
  [project.scripts]
34
34
  adaptsapi = "adaptsapi.cli:main"
@@ -62,7 +62,7 @@ ensure_newline_before_comments = true
62
62
  skip = [".venv", "build", "dist", "*.egg-info"]
63
63
 
64
64
  [tool.mypy]
65
- python_version = "3.10"
65
+ python_version = "0.1.8"
66
66
  warn_return_any = true
67
67
  warn_unused_configs = true
68
68
  disallow_untyped_defs = true
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="adaptsapi",
5
- version="0.1.7",
5
+ version="0.1.8",
6
6
  author="adapts",
7
7
  author_email="dev@adapts.ai",
8
8
  description="CLI to enqueue triggers via internal API Gateway → SNS",
@@ -5,6 +5,7 @@ import json
5
5
  from adaptsapi.generate_docs import post
6
6
  from adaptsapi.config import load_token, load_default_endpoint
7
7
 
8
+
8
9
  def main():
9
10
  default_ep = load_default_endpoint()
10
11
  parser = argparse.ArgumentParser(prog="adaptsapi")
@@ -12,10 +13,13 @@ def main():
12
13
  "--endpoint",
13
14
  default=default_ep,
14
15
  required=(default_ep is None),
15
- help="Full URL of the API endpoint (default from ./config.json)"
16
+ help="Full URL of the API endpoint (default from ./config.json)",
17
+ )
18
+ parser.add_argument(
19
+ "--payload-file",
20
+ type=argparse.FileType("r"),
21
+ help="Path to JSON payload file (overrides inline --data)",
16
22
  )
17
- parser.add_argument("--payload-file", type=argparse.FileType('r'),
18
- help="Path to JSON payload file (overrides inline --data)")
19
23
  parser.add_argument("--data", help="Inline JSON payload string")
20
24
  parser.add_argument("--timeout", type=int, default=30)
21
25
  args = parser.parse_args()
@@ -36,5 +40,6 @@ def main():
36
40
  sys.exit(resp.status_code)
37
41
  print(resp.text)
38
42
 
43
+
39
44
  if __name__ == "__main__":
40
- main()
45
+ main()
@@ -6,10 +6,13 @@ from pathlib import Path
6
6
  # Point at config.json in whatever directory you run the CLI from
7
7
  CONFIG_PATH = Path.cwd() / "config.json"
8
8
 
9
+
9
10
  class ConfigError(Exception):
10
11
  """Raised when no token can be found or loaded."""
12
+
11
13
  pass
12
14
 
15
+
13
16
  def load_token() -> str:
14
17
  # 1) ENV override
15
18
  token = os.getenv("ADAPTS_API_TOKEN")
@@ -35,6 +38,7 @@ def load_token() -> str:
35
38
  print(f"Saved token to {CONFIG_PATH}")
36
39
  return token
37
40
 
41
+
38
42
  def load_default_endpoint() -> str | None:
39
43
  """Return `endpoint` from ./config.json if present, else None."""
40
44
  if CONFIG_PATH.exists():
@@ -45,6 +49,7 @@ def load_default_endpoint() -> str | None:
45
49
  return None
46
50
  return None
47
51
 
52
+
48
53
  def _ensure_config_dir_and_write(obj: dict) -> None:
49
54
  """Helper to merge+write config.json in cwd."""
50
55
  # read existing if any
@@ -55,4 +60,4 @@ def _ensure_config_dir_and_write(obj: dict) -> None:
55
60
  except json.JSONDecodeError:
56
61
  existing = {}
57
62
  merged = {**existing, **obj}
58
- CONFIG_PATH.write_text(json.dumps(merged, indent=2))
63
+ CONFIG_PATH.write_text(json.dumps(merged, indent=2))
@@ -3,19 +3,20 @@ import requests
3
3
  from typing import Dict, Any
4
4
  from datetime import date
5
5
  import uuid
6
+ import os
6
7
 
7
8
  # regex for a very basic email validation
8
9
  _EMAIL_RE = re.compile(r"^[^@]+@[^@]+\.[^@]+$")
9
10
 
10
11
 
11
12
  class PayloadValidationError(ValueError):
12
- """Raised when the payload is missing required fields or
13
+ """Raised when the payload is missing required fields or
13
14
  has invalid values."""
14
15
 
15
16
 
16
17
  def _validate_payload(payload: Dict[str, Any]) -> None:
17
18
  # Top‐level required fields
18
- required = {
19
+ required = {
19
20
  "email_address": str,
20
21
  "user_name": str,
21
22
  "repo_object": dict,
@@ -62,18 +63,16 @@ def _validate_payload(payload: Dict[str, Any]) -> None:
62
63
  if field not in repo:
63
64
  raise PayloadValidationError(f"Missing repo_object.{field}")
64
65
  if not isinstance(repo[field], typ):
65
- raise PayloadValidationError(
66
- f"repo_object.{field} must be {typ.__name__}"
67
- )
66
+ raise PayloadValidationError(f"repo_object.{field} must be {typ.__name__}")
68
67
 
69
68
  # optional loop
70
69
  for field, typ in optional_repo_fields.items():
71
- if field in repo and repo[field] is not None and not isinstance(
72
- repo[field], typ
70
+ if (
71
+ field in repo
72
+ and repo[field] is not None
73
+ and not isinstance(repo[field], typ)
73
74
  ):
74
- raise PayloadValidationError(
75
- f"repo_object.{field} must be {typ.__name__}"
76
- )
75
+ raise PayloadValidationError(f"repo_object.{field} must be {typ.__name__}")
77
76
 
78
77
  # you can add more checks here (URL validation, branch name patterns, etc.)
79
78
 
@@ -100,13 +99,12 @@ def _populate_metadata(payload: Dict[str, Any]) -> None:
100
99
  payload["request_id"] = str(uuid.uuid4())
101
100
  payload["request_type"] = "code_to_wiki"
102
101
  payload["status"] = "pending"
102
+ if payload["service_name"] is None or payload["service_name"] == "":
103
+ payload["service_name"] = payload["repo_object"]["repository_name"]
103
104
 
104
105
 
105
106
  def post(
106
- endpoint: str,
107
- token: str,
108
- payload: Dict[str, Any],
109
- timeout: int = 30
107
+ endpoint: str, token: str, payload: Dict[str, Any], timeout: int = 30
110
108
  ) -> requests.Response:
111
109
  """
112
110
  Send a POST with validated payload and autopopulated metadata.
@@ -126,10 +124,20 @@ def post(
126
124
  "Content-Type": "application/json",
127
125
  }
128
126
 
127
+ try:
128
+ ip_address = requests.get("https://api.ipify.org").text
129
+ geo = requests.get(f"https://ipapi.co/{ip_address}/json/").json()
130
+ country = geo.get("country_name", "")
131
+ if ip_address and ip_address != "":
132
+ headers["x-ip"] = ip_address
133
+ if country and country != "":
134
+ headers["x-country"] = country
135
+ except Exception as e:
136
+ pass
137
+
129
138
  print("payload:", payload)
130
139
  print("testing")
131
- response = requests.post(endpoint, json=payload, headers=headers,
132
- timeout=timeout)
140
+ response = requests.post(endpoint, json=payload, headers=headers, timeout=timeout)
133
141
  print("response:", response.json())
134
142
  print("testing")
135
- return response
143
+ return response
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adaptsapi
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: CLI to enqueue triggers via internal API Gateway → SNS
5
5
  Home-page: https://github.com/adaptsai/adaptsapi
6
6
  Author: adapts
7
7
  Author-email: "VerifyAI Inc." <dev@adapts.ai>
8
8
  License-Expression: LicenseRef-Proprietary
9
- Project-URL: Homepage, https://github.com/adaptsai/adaptsapi
10
- Project-URL: Source, https://github.com/adaptsai/adaptsapi
9
+ Project-URL: Homepage, https://adapts.ai/
10
+ Project-URL: Source, https://github.com/AdaptsAI/adapts_api_client
11
11
  Keywords: adapts,api,cli,sns
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Operating System :: OS Independent
@@ -9,25 +9,25 @@ from adaptsapi.cli import main
9
9
 
10
10
  class TestCLIArgumentParsing:
11
11
  """Test CLI argument parsing functionality"""
12
-
13
- @patch('adaptsapi.cli.load_default_endpoint')
14
- @patch('adaptsapi.cli.load_token')
15
- @patch('adaptsapi.cli.post')
12
+
13
+ @patch("adaptsapi.cli.load_default_endpoint")
14
+ @patch("adaptsapi.cli.load_token")
15
+ @patch("adaptsapi.cli.post")
16
16
  def test_basic_cli_with_data(self, mock_post, mock_load_token, mock_load_endpoint):
17
17
  """Test CLI with inline JSON data"""
18
18
  mock_load_endpoint.return_value = "https://api.example.com/test"
19
19
  mock_load_token.return_value = "test-token"
20
-
20
+
21
21
  mock_response = Mock()
22
22
  mock_response.status_code = 200
23
23
  mock_response.text = '{"status": "success"}'
24
24
  mock_post.return_value = mock_response
25
-
25
+
26
26
  # Test with inline JSON data
27
- with patch('sys.argv', ['adaptsapi', '--data', '{"test": "data"}']):
28
- with patch('sys.stdout', new=StringIO()) as mock_stdout:
27
+ with patch("sys.argv", ["adaptsapi", "--data", '{"test": "data"}']):
28
+ with patch("sys.stdout", new=StringIO()) as mock_stdout:
29
29
  main()
30
-
30
+
31
31
  # Verify post was called correctly
32
32
  mock_post.assert_called_once()
33
33
  call_args = mock_post.call_args
@@ -35,160 +35,183 @@ class TestCLIArgumentParsing:
35
35
  assert call_args[0][1] == "test-token"
36
36
  assert call_args[0][2] == {"test": "data"}
37
37
  assert call_args[1]["timeout"] == 30 # default timeout
38
-
38
+
39
39
  # Verify output
40
40
  assert mock_stdout.getvalue().strip() == '{"status": "success"}'
41
-
42
- @patch('adaptsapi.cli.load_default_endpoint')
43
- @patch('adaptsapi.cli.load_token')
44
- @patch('adaptsapi.cli.post')
45
- def test_cli_with_payload_file(self, mock_post, mock_load_token, mock_load_endpoint):
41
+
42
+ @patch("adaptsapi.cli.load_default_endpoint")
43
+ @patch("adaptsapi.cli.load_token")
44
+ @patch("adaptsapi.cli.post")
45
+ def test_cli_with_payload_file(
46
+ self, mock_post, mock_load_token, mock_load_endpoint
47
+ ):
46
48
  """Test CLI with payload file"""
47
49
  mock_load_endpoint.return_value = "https://api.example.com/test"
48
50
  mock_load_token.return_value = "test-token"
49
-
51
+
50
52
  mock_response = Mock()
51
53
  mock_response.status_code = 200
52
54
  mock_response.text = '{"status": "success"}'
53
55
  mock_post.return_value = mock_response
54
-
56
+
55
57
  # Create a temporary payload file
56
- with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
58
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
57
59
  json.dump({"test": "data", "nested": {"value": 123}}, f)
58
60
  temp_file_path = f.name
59
-
61
+
60
62
  try:
61
- with patch('sys.argv', ['adaptsapi', '--payload-file', temp_file_path]):
62
- with patch('sys.stdout', new=StringIO()) as mock_stdout:
63
+ with patch("sys.argv", ["adaptsapi", "--payload-file", temp_file_path]):
64
+ with patch("sys.stdout", new=StringIO()) as mock_stdout:
63
65
  main()
64
-
66
+
65
67
  # Verify post was called with correct payload
66
68
  mock_post.assert_called_once()
67
69
  call_args = mock_post.call_args
68
70
  assert call_args[0][2] == {"test": "data", "nested": {"value": 123}}
69
71
  finally:
70
72
  os.unlink(temp_file_path)
71
-
72
- @patch('adaptsapi.cli.load_default_endpoint')
73
- @patch('adaptsapi.cli.load_token')
74
- @patch('adaptsapi.cli.post')
75
- def test_cli_with_custom_endpoint(self, mock_post, mock_load_token, mock_load_endpoint):
73
+
74
+ @patch("adaptsapi.cli.load_default_endpoint")
75
+ @patch("adaptsapi.cli.load_token")
76
+ @patch("adaptsapi.cli.post")
77
+ def test_cli_with_custom_endpoint(
78
+ self, mock_post, mock_load_token, mock_load_endpoint
79
+ ):
76
80
  """Test CLI with custom endpoint"""
77
81
  mock_load_endpoint.return_value = None # No default endpoint
78
82
  mock_load_token.return_value = "test-token"
79
-
83
+
80
84
  mock_response = Mock()
81
85
  mock_response.status_code = 200
82
86
  mock_response.text = '{"status": "success"}'
83
87
  mock_post.return_value = mock_response
84
-
85
- with patch('sys.argv', ['adaptsapi', '--endpoint', 'https://custom.api.com', '--data', '{"test": "data"}']):
86
- with patch('sys.stdout', new=StringIO()):
88
+
89
+ with patch(
90
+ "sys.argv",
91
+ [
92
+ "adaptsapi",
93
+ "--endpoint",
94
+ "https://custom.api.com",
95
+ "--data",
96
+ '{"test": "data"}',
97
+ ],
98
+ ):
99
+ with patch("sys.stdout", new=StringIO()):
87
100
  main()
88
-
101
+
89
102
  # Verify custom endpoint was used
90
103
  mock_post.assert_called_once()
91
104
  call_args = mock_post.call_args
92
105
  assert call_args[0][0] == "https://custom.api.com"
93
-
94
- @patch('adaptsapi.cli.load_default_endpoint')
95
- @patch('adaptsapi.cli.load_token')
96
- @patch('adaptsapi.cli.post')
97
- def test_cli_with_custom_timeout(self, mock_post, mock_load_token, mock_load_endpoint):
106
+
107
+ @patch("adaptsapi.cli.load_default_endpoint")
108
+ @patch("adaptsapi.cli.load_token")
109
+ @patch("adaptsapi.cli.post")
110
+ def test_cli_with_custom_timeout(
111
+ self, mock_post, mock_load_token, mock_load_endpoint
112
+ ):
98
113
  """Test CLI with custom timeout"""
99
114
  mock_load_endpoint.return_value = "https://api.example.com/test"
100
115
  mock_load_token.return_value = "test-token"
101
-
116
+
102
117
  mock_response = Mock()
103
118
  mock_response.status_code = 200
104
119
  mock_response.text = '{"status": "success"}'
105
120
  mock_post.return_value = mock_response
106
-
107
- with patch('sys.argv', ['adaptsapi', '--timeout', '60', '--data', '{"test": "data"}']):
108
- with patch('sys.stdout', new=StringIO()):
121
+
122
+ with patch(
123
+ "sys.argv", ["adaptsapi", "--timeout", "60", "--data", '{"test": "data"}']
124
+ ):
125
+ with patch("sys.stdout", new=StringIO()):
109
126
  main()
110
-
127
+
111
128
  # Verify custom timeout was used
112
129
  mock_post.assert_called_once()
113
130
  call_args = mock_post.call_args
114
131
  assert call_args[1]["timeout"] == 60
115
-
116
- @patch('adaptsapi.cli.load_default_endpoint')
117
- @patch('adaptsapi.cli.load_token')
132
+
133
+ @patch("adaptsapi.cli.load_default_endpoint")
134
+ @patch("adaptsapi.cli.load_token")
118
135
  def test_cli_missing_payload(self, mock_load_token, mock_load_endpoint):
119
136
  """Test CLI error when no payload is provided"""
120
137
  mock_load_endpoint.return_value = "https://api.example.com/test"
121
138
  mock_load_token.return_value = "test-token"
122
-
123
- with patch('sys.argv', ['adaptsapi']):
124
- with patch('sys.stderr', new=StringIO()) as mock_stderr:
139
+
140
+ with patch("sys.argv", ["adaptsapi"]):
141
+ with patch("sys.stderr", new=StringIO()) as mock_stderr:
125
142
  with pytest.raises(SystemExit) as exc_info:
126
143
  main()
127
-
144
+
128
145
  assert exc_info.value.code == 1
129
146
  assert "Error: must specify --data or --payload-file" in mock_stderr.getvalue()
130
-
131
- @patch('adaptsapi.cli.load_default_endpoint')
132
- @patch('adaptsapi.cli.load_token')
133
- @patch('adaptsapi.cli.post')
134
- def test_cli_api_error_response(self, mock_post, mock_load_token, mock_load_endpoint):
147
+
148
+ @patch("adaptsapi.cli.load_default_endpoint")
149
+ @patch("adaptsapi.cli.load_token")
150
+ @patch("adaptsapi.cli.post")
151
+ def test_cli_api_error_response(
152
+ self, mock_post, mock_load_token, mock_load_endpoint
153
+ ):
135
154
  """Test CLI handling of API error responses"""
136
155
  mock_load_endpoint.return_value = "https://api.example.com/test"
137
156
  mock_load_token.return_value = "test-token"
138
-
157
+
139
158
  mock_response = Mock()
140
159
  mock_response.status_code = 400
141
160
  mock_response.text = '{"error": "Bad Request"}'
142
161
  mock_post.return_value = mock_response
143
-
144
- with patch('sys.argv', ['adaptsapi', '--data', '{"test": "data"}']):
145
- with patch('sys.stderr', new=StringIO()) as mock_stderr:
162
+
163
+ with patch("sys.argv", ["adaptsapi", "--data", '{"test": "data"}']):
164
+ with patch("sys.stderr", new=StringIO()) as mock_stderr:
146
165
  with pytest.raises(SystemExit) as exc_info:
147
166
  main()
148
-
167
+
149
168
  assert exc_info.value.code == 400
150
- assert "Error 400: {\"error\": \"Bad Request\"}" in mock_stderr.getvalue()
151
-
152
- @patch('adaptsapi.cli.load_default_endpoint')
153
- @patch('adaptsapi.cli.load_token')
154
- @patch('adaptsapi.cli.post')
155
- def test_cli_invalid_json_data(self, mock_post, mock_load_token, mock_load_endpoint):
169
+ assert 'Error 400: {"error": "Bad Request"}' in mock_stderr.getvalue()
170
+
171
+ @patch("adaptsapi.cli.load_default_endpoint")
172
+ @patch("adaptsapi.cli.load_token")
173
+ @patch("adaptsapi.cli.post")
174
+ def test_cli_invalid_json_data(
175
+ self, mock_post, mock_load_token, mock_load_endpoint
176
+ ):
156
177
  """Test CLI error handling with invalid JSON data"""
157
178
  mock_load_endpoint.return_value = "https://api.example.com/test"
158
179
  mock_load_token.return_value = "test-token"
159
-
160
- with patch('sys.argv', ['adaptsapi', '--data', 'invalid json']):
180
+
181
+ with patch("sys.argv", ["adaptsapi", "--data", "invalid json"]):
161
182
  with pytest.raises(json.JSONDecodeError):
162
183
  main()
163
-
164
- @patch('adaptsapi.cli.load_default_endpoint')
165
- @patch('adaptsapi.cli.load_token')
184
+
185
+ @patch("adaptsapi.cli.load_default_endpoint")
186
+ @patch("adaptsapi.cli.load_token")
166
187
  def test_cli_missing_endpoint_no_default(self, mock_load_token, mock_load_endpoint):
167
188
  """Test CLI error when no endpoint is provided and no default exists"""
168
189
  mock_load_endpoint.return_value = None
169
190
  mock_load_token.return_value = "test-token"
170
-
171
- with patch('sys.argv', ['adaptsapi', '--data', '{"test": "data"}']):
191
+
192
+ with patch("sys.argv", ["adaptsapi", "--data", '{"test": "data"}']):
172
193
  with pytest.raises(SystemExit):
173
194
  main()
174
195
 
175
196
 
176
197
  class TestCLIIntegration:
177
198
  """Integration tests for CLI functionality"""
178
-
179
- @patch('adaptsapi.cli.load_default_endpoint')
180
- @patch('adaptsapi.cli.load_token')
181
- @patch('adaptsapi.cli.post')
182
- def test_cli_with_real_payload_structure(self, mock_post, mock_load_token, mock_load_endpoint):
199
+
200
+ @patch("adaptsapi.cli.load_default_endpoint")
201
+ @patch("adaptsapi.cli.load_token")
202
+ @patch("adaptsapi.cli.post")
203
+ def test_cli_with_real_payload_structure(
204
+ self, mock_post, mock_load_token, mock_load_endpoint
205
+ ):
183
206
  """Test CLI with a realistic payload structure"""
184
207
  mock_load_endpoint.return_value = "https://api.example.com/test"
185
208
  mock_load_token.return_value = "test-token"
186
-
209
+
187
210
  mock_response = Mock()
188
211
  mock_response.status_code = 200
189
212
  mock_response.text = '{"status": "success", "request_id": "123"}'
190
213
  mock_post.return_value = mock_response
191
-
214
+
192
215
  real_payload = {
193
216
  "email_address": "test@example.com",
194
217
  "user_name": "testuser",
@@ -201,23 +224,23 @@ class TestCLIIntegration:
201
224
  "language": "Python",
202
225
  "is_private": False,
203
226
  "git_provider_type": "github",
204
- "refresh_token": "1234567890"
205
- }
227
+ "refresh_token": "1234567890",
228
+ },
206
229
  }
207
-
208
- with patch('sys.argv', ['adaptsapi', '--data', json.dumps(real_payload)]):
209
- with patch('sys.stdout', new=StringIO()) as mock_stdout:
230
+
231
+ with patch("sys.argv", ["adaptsapi", "--data", json.dumps(real_payload)]):
232
+ with patch("sys.stdout", new=StringIO()) as mock_stdout:
210
233
  main()
211
-
234
+
212
235
  # Verify the payload was processed correctly
213
236
  mock_post.assert_called_once()
214
237
  call_args = mock_post.call_args
215
238
  sent_payload = call_args[0][2]
216
-
239
+
217
240
  # Check that the original payload structure is preserved
218
241
  assert sent_payload["email_address"] == "test@example.com"
219
242
  assert sent_payload["user_name"] == "testuser"
220
243
  assert sent_payload["repo_object"]["repository_name"] == "test-repo"
221
-
244
+
222
245
  # Note: The post function adds metadata, but we're testing the CLI integration
223
246
  # The actual metadata addition is tested in test_generate_docs.py