Skip to content

Commit 576eca4

Browse files
jguerreiroJguer
authored andcommitted
feat(quotas): add quota route
1 parent e5111b3 commit 576eca4

File tree

7 files changed

+182
-27
lines changed

7 files changed

+182
-27
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
default_stages: [commit]
22
repos:
33
- repo: https://github.com/ambv/black
4-
rev: 21.5b0
4+
rev: 21.5b1
55
hooks:
66
- id: black
77
language_version: python3
@@ -18,7 +18,7 @@ repos:
1818
- id: mypy
1919

2020
- repo: https://github.com/pre-commit/pre-commit-hooks
21-
rev: v3.4.0
21+
rev: v4.0.1
2222
hooks:
2323
- id: check-json
2424
- id: check-added-large-files

Pipfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
[[source]]
2+
name = "pypi"
23
url = "https://pypi.python.org/simple"
34
verify_ssl = true
4-
name = "pypi"
55

66
[packages]
7-
pygitguardian = {editable = true,path = "."}
87
marshmallow = ">=3.5"
8+
pygitguardian = {editable = true, path = "."}
99
requests = ">=2"
1010

1111
[dev-packages]
12-
black = "==21.5b0"
12+
black = "==21.5b1"
1313
coverage = "*"
1414
flake8 = "*"
1515
flake8-isort = "*"

Pipfile.lock

Lines changed: 17 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pygitguardian/client.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
DEFAULT_TIMEOUT,
1111
MULTI_DOCUMENT_LIMIT,
1212
)
13-
from .models import Detail, Document, MultiScanResult, ScanResult
13+
from .models import Detail, Document, MultiScanResult, QuotaResponse, ScanResult
1414

1515

1616
def load_detail(resp: Response) -> Detail:
@@ -244,3 +244,28 @@ def multi_content_scan(
244244
obj.status_code = resp.status_code
245245

246246
return obj
247+
248+
def quota_overview(
249+
self,
250+
extra_headers: Optional[Dict[str, str]] = None,
251+
) -> Union[Detail, QuotaResponse]:
252+
"""
253+
content_scan handles the /scan endpoint of the API
254+
255+
:param extra_headers: additional headers to add to the request
256+
:return: Detail or Quota response and status code
257+
"""
258+
259+
resp = self.get(
260+
endpoint="quotas",
261+
extra_headers=extra_headers,
262+
)
263+
264+
if is_ok(resp):
265+
obj = QuotaResponse.SCHEMA.load(resp.json())
266+
else:
267+
obj = load_detail(resp)
268+
269+
obj.status_code = resp.status_code
270+
271+
return obj

pygitguardian/models.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import date
12
from typing import ClassVar, Dict, List, Optional
23

34
from marshmallow import (
@@ -367,3 +368,73 @@ def __str__(self):
367368
]
368369
),
369370
)
371+
372+
373+
class QuotaSchema(Schema):
374+
count = fields.Int()
375+
limit = fields.Int()
376+
remaining = fields.Int()
377+
since = fields.Date()
378+
379+
@post_load
380+
def make_quota(self, data, **kwargs):
381+
return Quota(**data)
382+
383+
384+
class Quota(Base):
385+
"""
386+
Quota describes a quota category in the GitGuardian API.
387+
Allows you to check your current available quota.
388+
Example:
389+
{"count": 2,
390+
"limit": 5000,
391+
"remaining": 4998,
392+
"since": "2021-04-18"}
393+
"""
394+
395+
SCHEMA = QuotaSchema()
396+
397+
def __init__(self, count: int, limit: int, remaining: int, since: date, **kwargs):
398+
super().__init__()
399+
self.count = count
400+
self.limit = limit
401+
self.remaining = remaining
402+
self.since = since
403+
404+
def __repr__(self):
405+
return (
406+
"count:{0}, "
407+
"limit:{1}, "
408+
"remaining:{2}, "
409+
"since:{3}".format(self.count, self.limit, self.remaining, repr(self.since))
410+
)
411+
412+
413+
class QuotaResponseSchema(Schema):
414+
content = fields.Nested(QuotaSchema)
415+
416+
@post_load
417+
def make_quota_response(self, data, **kwargs):
418+
return QuotaResponse(**data)
419+
420+
421+
class QuotaResponse(Base):
422+
"""
423+
Quota describes a quota category in the GitGuardian API.
424+
Allows you to check your current available quota.
425+
Example:
426+
{"content": {
427+
"count": 2,
428+
"limit": 5000,
429+
"remaining": 4998,
430+
"since": "2021-04-18"}}
431+
"""
432+
433+
SCHEMA = QuotaResponseSchema()
434+
435+
def __init__(self, content: Quota, **kwargs):
436+
super().__init__()
437+
self.content = content
438+
439+
def __repr__(self):
440+
return "content:{0}".format(repr(self.content))

tests/cassettes/quota.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- "*/*"
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
User-Agent:
12+
- pygitguardian/1.1.3 (Linux;py3.9.5)
13+
method: GET
14+
uri: https://api.gitguardian.com/v1/quotas
15+
response:
16+
body:
17+
string: '{"content": {"count": 2,"limit": 5000,"remaining": 4998,"since": "2021-04-18"}}'
18+
headers:
19+
Access-Control-Expose-Headers:
20+
- X-App-Version
21+
Connection:
22+
- keep-alive
23+
Content-Length:
24+
- "79"
25+
Content-Type:
26+
- application/json
27+
Date:
28+
- Wed, 19 May 2021 15:57:19 GMT
29+
Referrer-Policy:
30+
- same-origin
31+
Server:
32+
- nginx
33+
Strict-Transport-Security:
34+
- max-age=31536000; includeSubDomains
35+
status:
36+
code: 200
37+
message: OK
38+
version: 1

tests/test_client.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
2-
from typing import Dict, List, Optional, Type
2+
from datetime import date
3+
from typing import Any, Dict, List, Optional, Type
34
from unittest.mock import Mock, patch
45

56
import pytest
@@ -13,7 +14,7 @@
1314
DOCUMENT_SIZE_THRESHOLD_BYTES,
1415
MULTI_DOCUMENT_LIMIT,
1516
)
16-
from pygitguardian.models import Detail, MultiScanResult, ScanResult
17+
from pygitguardian.models import Detail, MultiScanResult, QuotaResponse, ScanResult
1718

1819
from .conftest import base_uri, my_vcr
1920

@@ -432,7 +433,7 @@ def test_content_not_ok():
432433
def test_content_scan(
433434
client: GGClient,
434435
name: str,
435-
to_scan: Dict[str, str],
436+
to_scan: Dict[str, Any],
436437
has_secrets: bool,
437438
has_policy_breaks: bool,
438439
policy_break_count: int,
@@ -505,7 +506,7 @@ def test_assert_content_type(client: GGClient):
505506
def test_extra_headers(
506507
request_mock: Mock,
507508
client: GGClient,
508-
session_headers: Dict[str, str],
509+
session_headers: Any,
509510
extra_headers: Optional[Dict[str, str]],
510511
expected_headers: Dict[str, str],
511512
):
@@ -534,3 +535,23 @@ def test_extra_headers(
534535
assert request_mock.called
535536
_, kwargs = request_mock.call_args
536537
assert expected_headers == kwargs["headers"]
538+
539+
540+
def test_quota_overview(client: GGClient):
541+
with my_vcr.use_cassette("quota.yaml"):
542+
quota_response = client.quota_overview()
543+
assert type(repr(quota_response)) == str
544+
assert type(str(quota_response)) == str
545+
assert quota_response.status_code == 200
546+
if isinstance(quota_response, QuotaResponse):
547+
assert quota_response.content.limit == 5000
548+
assert quota_response.content.count == 2
549+
assert quota_response.content.remaining == 4998
550+
assert quota_response.content.since == date(2021, 4, 18)
551+
else:
552+
pytest.fail("returned should be a QuotaResponse")
553+
554+
assert type(quota_response.to_dict()) == dict
555+
quota_response_json = quota_response.to_json()
556+
assert type(quota_response_json) == str
557+
assert type(json.loads(quota_response_json)) == dict

0 commit comments

Comments
 (0)