Skip to content

Commit f1fb729

Browse files
Client improve docs (canada-ca#1656)
* First pass at fleshed out README * Remove duplicate IPython notice in README * Improve consistency of headers * Fix examples match output shown * Convert existing docstrings to rst style * revise function docstrings in client * Module docstrings, document queries * More test docstrings * Refactor formatting functions to own module This change is necessary to reduce the length of client.py in preparation for adding examples to docstrings. Otherwise it would become unmanageably long after examples are added. * Add examples for all get functions in client.py * Run black on last commit
1 parent c1228a3 commit f1fb729

10 files changed

Lines changed: 772 additions & 192 deletions

File tree

clients/python/README.md

Lines changed: 140 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
# Tracker Python API Client
22

3-
The Tracker Python API Client will provide a Python wrapper for key features of Tracker.
3+
The Tracker Python API Client provides a simple Python interface for the [Tracker GraphQL API](https://github.com/canada-ca/tracker/blob/master/api-js/README.md), with the aim of allowing users to easily integrate data from Tracker into existing workflows and platforms. It allows access to the JSON data served by the API without requiring specific knowledge of [GraphQL](https://graphql.org/) or the Tracker API. This is done by providing functions that execute canned queries against the API using [gql](https://github.com/graphql-python/gql). Responses are formatted to remove pagination related structures, and to ensure useful keys are always present.
44

5-
It makes use of [gql](https://github.com/graphql-python/gql), a Python GraphQL client, to query the Tracker API.
65

7-
#### Installing Dependencies
6+
## Installation
87

9-
Install [pipenv](https://pypi.org/project/pipenv/) if you don't already have it.
8+
### For Users
9+
10+
The client will soon be available to install as a package via pip or pipenv. Until then, follow the instructions for developers below.
11+
12+
### For Developers
13+
14+
Install [pipenv](https://pypi.org/project/pipenv/) if you don't already have it. The following instructions assume you are using pipenv.
15+
16+
#### Installing Dependencies
17+
18+
Make sure you have pulled the most recent version from the repo, then run:
1019

1120
```shell
1221
pipenv install --dev
@@ -18,7 +27,12 @@ If you run into issues, ensure pipenv has installed the most recent GQL version.
1827
pipenv install -e git+https://github.com/graphql-python/gql.git#egg=gql
1928
```
2029

21-
#### Authentication
30+
## Usage
31+
32+
33+
### Authentication
34+
35+
You must have a Tracker account that is a member of one or more organizations to make use of the Python client. You can manage your account in the [Tracker web interface](https://tracker.alpha.canada.ca/).
2236

2337
The client will attempt to draw credentials from its environment in order to obtain an authentication token. Pipenv makes this easy to set up by importing environment variables from a `.env` file present in this directory whenever `pipenv run` or `pipenv shell` are used. The `.env` file can be created like so:
2438

@@ -29,7 +43,122 @@ TRACKER_PASS=YOURPASSWORDHERE
2943
EOF
3044
```
3145

32-
#### Testing
46+
You should be mindful that setting these variables manually can result in credentials being stored in your shell command history.
47+
48+
### Basic Usage
49+
50+
You will generally start by creating a client with `create_client(auth_token=get_auth_token())` and storing the result. All functions that make queries expect such a client to be passed as the first argument.
51+
52+
### Examples
53+
54+
#### Get all domains in my organizations
55+
56+
Supposing I belong to two organizations with the acronyms "FOO" and "BAR":
57+
58+
```python
59+
>>> import tracker_client.client as tracker_client
60+
>>> client = tracker_client.create_client(auth_token=tracker_client.get_auth_token())
61+
>>> print(tracker_client.get_all_domains(client))
62+
{
63+
"FOO": [
64+
"foo.bar",
65+
"foo.bar.baz"
66+
],
67+
"BAR": [
68+
"fizz.buzz",
69+
"buzz.bang",
70+
"ab.cd.ef",
71+
]
72+
}
73+
```
74+
75+
The following examples continue the previous one (assume the package has been imported as above and a `client` object with a valid token exists).
76+
77+
#### Get a DMARC summary for a domain
78+
79+
```python
80+
>>> print(tracker_client.get_dmarc_summary(client, "foo.bar", "september", 2020))
81+
{
82+
"foo.bar": {
83+
"month": "SEPTEMBER",
84+
"year": "2020",
85+
"categoryPercentages": {
86+
"fullPassPercentage": 87,
87+
"passSpfOnlyPercentage": 0,
88+
"passDkimOnlyPercentage": 6,
89+
"failPercentage": 8,
90+
"totalMessages": 10534
91+
}
92+
}
93+
}
94+
```
95+
96+
#### Get summary metrics for an organization
97+
98+
```python
99+
>>> print(tracker_client.get_summary_by_acronym(client, "foo"))
100+
{
101+
"FOO": {
102+
"domainCount": 10,
103+
"summaries": {
104+
"web": {
105+
"total": 10,
106+
"categories": [
107+
{
108+
"name": "pass",
109+
"count": 1,
110+
"percentage": 10
111+
},
112+
{
113+
"name": "fail",
114+
"count": 9,
115+
"percentage": 90
116+
}
117+
]
118+
},
119+
"mail": {
120+
"total": 10,
121+
"categories": [
122+
{
123+
"name": "pass",
124+
"count": 5,
125+
"percentage": 50
126+
},
127+
{
128+
"name": "fail",
129+
"count": 5,
130+
"percentage": 50
131+
}
132+
]
133+
}
134+
}
135+
}
136+
}
137+
```
138+
139+
#### Get the status of a domain
140+
141+
```python
142+
>>> print(tracker_client.get_domain_status(client, "foo.bar"))
143+
{
144+
"foo.bar": {
145+
"lastRan": "2021-01-23 22:33:26.921529",
146+
"status": {
147+
"https": "FAIL",
148+
"ssl": "FAIL",
149+
"dmarc": "PASS",
150+
"dkim": "PASS",
151+
"spf": "PASS"
152+
}
153+
}
154+
}
155+
```
156+
157+
> **NOTE**: Because of gql limitations, the client is not currently compatible with IPython or Jupyter.
158+
159+
## Development
160+
161+
### Testing
33162

34163
Pytest is used for testing. To run tests, run the following in the project root (the folder containing this README.md):
35164

@@ -41,6 +170,9 @@ Alternatively, if you are already in a pipenv shell, just run `pytest`.
41170

42171
If tests are failing with ModuleNotFoundError, make sure tracker_client/ is on your PYTHONPATH. The .env file used to store your credentials is a good way to set this.
43172

44-
#### Note about IPython/Jupyter
173+
When additions or significant changes are made, check test coverage with:
174+
175+
```shell
176+
pipenv run pytest --cov=tracker_client
177+
```
45178

46-
Because of a limitation in gql, the client is not currently compatible with IPython or Jupyter.

clients/python/tests/test_formatting.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
from tracker_client.client import (
1+
"""Tests for query response formatting functions.
2+
3+
The queries named in ALL_CAPS can be found in tracker_client/queries.py
4+
"""
5+
from tracker_client.formatting import (
26
format_all_domains,
37
format_acronym_domains,
48
format_name_domains,
@@ -11,41 +15,52 @@
1115
format_domain_status,
1216
)
1317

18+
1419
def test_format_all_domains(all_domains_input, all_domains_output):
20+
"""Test formatting of ALL_DOMAINS_QUERY results"""
1521
assert format_all_domains(all_domains_input) == all_domains_output
1622

1723

1824
def test_format_acronym_domains(all_domains_input, org_domains_output):
25+
"""Test formatting + filtering by acronym of ALL_DOMAINS_QUERY results"""
1926
assert format_acronym_domains(all_domains_input, "def") == org_domains_output
2027

2128

2229
def test_format_name_domains(name_domain_input, org_domains_output):
30+
"""Test formatting of DOMAINS_BY_SLUG results"""
2331
assert format_name_domains(name_domain_input) == org_domains_output
2432

2533

2634
def test_format_dmarc_monthly(monthly_dmarc_input, monthly_dmarc_output):
35+
"""Test formatting of DMARC_SUMMARY results"""
2736
assert format_dmarc_monthly(monthly_dmarc_input) == monthly_dmarc_output
2837

2938

3039
def test_format_dmarc_yearly(yearly_dmarc_input, yearly_dmarc_output):
40+
"""Test formatting of YEARLY_DMARC_SUMMARIES results"""
3141
assert format_dmarc_yearly(yearly_dmarc_input) == yearly_dmarc_output
3242

3343

3444
def test_format_all_summaries(all_summaries_input, all_summaries_output):
45+
"""Test formatting of ALL_ORGS_SUMMARIES results"""
3546
assert format_all_summaries(all_summaries_input) == all_summaries_output
3647

3748

3849
def test_format_acronym_summary(all_summaries_input, org_summary_output):
50+
"""Test formatting + filtering by acronym of ALL_ORGS_SUMMARIES results"""
3951
assert format_acronym_summary(all_summaries_input, "def") == org_summary_output
4052

4153

4254
def test_format_name_summary(name_summary_input, org_summary_output):
55+
"""Test formatting of SUMMARY_BY_SLUG results"""
4356
assert format_name_summary(name_summary_input) == org_summary_output
4457

4558

4659
def test_format_domain_results(scan_results_input, scan_results_output):
60+
"""Test formatting of DOMAIN_RESULTS results"""
4761
assert format_domain_results(scan_results_input) == scan_results_output
4862

4963

5064
def test_format_domain_status(domain_status_input, domain_status_output):
65+
"""Test formatting of DOMAIN_STATUS results"""
5166
assert format_domain_status(domain_status_input) == domain_status_output

clients/python/tests/test_get_dmarc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Tests for functions that get DMARC summaries for a domain"""
12
import json
23

34
from gql import Client

clients/python/tests/test_get_domains.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Tests for functions that get domain lists"""
12
import json
23

34
from gql import Client

clients/python/tests/test_get_results.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Tests for functions that get scan results for a domain"""
12
import json
23

34
from gql import Client

clients/python/tests/test_get_summaries.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Tests for functions that get summary metrics"""
12
import json
23

34
from gql import Client

clients/python/tests/test_gql.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Tests for gql related utility functions"""
12
import re
23
import pytest
34

0 commit comments

Comments
 (0)