squid-crm 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.
- squid_crm-0.1.0/PKG-INFO +11 -0
- squid_crm-0.1.0/pyproject.toml +22 -0
- squid_crm-0.1.0/setup.cfg +4 -0
- squid_crm-0.1.0/src/squid_crm/__init__.py +116 -0
- squid_crm-0.1.0/src/squid_crm.egg-info/PKG-INFO +11 -0
- squid_crm-0.1.0/src/squid_crm.egg-info/SOURCES.txt +6 -0
- squid_crm-0.1.0/src/squid_crm.egg-info/dependency_links.txt +1 -0
- squid_crm-0.1.0/src/squid_crm.egg-info/top_level.txt +1 -0
squid_crm-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: squid-crm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the Squid CRM REST and GraphQL APIs
|
|
5
|
+
Author: Squid-CRM contributors
|
|
6
|
+
License: AGPL-3.0-only
|
|
7
|
+
Project-URL: Repository, https://github.com/Mullassery/Squid-CRM
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "squid-crm"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for the Squid CRM REST and GraphQL APIs"
|
|
9
|
+
requires-python = ">=3.9"
|
|
10
|
+
license = { text = "AGPL-3.0-only" }
|
|
11
|
+
authors = [{ name = "Squid-CRM contributors" }]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"License :: OSI Approved :: GNU Affero General Public License v3",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Repository = "https://github.com/Mullassery/Squid-CRM"
|
|
20
|
+
|
|
21
|
+
[tool.setuptools.packages.find]
|
|
22
|
+
where = ["src"]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Python client for the Squid CRM API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import urllib.error
|
|
7
|
+
import urllib.parse
|
|
8
|
+
import urllib.request
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
__version__ = "0.1.0"
|
|
12
|
+
|
|
13
|
+
__all__ = ["SquidClient", "SquidError", "__version__"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SquidError(RuntimeError):
|
|
17
|
+
"""Raised when the Squid API returns an error response."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, message: str, status: Optional[int] = None, body: Any = None):
|
|
20
|
+
super().__init__(message)
|
|
21
|
+
self.status = status
|
|
22
|
+
self.body = body
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SquidClient:
|
|
26
|
+
"""Small dependency-free client for the Squid REST/GraphQL API.
|
|
27
|
+
|
|
28
|
+
``api_key`` is the bearer token returned by the API-key lifecycle
|
|
29
|
+
mutations. User access tokens are rejected by the data API, matching the
|
|
30
|
+
upstream contract.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
base_url: str,
|
|
36
|
+
api_key: Optional[str] = None,
|
|
37
|
+
timeout: float = 30.0,
|
|
38
|
+
) -> None:
|
|
39
|
+
self.base_url = base_url.rstrip("/")
|
|
40
|
+
self.api_key = api_key
|
|
41
|
+
self.timeout = timeout
|
|
42
|
+
|
|
43
|
+
def _request(
|
|
44
|
+
self,
|
|
45
|
+
method: str,
|
|
46
|
+
path: str,
|
|
47
|
+
payload: Optional[Dict[str, Any]] = None,
|
|
48
|
+
params: Optional[Dict[str, Any]] = None,
|
|
49
|
+
) -> Any:
|
|
50
|
+
url = f"{self.base_url}{path}"
|
|
51
|
+
if params:
|
|
52
|
+
query = urllib.parse.urlencode(
|
|
53
|
+
{key: value for key, value in params.items() if value is not None}
|
|
54
|
+
)
|
|
55
|
+
if query:
|
|
56
|
+
url = f"{url}?{query}"
|
|
57
|
+
data = None
|
|
58
|
+
headers = {"Accept": "application/json"}
|
|
59
|
+
if payload is not None:
|
|
60
|
+
data = json.dumps(payload).encode("utf-8")
|
|
61
|
+
headers["Content-Type"] = "application/json"
|
|
62
|
+
if self.api_key:
|
|
63
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
64
|
+
request = urllib.request.Request(url, data=data, headers=headers, method=method)
|
|
65
|
+
try:
|
|
66
|
+
with urllib.request.urlopen(request, timeout=self.timeout) as response:
|
|
67
|
+
raw = response.read()
|
|
68
|
+
return json.loads(raw) if raw else None
|
|
69
|
+
except urllib.error.HTTPError as error:
|
|
70
|
+
raw = error.read()
|
|
71
|
+
try:
|
|
72
|
+
body = json.loads(raw)
|
|
73
|
+
except ValueError:
|
|
74
|
+
body = raw.decode("utf-8", errors="replace")
|
|
75
|
+
message = body.get("messages") if isinstance(body, dict) else body
|
|
76
|
+
raise SquidError(str(message), status=error.code, body=body) from error
|
|
77
|
+
except urllib.error.URLError as error:
|
|
78
|
+
raise SquidError(str(error.reason)) from error
|
|
79
|
+
|
|
80
|
+
def graphql(
|
|
81
|
+
self,
|
|
82
|
+
query: str,
|
|
83
|
+
variables: Optional[Dict[str, Any]] = None,
|
|
84
|
+
) -> Any:
|
|
85
|
+
"""Execute a GraphQL operation against ``/graphql``."""
|
|
86
|
+
response = self._request(
|
|
87
|
+
"POST",
|
|
88
|
+
"/graphql",
|
|
89
|
+
{"query": query, "variables": variables or {}},
|
|
90
|
+
)
|
|
91
|
+
if isinstance(response, dict) and response.get("errors"):
|
|
92
|
+
raise SquidError(str(response["errors"]), body=response)
|
|
93
|
+
return response.get("data") if isinstance(response, dict) else response
|
|
94
|
+
|
|
95
|
+
def list_people(self, limit: int = 20, offset: int = 0) -> Any:
|
|
96
|
+
"""List people through the REST data API."""
|
|
97
|
+
return self._request(
|
|
98
|
+
"GET",
|
|
99
|
+
"/rest/people",
|
|
100
|
+
params={"limit": limit, "offset": offset},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def create_person(
|
|
104
|
+
self,
|
|
105
|
+
first_name: str,
|
|
106
|
+
last_name: str,
|
|
107
|
+
email: str,
|
|
108
|
+
**fields: Any,
|
|
109
|
+
) -> Any:
|
|
110
|
+
"""Create a person through the REST data API."""
|
|
111
|
+
payload: Dict[str, Any] = {
|
|
112
|
+
"name": {"firstName": first_name, "lastName": last_name},
|
|
113
|
+
"emails": {"primaryEmail": email},
|
|
114
|
+
}
|
|
115
|
+
payload.update(fields)
|
|
116
|
+
return self._request("POST", "/rest/people", payload)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: squid-crm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the Squid CRM REST and GraphQL APIs
|
|
5
|
+
Author: Squid-CRM contributors
|
|
6
|
+
License: AGPL-3.0-only
|
|
7
|
+
Project-URL: Repository, https://github.com/Mullassery/Squid-CRM
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
squid_crm
|