Skip to content

Commit fdb2105

Browse files
author
João Guerreiro
committed
feat(pygitguardian): add to_json and to_dict methods
1 parent f2cab92 commit fdb2105

12 files changed

+465
-212
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Python 3.5+
1313

1414
## Getting started
1515

16-
You can obtain tokens for API usage on your [dashboard](https://dashboard.gitguardian.com/api).
16+
You can obtain API keys for API usage on your [dashboard](https://dashboard.gitguardian.com/api).
1717

1818
**pip**
1919

@@ -48,9 +48,9 @@ DOCUMENT = """
4848
consume(response.read())"
4949
"""
5050

51-
client = GGClient(token=API_KEY)
51+
client = GGClient(api_key=API_KEY)
5252

53-
# Check the health of the API and the token used.
53+
# Check the health of the API and the API key used.
5454
health_obj, status = client.health_check()
5555

5656
if status != codes[r"\o/"]: # this is 200 but cooler
@@ -70,7 +70,7 @@ print("Scan results:", scan_result.has_secrets, "-", scan_result.policy_break_co
7070

7171
```py
7272
API_KEY = os.getenv("GG_API_KEY")
73-
client = GGClient(token=API_KEY)
73+
client = GGClient(api_key=API_KEY)
7474
# Create a list of dictionaries for scanning
7575
to_scan = []
7676
for name in glob.glob("**/*"):

examples/content_scan.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
consume(response.read())"
1717
"""
1818

19-
client = GGClient(token=API_KEY)
19+
client = GGClient(api_key=API_KEY)
2020

21-
# Check the health of the API and the token used.
21+
# Check the health of the API and the API key used.
2222
health_obj, status = client.health_check()
2323

2424
if status == codes[r"\o/"]: # this is 200 but cooler

examples/directory_scan.py

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

1010
API_KEY = os.getenv("GG_API_KEY")
1111

12-
client = GGClient(token=API_KEY)
12+
client = GGClient(api_key=API_KEY)
1313

1414
# Create a list of dictionaries for scanning
1515
to_scan = []

pygitguardian/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
"""PyGitGuardian API Client"""
22
from .client import GGClient
3-
from .models import Detail, Match, PolicyBreak, ScanResult
4-
from .schemas import DetailSchema, DocumentSchema, ScanResultSchema
3+
from .models import (
4+
Detail,
5+
DetailSchema,
6+
DocumentSchema,
7+
Match,
8+
PolicyBreak,
9+
ScanResult,
10+
ScanResultSchema,
11+
)
512

613

7-
__version__ = "1.0.2"
14+
__version__ = "1.0.3"
815
GGClient._version = __version__
916

1017
__all__ = [

pygitguardian/client.py

Lines changed: 58 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import platform
22
import urllib.parse
3-
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
3+
from typing import Dict, Iterable, Optional, Union
44

55
import requests
6-
from marshmallow import Schema
76
from requests import Response, Session, codes
87

98
from .config import (
@@ -12,26 +11,22 @@
1211
DEFAULT_TIMEOUT,
1312
MULTI_DOCUMENT_LIMIT,
1413
)
15-
from .models import Detail, ScanResult
16-
from .schemas import DetailSchema, DocumentSchema, ScanResultSchema
14+
from .models import Detail, Document, MultiScanResult, ScanResult
1715

1816

1917
class GGClient:
20-
DETAIL_SCHEMA = DetailSchema()
21-
DOCUMENT_SCHEMA = DocumentSchema()
22-
SCAN_RESULT_SCHEMA = ScanResultSchema()
2318
_version = "undefined"
2419

2520
def __init__(
2621
self,
27-
token: str,
22+
api_key: str,
2823
base_uri: Optional[str] = None,
2924
session: Optional[requests.Session] = None,
3025
user_agent: Optional[str] = None,
3126
timeout: Optional[float] = DEFAULT_TIMEOUT,
3227
) -> "GGClient":
3328
"""
34-
:param token: APIKey to be added to requests
29+
:param api_key: API Key to be added to requests
3530
:param base_uri: Base URI for the API, defaults to "https://api.gitguardian.com"
3631
:param session: custom requests session, defaults to requests.Session()
3732
:param user_agent: user agent to identify requests, defaults to ""
@@ -46,11 +41,11 @@ def __init__(
4641
else:
4742
base_uri = DEFAULT_BASE_URI
4843

49-
if not isinstance(token, str):
50-
raise TypeError("Missing token string")
44+
if not isinstance(api_key, str):
45+
raise TypeError("api_key is not a string")
5146

5247
self.base_uri = base_uri
53-
self.token = token
48+
self.api_key = api_key
5449
self.session = (
5550
session if session is isinstance(session, Session) else requests.Session()
5651
)
@@ -63,77 +58,64 @@ def __init__(
6358
self.user_agent = " ".join([self.user_agent, user_agent])
6459

6560
self.session.headers.update(
66-
{"User-Agent": self.user_agent, "Authorization": "Token {0}".format(token)}
61+
{
62+
"User-Agent": self.user_agent,
63+
"Authorization": "Token {0}".format(api_key),
64+
}
6765
)
6866

6967
def request(
70-
self,
71-
method: str,
72-
endpoint: str,
73-
schema: Schema = None,
74-
version: str = DEFAULT_API_VERSION,
75-
many: bool = False,
76-
**kwargs
77-
) -> Tuple[Any, Response]:
68+
self, method: str, endpoint: str, version: str = DEFAULT_API_VERSION, **kwargs
69+
) -> Response:
7870
if version:
7971
endpoint = urllib.parse.urljoin(version + "/", endpoint)
8072

8173
url = urllib.parse.urljoin(self.base_uri, endpoint)
8274

83-
response = self.session.request(
75+
resp = self.session.request(
8476
method=method, url=url, timeout=self.timeout, **kwargs
8577
)
8678

87-
if response.headers["content-type"] != "application/json":
79+
if resp.headers["content-type"] != "application/json":
8880
raise TypeError("Response is not JSON")
8981

90-
if response.status_code == codes.ok and schema:
91-
obj = schema.load(response.json(), many=many)
92-
if many:
93-
for element in obj:
94-
element.status_code = response.status_code
95-
else:
96-
obj.status_code = response.status_code
97-
else:
98-
obj = self.DETAIL_SCHEMA.load(response.json())
99-
obj.status_code = response.status_code
100-
101-
return obj, response
82+
return resp
10283

10384
def post(
10485
self,
10586
endpoint: str,
10687
data: str = None,
107-
schema: Schema = None,
10888
version: str = DEFAULT_API_VERSION,
109-
many: bool = False,
11089
**kwargs
111-
) -> Tuple[Any, Response]:
90+
) -> Response:
11291
return self.request(
113-
"post",
114-
endpoint=endpoint,
115-
schema=schema,
116-
json=data,
117-
version=version,
118-
many=many,
119-
**kwargs,
92+
"post", endpoint=endpoint, json=data, version=version, **kwargs,
12093
)
12194

12295
def get(
123-
self,
124-
endpoint: str,
125-
schema: Schema = None,
126-
version: str = DEFAULT_API_VERSION,
127-
many: bool = False,
128-
**kwargs
129-
) -> Tuple[Any, Response]:
130-
return self.request(
131-
method="get", endpoint=endpoint, schema=schema, version=version, **kwargs
132-
)
96+
self, endpoint: str, version: str = DEFAULT_API_VERSION, **kwargs
97+
) -> Response:
98+
return self.request(method="get", endpoint=endpoint, version=version, **kwargs)
99+
100+
def health_check(self) -> Detail:
101+
"""
102+
health_check handles the /health endpoint of the API
103+
104+
use Detail.status_code to check the response status code of the API
105+
106+
200 if server is online and api_key is valid
107+
:return: Detail response and status code
108+
"""
109+
resp = self.get(endpoint="health")
110+
111+
obj = Detail.SCHEMA.load(resp.json())
112+
obj.status_code = resp.status_code
113+
114+
return obj
133115

134116
def content_scan(
135117
self, document: str, filename: Optional[str] = None
136-
) -> Tuple[Union[Detail, ScanResult], int]:
118+
) -> Union[Detail, ScanResult]:
137119
"""
138120
content_scan handles the /scan endpoint of the API
139121
@@ -146,15 +128,21 @@ def content_scan(
146128
if filename:
147129
doc_dict["filename"] = filename
148130

149-
request_obj = self.DOCUMENT_SCHEMA.load(doc_dict)
150-
obj, resp = self.post(
151-
endpoint="scan", data=request_obj, schema=self.SCAN_RESULT_SCHEMA
152-
)
153-
return obj, resp.status_code
131+
request_obj = Document.SCHEMA.load(doc_dict)
132+
133+
resp = self.post(endpoint="scan", data=request_obj)
134+
if resp.status_code == codes.ok:
135+
obj = ScanResult.SCHEMA.load(resp.json())
136+
else:
137+
obj = Detail.SCHEMA.load(resp.json())
138+
139+
obj.status_code = resp.status_code
140+
141+
return obj
154142

155143
def multi_content_scan(
156144
self, documents: Iterable[Dict[str, str]],
157-
) -> Tuple[Union[Detail, List[ScanResult]], int]:
145+
) -> Union[Detail, MultiScanResult]:
158146
"""
159147
multi_content_scan handles the /multiscan endpoint of the API
160148
@@ -171,26 +159,17 @@ def multi_content_scan(
171159
)
172160

173161
if all(isinstance(doc, dict) for doc in documents):
174-
request_obj = self.DOCUMENT_SCHEMA.load(documents, many=True)
162+
request_obj = Document.SCHEMA.load(documents, many=True)
175163
else:
176164
raise TypeError("each document must be a dict")
177165

178-
obj, resp = self.post(
179-
endpoint="multiscan",
180-
data=request_obj,
181-
schema=self.SCAN_RESULT_SCHEMA,
182-
many=True,
183-
)
184-
return obj, resp.status_code
166+
resp = self.post(endpoint="multiscan", data=request_obj)
185167

186-
def health_check(self) -> Tuple[Detail, int]:
187-
"""
188-
health_check handles the /health endpoint of the API
168+
if resp.status_code == codes.ok:
169+
obj = MultiScanResult.SCHEMA.load(dict(scan_results=resp.json()))
170+
else:
171+
obj = Detail.SCHEMA.load(resp.json())
189172

190-
use Detail.status_code to check the response status code of the API
173+
obj.status_code = resp.status_code
191174

192-
200 if server is online and token is valid
193-
:return: Detail response and status code
194-
"""
195-
obj, resp = self.get(endpoint="health")
196-
return obj, resp.status_code
175+
return obj

0 commit comments

Comments
 (0)