Skip to content

Commit 692b016

Browse files
authored
Add tracking headers (#266)
Add Archivist-User-Agent and Archivist-Partner-ID headers. Fixes AB#9572
1 parent 25a228c commit 692b016

46 files changed

Lines changed: 563 additions & 60 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

archivist/archivist.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,15 @@ def __init__(
108108
fixtures: "dict[str,dict[Any,Any]]|None" = None,
109109
verify: bool = True,
110110
max_time: float = MAX_TIME,
111+
partner_id: str = "",
112+
user_agent: str = "",
111113
):
112114
super().__init__(
113115
fixtures=fixtures,
114116
verify=verify,
115117
max_time=max_time,
118+
partner_id=partner_id,
119+
user_agent=user_agent,
116120
)
117121

118122
if isinstance(auth, tuple):
@@ -214,10 +218,12 @@ def __copy__(self) -> "Archivist":
214218
fixtures=deepcopy(self._fixtures),
215219
verify=self._verify,
216220
max_time=self._max_time,
221+
partner_id=self._partner_id,
222+
user_agent=self._user_agent,
217223
)
218224

219225
def _add_headers(self, headers: "dict[str,str]|None") -> "dict[str,Any]":
220-
newheaders = {**headers} if isinstance(headers, dict) else {}
226+
newheaders = super()._add_headers(headers)
221227

222228
auth = self.auth # this may trigger a refetch so only do it once here
223229
# for appidp endpoint there may not be an authtoken

archivist/archivistpublic.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,16 @@
3333
from requests.models import Response
3434

3535

36+
from .about import __version__ as VERSION
3637
from .assetattachments import _AssetAttachmentsClient
3738
from .assets import _AssetsPublic
3839
from .confirmer import MAX_TIME
3940
from .constants import (
4041
HEADERS_REQUEST_TOTAL_COUNT,
4142
HEADERS_TOTAL_COUNT,
43+
PARTNER_ID,
44+
USER_AGENT,
45+
USER_AGENT_PREFIX,
4246
)
4347
from .dictmerge import _deepmerge, _dotdict
4448
from .errors import (
@@ -82,12 +86,16 @@ def __init__(
8286
fixtures: "dict[str, Any]|None" = None,
8387
verify: bool = True,
8488
max_time: float = MAX_TIME,
89+
partner_id: str = "",
90+
user_agent: str = "",
8591
):
8692
self._verify = verify
8793
self._response_ring_buffer = deque(maxlen=self.RING_BUFFER_MAX_LEN)
8894
self._session = None
8995
self._max_time = max_time
9096
self._fixtures = fixtures or {}
97+
self._partner_id = partner_id
98+
self._user_agent = user_agent
9199

92100
# Type hints for IDE autocomplete, keep in sync with CLIENTS map above
93101
self.assets: _AssetsPublic
@@ -151,6 +159,21 @@ def max_time(self) -> float:
151159
"""bool: Returns maximum time in seconds to wait for confirmation"""
152160
return self._max_time
153161

162+
@property
163+
def version(self) -> str:
164+
"""str: Returns version of the archivist package"""
165+
return VERSION
166+
167+
@property
168+
def partner_id(self) -> str:
169+
"""str: Returns partner id if set when initialising an instance of this class"""
170+
return self._partner_id
171+
172+
@property
173+
def user_agent(self) -> str:
174+
"""str: Returns partner id if set when initialising an instance of this class"""
175+
return self._user_agent
176+
154177
@property
155178
def fixtures(self) -> "dict[str, Any]":
156179
"""dict: Contains predefined attributes for each endpoint"""
@@ -170,6 +193,18 @@ def __copy__(self):
170193

171194
def _add_headers(self, headers: "dict[str, str]|None") -> "dict[str, str]":
172195
newheaders = {**headers} if headers is not None else {}
196+
u = self.user_agent
197+
if u:
198+
newheaders[USER_AGENT] = (
199+
f"{self.user_agent} "
200+
f"{USER_AGENT_PREFIX}{self.version}"
201+
)
202+
else:
203+
newheaders[USER_AGENT] = f"{USER_AGENT_PREFIX}{self.version}"
204+
205+
p = self.partner_id
206+
if p:
207+
newheaders[PARTNER_ID] = p
173208

174209
return newheaders
175210

archivist/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
"RecordEvidence",
1414
]
1515

16+
USER_AGENT = "DataTrails-User-Agent"
17+
USER_AGENT_PREFIX = "pysdk/"
18+
PARTNER_ID = "DataTrails-Partner-ID"
19+
1620
# define in MIME canonical form
1721
HEADERS_REQUEST_TOTAL_COUNT = "X-Request-Total-Count"
1822
HEADERS_TOTAL_COUNT = "X-Total-Count"

archivist/parser.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ def common_parser(description: str):
129129
default=None,
130130
help="namespace of item population",
131131
)
132+
parser.add_argument(
133+
"-p",
134+
"--partner_id",
135+
type=str,
136+
dest="partner_id",
137+
action="store",
138+
default=None,
139+
help="partner id",
140+
)
132141

133142
return parser
134143

@@ -171,7 +180,12 @@ def endpoint(args):
171180
LOGGER.error("Critical error. Aborting.")
172181
sys_exit(1)
173182

174-
arch = Archivist(args.url, auth, fixtures=fixtures)
183+
arch = Archivist(
184+
args.url,
185+
auth,
186+
fixtures=fixtures,
187+
partner_id=args.partner_id,
188+
)
175189
if arch is None:
176190
LOGGER.error("Critical error. Aborting.")
177191
sys_exit(1)

examples/create_asset.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def main():
7575
create an example archivist connection and create an asset.
7676
7777
"""
78-
# optional call to set the logger level for all subsystems. The argumant can
78+
# optional call to set the logger level for all subsystems. The argument can
7979
# be either "INFO" or "DEBUG". For more sophisticated logging control see the
8080
# documentation.
8181
set_logger("INFO")
@@ -97,10 +97,14 @@ def main():
9797
# Initialize connection to Archivist. max_time is the time to wait for confirmation
9898
# of an asset or event creation - the default is 300 seconds but one can optionally
9999
# specify a different value.
100+
# The optional partner id field is allocated by Datatrails to partners - partners are then
101+
# expected to specify this value when submitting any request to the archivist product.
102+
# Leave blank if if you do not have a partner ID.
100103
with Archivist(
101104
"https://app.datatrails.ai",
102105
auth,
103106
max_time=300,
107+
partner_id="acme/f7a6beef-f01c-4b39-a494-3fa6b45d6bf4",
104108
) as arch:
105109
# Create a new asset
106110
asset = create_asset(arch)

functests/constants.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
"""
2-
hide docstringss
2+
constantss
33
"""
44

55
# pylint: disable=missing-docstring
6+
67
import unittest
78

9+
PARTNER_ID_VALUE = "acmecorp"
10+
USER_AGENT_VALUE = "functests/v0.1.0"
11+
812

913
class TestCase(unittest.TestCase):
1014
# ....

functests/execaccess_policies.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
from archivist.constants import ASSET_BEHAVIOURS
1515
from archivist.utils import get_auth
1616

17-
from .constants import TestCase
17+
from .constants import (
18+
PARTNER_ID_VALUE,
19+
USER_AGENT_VALUE,
20+
TestCase,
21+
)
1822

1923
# pylint: disable=fixme
2024
# pylint: disable=missing-docstring
@@ -117,7 +121,12 @@ def setUp(self):
117121
client_secret=getenv("DATATRAILS_APPREG_SECRET"),
118122
client_secret_filename=getenv("DATATRAILS_APPREG_SECRET_FILENAME"),
119123
)
120-
self.arch = Archivist(getenv("DATATRAILS_URL"), auth)
124+
self.arch = Archivist(
125+
getenv("DATATRAILS_URL"),
126+
auth,
127+
partner_id=PARTNER_ID_VALUE,
128+
user_agent=USER_AGENT_VALUE,
129+
)
121130

122131
# these are for access_policies
123132
self.ac_props = deepcopy(PROPS)
@@ -298,8 +307,12 @@ def setUp(self):
298307
super().setUp()
299308
with open(getenv("DATATRAILS_AUTHTOKEN_FILENAME_2"), encoding="utf-8") as fd:
300309
auth_2 = fd.read().strip()
301-
self.arch_2 = Archivist(getenv("DATATRAILS_URL"), auth_2)
302-
310+
self.arch_2 = Archivist(
311+
getenv("DATATRAILS_URL"),
312+
auth_2,
313+
partner_id=PARTNER_ID_VALUE,
314+
user_agent=USER_AGENT_VALUE,
315+
)
303316
# creates reciprocal subjects for arch 1 and arch 2.
304317
# subject 1 contains details of subject 2 to be shared
305318
self.subject_1, self.subject_2 = self.arch.subjects.share(

functests/execapplications.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
from archivist.errors import ArchivistUnauthenticatedError
1515
from archivist.utils import get_auth
1616

17-
from .constants import TestCase
17+
from .constants import (
18+
PARTNER_ID_VALUE,
19+
USER_AGENT_VALUE,
20+
TestCase,
21+
)
1822

1923
# pylint: disable=fixme
2024
# pylint: disable=missing-docstring
@@ -54,7 +58,12 @@ def setUp(self):
5458
client_secret=getenv("DATATRAILS_APPREG_SECRET"),
5559
client_secret_filename=getenv("DATATRAILS_APPREG_SECRET_FILENAME"),
5660
)
57-
self.arch = Archivist(getenv("DATATRAILS_URL"), auth)
61+
self.arch = Archivist(
62+
getenv("DATATRAILS_URL"),
63+
auth,
64+
partner_id=PARTNER_ID_VALUE,
65+
user_agent=USER_AGENT_VALUE,
66+
)
5867
self.display_name = f"{DISPLAY_NAME} {uuid4()}"
5968

6069
def tearDown(self):

functests/execassets.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
from archivist.proof_mechanism import ProofMechanism
1414
from archivist.utils import get_auth
1515

16-
from .constants import TestCase
16+
from .constants import (
17+
PARTNER_ID_VALUE,
18+
USER_AGENT_VALUE,
19+
TestCase,
20+
)
1721

1822
# pylint: disable=fixme
1923
# pylint: disable=missing-docstring
@@ -87,7 +91,13 @@ def setUp(self):
8791
client_secret=getenv("DATATRAILS_APPREG_SECRET"),
8892
client_secret_filename=getenv("DATATRAILS_APPREG_SECRET_FILENAME"),
8993
)
90-
self.arch = Archivist(getenv("DATATRAILS_URL"), auth, max_time=30)
94+
self.arch = Archivist(
95+
getenv("DATATRAILS_URL"),
96+
auth,
97+
max_time=30,
98+
partner_id=PARTNER_ID_VALUE,
99+
user_agent=USER_AGENT_VALUE,
100+
)
91101
self.attrs = deepcopy(ATTRS)
92102
self.traffic_light = deepcopy(ATTRS)
93103
self.traffic_light["arc_display_type"] = "Traffic light with violation camera"
@@ -233,7 +243,13 @@ def setUp(self):
233243
client_secret=getenv("DATATRAILS_APPREG_SECRET"),
234244
client_secret_filename=getenv("DATATRAILS_APPREG_SECRET_FILENAME"),
235245
)
236-
self.arch = Archivist(getenv("DATATRAILS_URL"), auth, max_time=600)
246+
self.arch = Archivist(
247+
getenv("DATATRAILS_URL"),
248+
auth,
249+
max_time=30,
250+
partner_id=PARTNER_ID_VALUE,
251+
user_agent=USER_AGENT_VALUE,
252+
)
237253

238254
def tearDown(self):
239255
self.arch.close()

functests/execattachments.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
from archivist.errors import ArchivistBadRequestError
1515
from archivist.utils import get_auth, get_url
1616

17-
from .constants import TestCase
17+
from .constants import (
18+
PARTNER_ID_VALUE,
19+
USER_AGENT_VALUE,
20+
TestCase,
21+
)
1822

1923
if getenv("DATATRAILS_LOGLEVEL") is not None:
2024
logger.set_logger(getenv("DATATRAILS_LOGLEVEL"))
@@ -46,7 +50,12 @@ def setUp(self):
4650
client_secret=getenv("DATATRAILS_APPREG_SECRET"),
4751
client_secret_filename=getenv("DATATRAILS_APPREG_SECRET_FILENAME"),
4852
)
49-
self.arch = Archivist(getenv("DATATRAILS_URL"), auth)
53+
self.arch = Archivist(
54+
getenv("DATATRAILS_URL"),
55+
auth,
56+
partner_id=PARTNER_ID_VALUE,
57+
user_agent=USER_AGENT_VALUE,
58+
)
5059
self.file_uuid: str = ""
5160

5261
with suppress(FileNotFoundError):
@@ -208,7 +217,12 @@ def setUp(self):
208217
client_secret=getenv("DATATRAILS_APPREG_SECRET"),
209218
client_secret_filename=getenv("DATATRAILS_APPREG_SECRET_FILENAME"),
210219
)
211-
self.arch = Archivist(getenv("DATATRAILS_URL"), auth)
220+
self.arch = Archivist(
221+
getenv("DATATRAILS_URL"),
222+
auth,
223+
partner_id=PARTNER_ID_VALUE,
224+
user_agent=USER_AGENT_VALUE,
225+
)
212226

213227
def tearDown(self):
214228
self.arch.close()

0 commit comments

Comments
 (0)