gl-connectors-cli 0.1.0__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.
@@ -0,0 +1,5 @@
1
+ """GL Connectors CLI module for managing integrations and authentication."""
2
+
3
+ from gl_connectors_cli.main import main
4
+
5
+ __all__ = ["main"]
@@ -0,0 +1,12 @@
1
+ """CLI entry point.
2
+
3
+ Authors:
4
+ GL Connectors Team
5
+ """
6
+
7
+ import sys
8
+
9
+ from gl_connectors_cli.main import main
10
+
11
+ if __name__ == "__main__":
12
+ sys.exit(main())
@@ -0,0 +1,19 @@
1
+ """GL Connectors CLI API clients.
2
+
3
+ Author:
4
+ I Gusti Ngurah Gana Untaran (i.gusti.n.g.untaran@gdplabs.id)
5
+ """
6
+
7
+ from gl_connectors_cli.api.auth import AuthAPIClient
8
+ from gl_connectors_cli.api.integrations import IntegrationsAPIClient
9
+ from gl_connectors_cli.api.models import IntegrationDetail, Token, User
10
+ from gl_connectors_cli.api.users import UsersAPIClient
11
+
12
+ __all__ = [
13
+ "AuthAPIClient",
14
+ "UsersAPIClient",
15
+ "IntegrationsAPIClient",
16
+ "User",
17
+ "Token",
18
+ "IntegrationDetail",
19
+ ]
@@ -0,0 +1,47 @@
1
+ """Authentication API client for GL Connectors CLI.
2
+
3
+ Author:
4
+ I Gusti Ngurah Gana Untaran (i.gusti.n.g.untaran@gdplabs.id)
5
+ """
6
+
7
+ from gl_connectors_cli.api.base import BaseAPIClient
8
+ from gl_connectors_cli.api.models import Token
9
+ from gl_connectors_cli.constants import API_KEY_HEADER, HTTP_POST
10
+ from gl_connectors_cli.utils import CLIError
11
+
12
+
13
+ class AuthAPIClient(BaseAPIClient):
14
+ """Authentication API client."""
15
+
16
+ def authenticate_user(self, client_key: str, identifier: str, secret: str) -> Token:
17
+ """Authenticate user and get JWT token.
18
+
19
+ Args:
20
+ client_key: Client API key
21
+ identifier: User identifier
22
+ secret: User secret
23
+
24
+ Returns:
25
+ Authentication token information
26
+
27
+ Raises:
28
+ CLIError: If authentication fails
29
+
30
+ """
31
+ headers = {
32
+ API_KEY_HEADER: client_key,
33
+ }
34
+
35
+ data = {"identifier": identifier, "secret": secret}
36
+
37
+ try:
38
+ response = self._make_request(HTTP_POST, "/clients/oauth-token", headers=headers, data=data)
39
+ token_data = response.get("data", None)
40
+
41
+ if not token_data:
42
+ raise CLIError("Authentication error. Please check your credentials and try again.")
43
+
44
+ return Token(**token_data)
45
+
46
+ except Exception as e:
47
+ raise CLIError(str(e)) from e
@@ -0,0 +1,86 @@
1
+ """Base API client for GL Connectors CLI.
2
+
3
+ Author:
4
+ I Gusti Ngurah Gana Untaran (i.gusti.n.g.untaran@gdplabs.id)
5
+ """
6
+
7
+ import json
8
+ from typing import Any, Dict, Optional
9
+
10
+ import requests
11
+
12
+ from gl_connectors_cli.constants import HTTP_DELETE, HTTP_GET, HTTP_POST, HTTP_PUT
13
+ from gl_connectors_cli.utils import CLIError
14
+
15
+
16
+ class BaseAPIClient:
17
+ """Base API client with shared request functionality."""
18
+
19
+ def __init__(self, base_url: str):
20
+ """Initialize base API client.
21
+
22
+ Args:
23
+ base_url: Connector base URL
24
+
25
+ """
26
+ self.base_url = base_url.rstrip("/")
27
+ self.session = requests.Session()
28
+
29
+ def _make_request(
30
+ self,
31
+ method: str,
32
+ endpoint: str,
33
+ headers: Optional[Dict[str, str]] = None,
34
+ data: Optional[Dict[str, Any]] = None,
35
+ params: Optional[Dict[str, str]] = None,
36
+ ) -> Dict[str, Any]:
37
+ """Make HTTP request to Connector.
38
+
39
+ Args:
40
+ method: HTTP method (GET, POST, DELETE, etc.)
41
+ endpoint: API endpoint path
42
+ headers: Request headers
43
+ data: Request body data
44
+ params: URL parameters
45
+
46
+ Returns:
47
+ Response JSON data
48
+
49
+ Raises:
50
+ CLIError: If request fails
51
+
52
+ """
53
+ url = f"{self.base_url}{endpoint}"
54
+
55
+ try:
56
+ if method.upper() == HTTP_GET:
57
+ response = self.session.get(url, headers=headers, params=params)
58
+ elif method.upper() == HTTP_POST:
59
+ response = self.session.post(url, headers=headers, json=data, params=params)
60
+ elif method.upper() == HTTP_DELETE:
61
+ response = self.session.delete(url, headers=headers, params=params)
62
+ elif method.upper() == HTTP_PUT:
63
+ response = self.session.put(url, headers=headers, json=data, params=params)
64
+ else:
65
+ raise CLIError(f"Unsupported HTTP method: {method}")
66
+
67
+ response_data = response.json()
68
+ if not response.ok or ("success" in response_data and response_data["success"] is False):
69
+ error_data = response_data.get("error", {})
70
+ error_message = error_data.get("message", response.status_code)
71
+ raise CLIError(f"API error: {error_message}")
72
+
73
+ return response_data
74
+
75
+ except requests.exceptions.ConnectionError as e:
76
+ raise CLIError(f"Failed to connect to {self.base_url}: {str(e)}") from e
77
+ except requests.exceptions.Timeout as e:
78
+ raise CLIError(f"Request timeout: {str(e)}") from e
79
+ except requests.exceptions.RequestException as e:
80
+ raise CLIError(f"Request failed: {str(e)}") from e
81
+ except json.JSONDecodeError as e:
82
+ raise CLIError(f"Invalid JSON response from server: {str(e)}") from e
83
+ except CLIError as e:
84
+ raise e
85
+ except Exception as e:
86
+ raise CLIError(f"An unexpected error occurred: {str(e)}") from e
@@ -0,0 +1,199 @@
1
+ """Integrations API client for GL Connectors CLI.
2
+
3
+ Author:
4
+ I Gusti Ngurah Gana Untaran (i.gusti.n.g.untaran@gdplabs.id)
5
+ """
6
+
7
+ from typing import Any, Dict, List
8
+
9
+ from gl_connectors_cli.api.base import BaseAPIClient
10
+ from gl_connectors_cli.api.models import IntegrationDetail
11
+ from gl_connectors_cli.constants import (
12
+ API_KEY_HEADER,
13
+ AUTHORIZATION_HEADER,
14
+ BEARER_PREFIX,
15
+ HTTP_DELETE,
16
+ HTTP_GET,
17
+ HTTP_POST,
18
+ )
19
+ from gl_connectors_cli.utils import CLIError
20
+
21
+
22
+ class IntegrationsAPIClient(BaseAPIClient):
23
+ """Integrations API client."""
24
+
25
+ def get_connectors(self, client_key: str) -> List[str]:
26
+ """Get all available connectors.
27
+
28
+ Args:
29
+ client_key: Client API key
30
+
31
+ Returns:
32
+ List of available connectors
33
+
34
+ Raises:
35
+ CLIError: If request fails
36
+
37
+ """
38
+ headers = {AUTHORIZATION_HEADER: f"{BEARER_PREFIX} {client_key}"}
39
+
40
+ try:
41
+ response = self._make_request(HTTP_GET, "/connectors", headers=headers)
42
+
43
+ return list(response.keys())
44
+
45
+ except Exception as e:
46
+ raise CLIError(f"Failed to get connectors: {str(e)}") from e
47
+
48
+ def check_integration_status(self, client_key: str, connector_name: str, token: str) -> bool:
49
+ """Check if integration exists for a connector.
50
+
51
+ Args:
52
+ client_key: Client API key
53
+ connector_name: Name of the connector
54
+ token: User JWT token
55
+
56
+ Returns:
57
+ Integration status information (True if integration exists, False otherwise)
58
+
59
+ Raises:
60
+ CLIError: If request fails
61
+
62
+ """
63
+ headers = {API_KEY_HEADER: client_key, AUTHORIZATION_HEADER: f"{BEARER_PREFIX} {token}"}
64
+
65
+ try:
66
+ response = self._make_request(HTTP_GET, f"/connectors/{connector_name}/integration-exists", headers=headers)
67
+ return response.get("data", {}).get("has_integration", False)
68
+
69
+ except Exception as e:
70
+ raise CLIError(f"Failed to check integration status: {str(e)}") from e
71
+
72
+ def initiate_integration(self, client_key: str, connector_name: str, token: str) -> str:
73
+ """Initiate OAuth integration for a connector.
74
+
75
+ Args:
76
+ client_key: Client API key
77
+ connector_name: Name of the connector
78
+ token: User JWT token
79
+
80
+ Returns:
81
+ str: Integration initiation response (contains OAuth URL)
82
+
83
+ Raises:
84
+ CLIError: If request fails
85
+
86
+ """
87
+ headers = {
88
+ API_KEY_HEADER: client_key,
89
+ AUTHORIZATION_HEADER: f"{BEARER_PREFIX} {token}",
90
+ }
91
+
92
+ callback_url = f"{self.base_url}/connectors/{connector_name}/success-authorize-callback"
93
+
94
+ data = {"callback_url": callback_url}
95
+
96
+ try:
97
+ response = self._make_request(
98
+ HTTP_POST, f"/connectors/{connector_name}/integrations", headers=headers, data=data
99
+ )
100
+
101
+ if not response.get("data", {}).get("url"):
102
+ raise CLIError("Failed to initiate integration.")
103
+
104
+ return response.get("data", {}).get("url", "")
105
+
106
+ except Exception as e:
107
+ raise CLIError(f"Failed to initiate integration: {str(e)}") from e
108
+
109
+ def remove_integration(
110
+ self, client_key: str, connector_name: str, user_identifier: str, token: str
111
+ ) -> Dict[str, Any]:
112
+ """Remove integration for a connector and specific user identifier.
113
+
114
+ Args:
115
+ client_key: Client API key
116
+ connector_name: Name of the connector
117
+ user_identifier: User identifier to specify which integration to remove
118
+ token: User JWT token
119
+
120
+ Returns:
121
+ Removal response
122
+
123
+ Raises:
124
+ CLIError: If request fails
125
+
126
+ """
127
+ headers = {API_KEY_HEADER: client_key, AUTHORIZATION_HEADER: f"{BEARER_PREFIX} {token}"}
128
+
129
+ try:
130
+ response = self._make_request(
131
+ HTTP_DELETE, f"/connectors/{connector_name}/integrations/{user_identifier}", headers=headers
132
+ )
133
+ return response.get("data", {})
134
+
135
+ except Exception as e:
136
+ raise CLIError(f"Failed to remove integration: {str(e)}") from e
137
+
138
+ def get_integration_details_by_identifier(
139
+ self, client_key: str, connector: str, user_identifier: str, token: str
140
+ ) -> IntegrationDetail:
141
+ """Get a specific integration by user identifier.
142
+
143
+ Args:
144
+ client_key: Client API key
145
+ connector: Connector name
146
+ user_identifier: The third-party service user identifier (e.g., GitHub username, Google email)
147
+ token: User token
148
+
149
+ Returns:
150
+ Integration details including access token
151
+
152
+ Raises:
153
+ CLIError: If request fails or integration not found
154
+
155
+ """
156
+ headers = {API_KEY_HEADER: client_key, AUTHORIZATION_HEADER: f"{BEARER_PREFIX} {token}"}
157
+
158
+ try:
159
+ response = self._make_request(
160
+ HTTP_GET, f"/connectors/{connector}/integrations/{user_identifier}", headers=headers
161
+ )
162
+ data = response.get("data", {})
163
+ if data.get("success") is False:
164
+ raise CLIError({data.get("error", "Unknown error")})
165
+
166
+ return IntegrationDetail(**response.get("data", {}))
167
+
168
+ except Exception as e:
169
+ raise CLIError(f"Failed to get integration for {user_identifier} in {connector}: {str(e)}") from e
170
+
171
+ def set_selected_integration(
172
+ self, client_key: str, connector_name: str, user_identifier: str, token: str
173
+ ) -> Dict[str, Any]:
174
+ """Set integration as selected/unselected.
175
+
176
+ Args:
177
+ client_key: Client API key
178
+ connector_name: Name of the connector
179
+ user_identifier: User identifier for the integration
180
+ token: User JWT token
181
+
182
+ Returns:
183
+ Update response
184
+
185
+ Raises:
186
+ CLIError: If request fails
187
+
188
+ """
189
+ headers = {API_KEY_HEADER: client_key, AUTHORIZATION_HEADER: f"{BEARER_PREFIX} {token}"}
190
+ data = {"selected": True}
191
+
192
+ try:
193
+ response = self._make_request(
194
+ HTTP_POST, f"/connectors/{connector_name}/integrations/{user_identifier}", headers=headers, data=data
195
+ )
196
+ return response.get("data", {})
197
+
198
+ except Exception as e:
199
+ raise CLIError(f"Failed to update integration selection: {str(e)}") from e
@@ -0,0 +1,47 @@
1
+ """Shared data models for GL Connectors CLI API clients.
2
+
3
+ Author:
4
+ I Gusti Ngurah Gana Untaran (i.gusti.n.g.untaran@gdplabs.id)
5
+ """
6
+
7
+ from datetime import datetime
8
+ from typing import Any, Dict, List
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+
13
+ class User(BaseModel):
14
+ """GL Connectors user information."""
15
+
16
+ id: str
17
+ identifier: str = ""
18
+ secret_preview: str = ""
19
+ is_active: bool = True
20
+ client_id: str = ""
21
+ integrations: List[Dict[str, Any]] = Field(default_factory=list)
22
+
23
+
24
+ class CreatedUser(User):
25
+ """GL Connectors user information with secret."""
26
+
27
+ secret: str = ""
28
+
29
+
30
+ class Token(BaseModel):
31
+ """GL Connectors authentication token."""
32
+
33
+ token: str
34
+ token_type: str = "Bearer"
35
+ expires_at: datetime = Field(default_factory=datetime.now)
36
+ is_revoked: bool = False
37
+ user_id: str = ""
38
+
39
+
40
+ class IntegrationDetail(BaseModel):
41
+ """Integration detail."""
42
+
43
+ connector: str
44
+ user_identifier: str
45
+ auth_string: str
46
+ auth_scopes: List[str]
47
+ selected: bool = False
@@ -0,0 +1,66 @@
1
+ """Users API client for GL Connectors CLI.
2
+
3
+ Author:
4
+ I Gusti Ngurah Gana Untaran (i.gusti.n.g.untaran@gdplabs.id)
5
+ """
6
+
7
+ from gl_connectors_cli.api.base import BaseAPIClient
8
+ from gl_connectors_cli.api.models import CreatedUser, User
9
+ from gl_connectors_cli.constants import API_KEY_HEADER, AUTHORIZATION_HEADER, BEARER_PREFIX, HTTP_GET, HTTP_POST
10
+ from gl_connectors_cli.utils import CLIError
11
+
12
+
13
+ class UsersAPIClient(BaseAPIClient):
14
+ """Users API client."""
15
+
16
+ def create_user(self, client_key: str, username: str) -> CreatedUser:
17
+ """Create a new user.
18
+
19
+ Args:
20
+ client_key: Client API key
21
+ username: Username for the new user
22
+
23
+ Returns:
24
+ CreatedUser: User creation response data
25
+
26
+ Raises:
27
+ CLIError: If user creation fails
28
+
29
+ """
30
+ headers = {
31
+ API_KEY_HEADER: client_key,
32
+ }
33
+
34
+ data = {"identifier": username}
35
+
36
+ try:
37
+ response = self._make_request(HTTP_POST, "/clients/register-user", headers=headers, data=data)
38
+ user_data = response.get("data", {})
39
+ return CreatedUser(**user_data)
40
+
41
+ except Exception as e:
42
+ raise CLIError(f"Failed to create user: {str(e)}") from e
43
+
44
+ def get_user_info(self, client_key: str, token: str) -> User:
45
+ """Get current user information.
46
+
47
+ Args:
48
+ client_key: Client API key
49
+ token: User JWT token
50
+
51
+ Returns:
52
+ User: User information
53
+
54
+ Raises:
55
+ CLIError: If request fails
56
+
57
+ """
58
+ headers = {AUTHORIZATION_HEADER: f"{BEARER_PREFIX} {token}", API_KEY_HEADER: client_key}
59
+
60
+ try:
61
+ response = self._make_request(HTTP_GET, "/clients/user", headers=headers)
62
+ user_data = response.get("data", {})
63
+ return User(**user_data)
64
+
65
+ except Exception as e:
66
+ raise CLIError(f"Failed to get user info: {str(e)}") from e
@@ -0,0 +1 @@
1
+ """CLI commands module."""