Skip to content

Commit 8d79357

Browse files
committed
Added SelfDescribingJson class (closes snowplow#140)
1 parent 792bec6 commit 8d79357

4 files changed

Lines changed: 56 additions & 19 deletions

File tree

snowplow_tracker/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from snowplow_tracker._version import __version__
22
from snowplow_tracker.subject import Subject
33
from snowplow_tracker.emitters import logger, Emitter, AsyncEmitter, CeleryEmitter, RedisEmitter
4+
from snowplow_tracker.self_describing_json import SelfDescribingJson
45
from snowplow_tracker.tracker import Tracker
56
from contracts import disable_all as disable_contracts, enable_all as enable_contracts
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
self_describing_json.py
3+
4+
Copyright (c) 2013-2014 Snowplow Analytics Ltd. All rights reserved.
5+
6+
This program is licensed to you under the Apache License Version 2.0,
7+
and you may not use this file except in compliance with the Apache License
8+
Version 2.0. You may obtain a copy of the Apache License Version 2.0 at
9+
http://www.apache.org/licenses/LICENSE-2.0.
10+
11+
Unless required by applicable law or agreed to in writing,
12+
software distributed under the Apache License Version 2.0 is distributed on
13+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14+
express or implied. See the Apache License Version 2.0 for the specific
15+
language governing permissions and limitations there under.
16+
17+
Authors: Anuj More, Alex Dean, Fred Blundun
18+
Copyright: Copyright (c) 2013-2014 Snowplow Analytics Ltd
19+
License: Apache License Version 2.0
20+
"""
21+
22+
class SelfDescribingJson(object):
23+
24+
def __init__(self, schema, data):
25+
self.schema = schema
26+
self.data = data
27+
28+
def to_json(self):
29+
return {
30+
"schema": self.schema,
31+
"data": self.data
32+
}

snowplow_tracker/test/integration/test_integration.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import json
2727
import base64
2828
from snowplow_tracker import tracker, _version, emitters, subject
29+
from snowplow_tracker.self_describing_json import SelfDescribingJson
2930
from httmock import all_requests, HTTMock
3031

3132
try:
@@ -149,7 +150,7 @@ def test_integration_struct_event(self):
149150
def test_integration_unstruct_event_non_base64(self):
150151
t = tracker.Tracker([default_emitter], default_subject, encode_base64=False)
151152
with HTTMock(pass_response_content):
152-
t.track_unstruct_event({"schema": "iglu:com.acme/viewed_product/jsonschema/2-0-2", "data": {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": 1000}})
153+
t.track_unstruct_event(SelfDescribingJson("iglu:com.acme/viewed_product/jsonschema/2-0-2", {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": 1000}))
153154
expected_fields = {"e": "ue"}
154155
for key in expected_fields:
155156
self.assertEquals(from_querystring(key, querystrings[-1]), expected_fields[key])
@@ -163,7 +164,7 @@ def test_integration_unstruct_event_non_base64(self):
163164
def test_integration_unstruct_event_base64(self):
164165
t = tracker.Tracker([default_emitter], default_subject, encode_base64=True)
165166
with HTTMock(pass_response_content):
166-
t.track_unstruct_event({"schema": "iglu:com.acme/viewed_product/jsonschema/2-0-2", "data": {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": 1000}})
167+
t.track_unstruct_event(SelfDescribingJson("iglu:com.acme/viewed_product/jsonschema/2-0-2", {"product_id": "ASO01043", "price$flt": 49.95, "walrus$tms": 1000}))
167168
expected_fields = {"e": "ue"}
168169
for key in expected_fields:
169170
self.assertEquals(from_querystring(key, querystrings[-1]), expected_fields[key])
@@ -177,7 +178,7 @@ def test_integration_unstruct_event_base64(self):
177178
def test_integration_context_non_base64(self):
178179
t = tracker.Tracker([default_emitter], default_subject, encode_base64=False)
179180
with HTTMock(pass_response_content):
180-
t.track_page_view("localhost", "local host", None, [{"schema": "iglu:com.example/user/jsonschema/2-0-3", "data": {"user_type": "tester"}}])
181+
t.track_page_view("localhost", "local host", None, [SelfDescribingJson("iglu:com.example/user/jsonschema/2-0-3", {"user_type": "tester"})])
181182
envelope_string = from_querystring("co", querystrings[-1])
182183
envelope = json.loads(unquote_plus(envelope_string))
183184
self.assertEquals(envelope, {
@@ -188,7 +189,7 @@ def test_integration_context_non_base64(self):
188189
def test_integration_context_base64(self):
189190
t = tracker.Tracker([default_emitter], default_subject, encode_base64=True)
190191
with HTTMock(pass_response_content):
191-
t.track_page_view("localhost", "local host", None, [{"schema": "iglu:com.example/user/jsonschema/2-0-3", "data": {"user_type": "tester"}}])
192+
t.track_page_view("localhost", "local host", None, [SelfDescribingJson("iglu:com.example/user/jsonschema/2-0-3", {"user_type": "tester"})])
192193
envelope_string = unquote_plus(from_querystring("cx", querystrings[-1]))
193194
envelope = json.loads((base64.urlsafe_b64decode(bytearray(envelope_string, "utf-8"))).decode("utf-8"))
194195
self.assertEquals(envelope, {

snowplow_tracker/tracker.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import time
2323
import uuid
2424
import six
25-
from snowplow_tracker import payload, _version
25+
from snowplow_tracker import payload, _version, SelfDescribingJson
2626
from snowplow_tracker import subject as _subject
2727
from contracts import contract, new_contract
2828

@@ -55,6 +55,10 @@ class Tracker:
5555

5656
new_contract("emitter", lambda s: hasattr(s, "input"))
5757

58+
new_contract("self_describing_json", lambda s: isinstance(s, SelfDescribingJson))
59+
60+
new_contract("context_array", "list(self_describing_json)")
61+
5862
@contract
5963
def __init__(self, emitters, subject=None,
6064
namespace=None, app_id=None, encode_base64=DEFAULT_ENCODE_BASE64):
@@ -138,15 +142,16 @@ def complete_payload(self, pb, context, tstamp):
138142
:param pb: Payload builder
139143
:type pb: payload
140144
:param context: Custom context for the event
141-
:type context: list(dict(string:*)) | None
145+
:type context: context_array | None
142146
:param tstamp: Optional user-provided timestamp for the event
143147
:type tstamp: int | float | None
144148
:rtype: tracker
145149
"""
146150
pb.add("eid", Tracker.get_uuid())
147151
pb.add("dtm", Tracker.get_timestamp(tstamp))
148152
if context is not None:
149-
context_envelope = {"schema": CONTEXT_SCHEMA, "data": context}
153+
context_jsons = list(map(lambda c: c.to_json(), context))
154+
context_envelope = SelfDescribingJson(CONTEXT_SCHEMA, context_jsons).to_json()
150155
pb.add_json(context_envelope, self.encode_base64, "cx", "co")
151156

152157
pb.add_dict(self.standard_nv_pairs)
@@ -165,7 +170,7 @@ def track_page_view(self, page_url, page_title=None, referrer=None, context=None
165170
:param referrer: Referrer of the page
166171
:type referrer: string_or_none
167172
:param context: Custom context for the event
168-
:type context: list(dict(string:*)) | None
173+
:type context: context_array | None
169174
:rtype: tracker
170175
"""
171176
pb = payload.Payload()
@@ -200,7 +205,7 @@ def track_ecommerce_transaction_item(self, order_id, sku, price, quantity,
200205
:param currency: The currency the price is expressed in
201206
:type currency: string_or_none
202207
:param context: Custom context for the event
203-
:type context: list(dict(string:*)) | None
208+
:type context: context_array | None
204209
:rtype: tracker
205210
"""
206211
pb = payload.Payload()
@@ -243,7 +248,7 @@ def track_ecommerce_transaction(self, order_id, total_value,
243248
:param items: The items in the transaction
244249
:type items: list(dict(str:*))
245250
:param context: Custom context for the event
246-
:type context: list(dict(string:*)) | None
251+
:type context: context_array | None
247252
:rtype: tracker
248253
"""
249254
pb = payload.Payload()
@@ -278,7 +283,7 @@ def track_screen_view(self, name=None, id_=None, context=None, tstamp=None):
278283
:param id_: Screen view ID
279284
:type id_: string_or_none
280285
:param context: Custom context for the event
281-
:type context: list(dict(string:*)) | None
286+
:type context: context_array | None
282287
:rtype: tracker
283288
"""
284289
screen_view_properties = {}
@@ -287,10 +292,8 @@ def track_screen_view(self, name=None, id_=None, context=None, tstamp=None):
287292
if id_ is not None:
288293
screen_view_properties["id"] = id_
289294

290-
event_json = {
291-
"schema": "%s/screen_view/%s/1-0-0" % (BASE_SCHEMA_PATH, SCHEMA_TAG),
292-
"data": screen_view_properties
293-
}
295+
event_json = SelfDescribingJson("%s/screen_view/%s/1-0-0" % (BASE_SCHEMA_PATH, SCHEMA_TAG), screen_view_properties)
296+
294297
return self.track_unstruct_event(event_json, context, tstamp)
295298

296299
@contract
@@ -311,7 +314,7 @@ def track_struct_event(self, category, action, label=None, property_=None, value
311314
:param value: A value associated with the user action
312315
:type value: int | float | None
313316
:param context: Custom context for the event
314-
:type context: list(dict(string:*)) | None
317+
:type context: context_array | None
315318
:rtype: tracker
316319
"""
317320
pb = payload.Payload()
@@ -331,13 +334,13 @@ def track_unstruct_event(self, event_json, context=None, tstamp=None):
331334
A "data" field containing the event properties and
332335
A "schema" field identifying the schema against which the data is validated
333336
334-
:type event_json: dict(string: string | dict)
337+
:type event_json: self_describing_json
335338
:param context: Custom context for the event
336-
:type context: list(dict(string:*)) | None
339+
:type context: context_array | None
337340
:rtype: tracker
338341
"""
339342

340-
envelope = {"schema": UNSTRUCT_EVENT_SCHEMA , "data": event_json}
343+
envelope = SelfDescribingJson(UNSTRUCT_EVENT_SCHEMA, event_json.to_json()).to_json()
341344

342345
pb = payload.Payload()
343346

0 commit comments

Comments
 (0)