amplitude-api 0.1.0 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -4
- data/.rubocop.yml +19 -1
- data/.travis.yml +2 -3
- data/Changelog.md +32 -0
- data/Gemfile +3 -12
- data/Rakefile +7 -5
- data/amplitude-api.gemspec +17 -15
- data/lib/amplitude-api.rb +3 -4
- data/lib/amplitude_api.rb +71 -45
- data/lib/amplitude_api/config.rb +27 -5
- data/lib/amplitude_api/event.rb +77 -16
- data/lib/amplitude_api/identification.rb +3 -1
- data/lib/amplitude_api/version.rb +3 -1
- data/readme.md +38 -6
- data/spec/lib/amplitude_api/event_spec.rb +212 -112
- data/spec/lib/amplitude_api/identification_spec.rb +19 -17
- data/spec/lib/amplitude_api_spec.rb +328 -188
- data/spec/spec_helper.rb +7 -5
- metadata +6 -7
- data/Gemfile.lock +0 -61
data/lib/amplitude_api/event.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class is 115 lines long. It's on the limit, it should be refactored before
|
4
|
+
# including more code.
|
5
|
+
#
|
6
|
+
# rubocop:disable Metrics/ClassLength
|
1
7
|
class AmplitudeAPI
|
2
8
|
# AmplitudeAPI::Event
|
3
9
|
class Event
|
@@ -7,15 +13,46 @@ class AmplitudeAPI
|
|
7
13
|
|
8
14
|
# Create a new Event
|
9
15
|
#
|
10
|
-
# See (Amplitude HTTP API Documentation)[https://amplitude.
|
16
|
+
# See (Amplitude HTTP API Documentation)[https://developers.amplitude.com/docs/http-api-v2]
|
11
17
|
# for a list of valid parameters and their types.
|
12
18
|
def initialize(attributes = {})
|
19
|
+
@extra_properties = []
|
13
20
|
attributes.each do |k, v|
|
14
|
-
send("#{k}=", v)
|
21
|
+
send("#{k}=", v)
|
15
22
|
end
|
16
23
|
validate_arguments
|
17
24
|
end
|
18
25
|
|
26
|
+
def method_missing(method_name, *args)
|
27
|
+
super if block_given?
|
28
|
+
super unless method_name.to_s.end_with? "="
|
29
|
+
|
30
|
+
property_name = method_name.to_s.delete_suffix("=")
|
31
|
+
|
32
|
+
@extra_properties << property_name
|
33
|
+
|
34
|
+
create_setter property_name
|
35
|
+
create_getter property_name
|
36
|
+
|
37
|
+
send("#{property_name}=".to_sym, *args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_setter(attribute_name)
|
41
|
+
self.class.send(:define_method, "#{attribute_name}=".to_sym) do |value|
|
42
|
+
instance_variable_set("@" + attribute_name.to_s, value)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_getter(attribute_name)
|
47
|
+
self.class.send(:define_method, attribute_name.to_sym) do
|
48
|
+
instance_variable_get("@" + attribute_name.to_s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def respond_to_missing?(method_name, *args)
|
53
|
+
@extra_properties.include?(method_name) || @extra_properties.include?("#{method_name}=") || super
|
54
|
+
end
|
55
|
+
|
19
56
|
def user_id=(value)
|
20
57
|
@user_id =
|
21
58
|
if value.respond_to?(:id)
|
@@ -36,28 +73,42 @@ class AmplitudeAPI
|
|
36
73
|
}
|
37
74
|
event[:user_id] = user_id if user_id
|
38
75
|
event[:device_id] = device_id if device_id
|
39
|
-
event.merge(optional_properties).merge(revenue_hash)
|
76
|
+
event.merge(optional_properties).merge(revenue_hash).merge(extra_properties)
|
40
77
|
end
|
41
78
|
alias to_h to_hash
|
42
79
|
|
43
80
|
# @return [ Hash ] Optional properties
|
81
|
+
#
|
82
|
+
# Returns optional properties (belong to the API but are optional)
|
44
83
|
def optional_properties
|
45
|
-
|
84
|
+
AmplitudeAPI::Config.optional_properties.map do |prop|
|
46
85
|
val = prop == :time ? formatted_time : send(prop)
|
47
86
|
val ? [prop, val] : nil
|
48
87
|
end.compact.to_h
|
49
88
|
end
|
50
89
|
|
90
|
+
# @return [ Hash ] Extra properties
|
91
|
+
#
|
92
|
+
# Returns optional properties (not belong to the API, are assigned by the user)
|
93
|
+
# This way, if the API is updated with new properties, the gem will be able
|
94
|
+
# to work with the new specification until the code is modified
|
95
|
+
def extra_properties
|
96
|
+
@extra_properties.map do |prop|
|
97
|
+
val = send(prop)
|
98
|
+
val ? [prop.to_sym, val] : nil
|
99
|
+
end.compact.to_h
|
100
|
+
end
|
101
|
+
|
51
102
|
# @return [ true, false ]
|
52
103
|
#
|
53
104
|
# Returns true if the event type matches one reserved by Amplitude API.
|
54
105
|
def reserved_event?(type)
|
55
|
-
[
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
106
|
+
["[Amplitude] Start Session",
|
107
|
+
"[Amplitude] End Session",
|
108
|
+
"[Amplitude] Revenue",
|
109
|
+
"[Amplitude] Revenue (Verified)",
|
110
|
+
"[Amplitude] Revenue (Unverified)",
|
111
|
+
"[Amplitude] Merged User"].include?(type)
|
61
112
|
end
|
62
113
|
|
63
114
|
# @return [ true, false ]
|
@@ -89,15 +140,23 @@ class AmplitudeAPI
|
|
89
140
|
end
|
90
141
|
|
91
142
|
def validate_required_arguments
|
92
|
-
raise ArgumentError,
|
93
|
-
raise ArgumentError,
|
94
|
-
raise ArgumentError,
|
143
|
+
raise ArgumentError, "You must provide user_id or device_id (or both)" unless user_id || device_id
|
144
|
+
raise ArgumentError, "You must provide event_type" unless event_type
|
145
|
+
raise ArgumentError, "Invalid event_type - cannot match a reserved event name" if reserved_event?(event_type)
|
95
146
|
end
|
96
147
|
|
97
148
|
def validate_revenue_arguments
|
98
|
-
return
|
99
|
-
|
100
|
-
|
149
|
+
return true if !revenue_type && !product_id
|
150
|
+
return true if revenue || price
|
151
|
+
|
152
|
+
raise ArgumentError, revenue_error_message
|
153
|
+
end
|
154
|
+
|
155
|
+
def revenue_error_message
|
156
|
+
error_field = "product_id" if product_id
|
157
|
+
error_field = "revenue_type" if revenue_type
|
158
|
+
|
159
|
+
"You must provide a price or a revenue in order to use the field #{error_field}"
|
101
160
|
end
|
102
161
|
|
103
162
|
def revenue_hash
|
@@ -106,6 +165,7 @@ class AmplitudeAPI
|
|
106
165
|
revenue_hash[:revenueType] = revenue_type if revenue_type
|
107
166
|
revenue_hash[:quantity] = quantity if quantity
|
108
167
|
revenue_hash[:price] = price if price
|
168
|
+
revenue_hash[:revenue] = revenue if revenue
|
109
169
|
revenue_hash
|
110
170
|
end
|
111
171
|
|
@@ -114,3 +174,4 @@ class AmplitudeAPI
|
|
114
174
|
end
|
115
175
|
end
|
116
176
|
end
|
177
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class AmplitudeAPI
|
2
4
|
# AmplitudeAPI::Identification
|
3
5
|
class Identification
|
@@ -16,7 +18,7 @@ class AmplitudeAPI
|
|
16
18
|
# @param [ String ] user_id a user_id to associate with the identification
|
17
19
|
# @param [ String ] device_id a device_id to associate with the identification
|
18
20
|
# @param [ Hash ] user_properties various properties to attach to the user identification
|
19
|
-
def initialize(user_id:
|
21
|
+
def initialize(user_id: "", device_id: nil, user_properties: {})
|
20
22
|
self.user_id = user_id
|
21
23
|
self.device_id = device_id if device_id
|
22
24
|
self.user_properties = user_properties
|
data/readme.md
CHANGED
@@ -19,7 +19,7 @@ AmplitudeAPI.config.api_key = "abcdef123456"
|
|
19
19
|
|
20
20
|
|
21
21
|
event = AmplitudeAPI::Event.new({
|
22
|
-
user_id: "
|
22
|
+
user_id: "12345",
|
23
23
|
event_type: "clicked on home",
|
24
24
|
time: Time.now,
|
25
25
|
insert_id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
|
@@ -31,7 +31,38 @@ event = AmplitudeAPI::Event.new({
|
|
31
31
|
AmplitudeAPI.track(event)
|
32
32
|
```
|
33
33
|
|
34
|
-
|
34
|
+
You can track multiple events with a single call, with the only limit of the payload
|
35
|
+
size imposed by Amplitude:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
event_1 = AmplitudeAPI::Event.new(...)
|
39
|
+
event_2 = AmplitudeAPI::Event.new(...)
|
40
|
+
|
41
|
+
AmplitudeAPI.track(event_1, event_2)
|
42
|
+
```
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
events = [event_1, event_2]
|
46
|
+
AmplitudeAPI.track(*events)
|
47
|
+
```
|
48
|
+
|
49
|
+
In case you use an integer as the time, it is expected to be in seconds. Values in
|
50
|
+
the time field will be converted to milliseconds using `->(time) { time ? time.to_i * 1_000 : nil }`
|
51
|
+
You can change this behaviour and use your custom formatter. For example, in case
|
52
|
+
you wanted to use milliseconds instead of seconds you could do this:
|
53
|
+
```ruby
|
54
|
+
AmplitudeAPI.config.time_formatter = ->(time) { time ? time.to_i : nil },
|
55
|
+
```
|
56
|
+
|
57
|
+
You can speficy track options in the config. The options will be applied to all subsequent requests:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
AmplitudeAPI.config.options = { min_id_length: 10 }
|
61
|
+
AmplitudeAPI.track(event)
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
## User Privacy APIs
|
35
66
|
|
36
67
|
The following code snippet will delete a user from amplitude
|
37
68
|
|
@@ -42,12 +73,13 @@ AmplitudeAPI.config.api_key = "abcdef123456"
|
|
42
73
|
# Configure your Amplitude Secret Key
|
43
74
|
AmplitudeAPI.config.secret_key = "secretMcSecret"
|
44
75
|
|
45
|
-
AmplitudeAPI.delete(user_ids: [
|
46
|
-
requester: "privacy@
|
76
|
+
AmplitudeAPI.delete(user_ids: ["12345"],
|
77
|
+
requester: "privacy@example.com"
|
47
78
|
)
|
48
79
|
```
|
49
80
|
|
50
|
-
Currently, we are using this in Rails and using ActiveJob to dispatch events asynchronously. I plan on moving
|
81
|
+
Currently, we are using this in Rails and using ActiveJob to dispatch events asynchronously. I plan on moving
|
82
|
+
background/asynchronous support into this gem.
|
51
83
|
|
52
84
|
## What's Next
|
53
85
|
|
@@ -55,7 +87,7 @@ Currently, we are using this in Rails and using ActiveJob to dispatch events asy
|
|
55
87
|
* Configurable default account to use when no `user_id` present
|
56
88
|
|
57
89
|
## Other useful resources
|
58
|
-
* [Amplitude HTTP Api Documentation](https://amplitude.
|
90
|
+
* [Amplitude HTTP API V2 Api Documentation](https://developers.amplitude.com/docs/http-api-v2)
|
59
91
|
* [Segment.io Amplitude integration](https://segment.com/docs/integrations/amplitude/)
|
60
92
|
|
61
93
|
## Contributing
|
@@ -1,277 +1,318 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
2
4
|
|
3
5
|
describe AmplitudeAPI::Event do
|
4
6
|
user = Struct.new(:id)
|
5
7
|
|
6
|
-
context
|
7
|
-
describe
|
8
|
+
context "with a user object" do
|
9
|
+
describe "#body" do
|
8
10
|
it "populates with the user's id" do
|
9
11
|
event = described_class.new(
|
10
12
|
user_id: user.new(123),
|
11
|
-
event_type:
|
13
|
+
event_type: "clicked on home"
|
12
14
|
)
|
13
15
|
expect(event.to_hash[:user_id]).to eq(123)
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
|
-
context
|
19
|
-
describe
|
20
|
+
context "with a user id" do
|
21
|
+
describe "#body" do
|
20
22
|
it "populates with the user's id" do
|
21
23
|
event = described_class.new(
|
22
24
|
user_id: 123,
|
23
|
-
event_type:
|
25
|
+
event_type: "clicked on home"
|
24
26
|
)
|
25
27
|
expect(event.to_hash[:user_id]).to eq(123)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
|
-
context
|
31
|
-
describe
|
32
|
-
it
|
32
|
+
context "without a user" do
|
33
|
+
describe "#body" do
|
34
|
+
it "populates with the unknown user" do
|
33
35
|
event = described_class.new(
|
34
36
|
user_id: nil,
|
35
|
-
event_type:
|
37
|
+
event_type: "clicked on home"
|
36
38
|
)
|
37
39
|
expect(event.to_hash[:user_id]).to eq(AmplitudeAPI::USER_WITH_NO_ACCOUNT)
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
42
|
-
describe
|
43
|
-
context
|
44
|
-
it
|
44
|
+
describe "init" do
|
45
|
+
context "attributes" do
|
46
|
+
it "accepts string attributes" do
|
45
47
|
time = Time.at(1_451_606_400_000 / 1_000)
|
46
48
|
event = described_class.new(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
"user_id" => 123,
|
50
|
+
"device_id" => "abcd",
|
51
|
+
"event_type" => "sausage",
|
52
|
+
"event_properties" => { "a" => "b" },
|
53
|
+
"user_properties" => { "c" => "d" },
|
54
|
+
"time" => time,
|
55
|
+
"ip" => "127.0.0.1",
|
56
|
+
"platform" => "Web",
|
57
|
+
"country" => "United States",
|
58
|
+
"insert_id" => "bestId"
|
57
59
|
)
|
58
60
|
|
59
|
-
expect(event.to_hash).to eq(event_type:
|
61
|
+
expect(event.to_hash).to eq(event_type: "sausage",
|
60
62
|
user_id: 123,
|
61
|
-
device_id:
|
62
|
-
event_properties: {
|
63
|
-
user_properties: {
|
63
|
+
device_id: "abcd",
|
64
|
+
event_properties: { "a" => "b" },
|
65
|
+
user_properties: { "c" => "d" },
|
64
66
|
time: 1_451_606_400_000,
|
65
|
-
ip:
|
66
|
-
platform:
|
67
|
-
country:
|
68
|
-
insert_id:
|
67
|
+
ip: "127.0.0.1",
|
68
|
+
platform: "Web",
|
69
|
+
country: "United States",
|
70
|
+
insert_id: "bestId")
|
69
71
|
end
|
70
72
|
|
71
|
-
it
|
73
|
+
it "accepts symbol attributes" do
|
72
74
|
time = Time.at(1_451_606_400_000 / 1_000)
|
73
75
|
event = described_class.new(
|
74
76
|
user_id: 123,
|
75
|
-
device_id:
|
76
|
-
event_type:
|
77
|
-
event_properties: {
|
78
|
-
user_properties: {
|
77
|
+
device_id: "abcd",
|
78
|
+
event_type: "sausage",
|
79
|
+
event_properties: { "a" => "b" },
|
80
|
+
user_properties: { "c" => "d" },
|
79
81
|
time: time,
|
80
|
-
ip:
|
81
|
-
platform:
|
82
|
-
country:
|
83
|
-
insert_id:
|
82
|
+
ip: "127.0.0.1",
|
83
|
+
platform: "Web",
|
84
|
+
country: "United States",
|
85
|
+
insert_id: "bestId"
|
84
86
|
)
|
85
87
|
|
86
|
-
expect(event.to_hash).to eq(event_type:
|
88
|
+
expect(event.to_hash).to eq(event_type: "sausage",
|
87
89
|
user_id: 123,
|
88
|
-
device_id:
|
89
|
-
event_properties: {
|
90
|
-
user_properties: {
|
90
|
+
device_id: "abcd",
|
91
|
+
event_properties: { "a" => "b" },
|
92
|
+
user_properties: { "c" => "d" },
|
91
93
|
time: 1_451_606_400_000,
|
92
|
-
ip:
|
93
|
-
platform:
|
94
|
-
country:
|
95
|
-
insert_id:
|
94
|
+
ip: "127.0.0.1",
|
95
|
+
platform: "Web",
|
96
|
+
country: "United States",
|
97
|
+
insert_id: "bestId")
|
96
98
|
end
|
97
99
|
end
|
98
100
|
|
99
|
-
context
|
100
|
-
it
|
101
|
+
context "the user sends a revenue_type or a product_id" do
|
102
|
+
it "raises an error if there is not a price neither a revenue" do
|
103
|
+
expect do
|
104
|
+
described_class.new(
|
105
|
+
user_id: 123,
|
106
|
+
event_type: "bad event",
|
107
|
+
product_id: "hopscotch.4lyfe"
|
108
|
+
)
|
109
|
+
end.to raise_error ArgumentError, /You must provide a price or a revenue/
|
110
|
+
|
111
|
+
expect do
|
112
|
+
described_class.new(
|
113
|
+
user_id: 123,
|
114
|
+
event_type: "bad event",
|
115
|
+
revenue_type: "whatever"
|
116
|
+
)
|
117
|
+
end.to raise_error ArgumentError, /You must provide a price or a revenue/
|
118
|
+
end
|
119
|
+
|
120
|
+
it "does not raise an error if there is a price" do
|
121
|
+
expect do
|
122
|
+
described_class.new(
|
123
|
+
user_id: 123,
|
124
|
+
event_type: "bad event",
|
125
|
+
product_id: "hopscotch.4lyfe",
|
126
|
+
price: 10.2
|
127
|
+
)
|
128
|
+
end.not_to raise_error
|
129
|
+
|
101
130
|
expect do
|
102
131
|
described_class.new(
|
103
132
|
user_id: 123,
|
104
|
-
event_type:
|
105
|
-
|
133
|
+
event_type: "bad event",
|
134
|
+
revenue_type: "whatever",
|
135
|
+
price: 10.2
|
106
136
|
)
|
107
|
-
end.
|
137
|
+
end.not_to raise_error
|
108
138
|
end
|
109
139
|
|
110
|
-
it
|
140
|
+
it "does not raise an error if there is a revenue" do
|
141
|
+
expect do
|
142
|
+
described_class.new(
|
143
|
+
user_id: 123,
|
144
|
+
event_type: "bad event",
|
145
|
+
product_id: "hopscotch.4lyfe",
|
146
|
+
revenue: 100.1
|
147
|
+
)
|
148
|
+
end.not_to raise_error
|
149
|
+
|
111
150
|
expect do
|
112
151
|
described_class.new(
|
113
152
|
user_id: 123,
|
114
|
-
event_type:
|
115
|
-
revenue_type:
|
153
|
+
event_type: "bad event",
|
154
|
+
revenue_type: "whatever",
|
155
|
+
revenue: 100.1
|
116
156
|
)
|
117
|
-
end.
|
157
|
+
end.not_to raise_error
|
118
158
|
end
|
119
159
|
end
|
120
160
|
end
|
121
161
|
|
122
|
-
describe
|
123
|
-
it
|
162
|
+
describe "#to_hash" do
|
163
|
+
it "includes the event type" do
|
124
164
|
event = described_class.new(
|
125
165
|
user_id: 123,
|
126
|
-
event_type:
|
166
|
+
event_type: "clicked on home"
|
127
167
|
)
|
128
|
-
expect(event.to_hash[:event_type]).to eq(
|
168
|
+
expect(event.to_hash[:event_type]).to eq("clicked on home")
|
129
169
|
end
|
130
170
|
|
131
|
-
it
|
171
|
+
it "includes arbitrary properties" do
|
132
172
|
event = described_class.new(
|
133
173
|
user_id: 123,
|
134
|
-
event_type:
|
174
|
+
event_type: "clicked on home",
|
135
175
|
event_properties: { abc: :def }
|
136
176
|
)
|
137
177
|
expect(event.to_hash[:event_properties]).to eq(abc: :def)
|
138
178
|
end
|
139
179
|
|
140
|
-
describe
|
141
|
-
it
|
180
|
+
describe "time" do
|
181
|
+
it "includes a time for the event" do
|
142
182
|
time = Time.at(1_451_606_400_000 / 1_000)
|
143
183
|
event = described_class.new(
|
144
184
|
user_id: 123,
|
145
|
-
event_type:
|
185
|
+
event_type: "clicked on home",
|
146
186
|
time: time
|
147
187
|
)
|
148
188
|
expect(event.to_hash[:time]).to eq(1_451_606_400_000)
|
149
189
|
end
|
150
190
|
|
151
|
-
it
|
191
|
+
it "does not include time if it is not set" do
|
152
192
|
event = described_class.new(
|
153
193
|
user_id: 123,
|
154
|
-
event_type:
|
194
|
+
event_type: "clicked on home"
|
155
195
|
)
|
156
196
|
expect(event.to_hash).not_to have_key(:time)
|
157
197
|
end
|
158
198
|
end
|
159
199
|
|
160
|
-
describe
|
161
|
-
it
|
200
|
+
describe "insert_id" do
|
201
|
+
it "includes an insert_id for the event" do
|
162
202
|
event = described_class.new(
|
163
203
|
user_id: 123,
|
164
|
-
event_type:
|
165
|
-
insert_id:
|
204
|
+
event_type: "clicked on home",
|
205
|
+
insert_id: "foo-bar"
|
166
206
|
)
|
167
|
-
expect(event.to_hash[:insert_id]).to eq(
|
207
|
+
expect(event.to_hash[:insert_id]).to eq("foo-bar")
|
168
208
|
end
|
169
209
|
|
170
|
-
it
|
210
|
+
it "does not include insert_id if it is not set" do
|
171
211
|
event = described_class.new(
|
172
212
|
user_id: 123,
|
173
|
-
event_type:
|
213
|
+
event_type: "clicked on home"
|
174
214
|
)
|
175
215
|
expect(event.to_hash).not_to have_key(:insert_id)
|
176
216
|
end
|
177
217
|
end
|
178
218
|
|
179
|
-
describe
|
180
|
-
it
|
219
|
+
describe "platform" do
|
220
|
+
it "includes the platform for the event" do
|
181
221
|
event = described_class.new(
|
182
222
|
user_id: 123,
|
183
|
-
event_type:
|
184
|
-
platform:
|
223
|
+
event_type: "clicked on home",
|
224
|
+
platform: "Web"
|
185
225
|
)
|
186
|
-
expect(event.to_hash[:platform]).to eq(
|
226
|
+
expect(event.to_hash[:platform]).to eq("Web")
|
187
227
|
end
|
188
228
|
|
189
|
-
it
|
229
|
+
it "does not include the platform if it is not set" do
|
190
230
|
event = described_class.new(
|
191
231
|
user_id: 123,
|
192
|
-
event_type:
|
232
|
+
event_type: "clicked on home"
|
193
233
|
)
|
194
234
|
expect(event.to_hash).not_to have_key(:platform)
|
195
235
|
end
|
196
236
|
end
|
197
237
|
|
198
|
-
describe
|
199
|
-
it
|
238
|
+
describe "country" do
|
239
|
+
it "includes the country for the event" do
|
200
240
|
event = described_class.new(
|
201
241
|
user_id: 123,
|
202
|
-
event_type:
|
203
|
-
country:
|
242
|
+
event_type: "clicked on home",
|
243
|
+
country: "United States"
|
204
244
|
)
|
205
|
-
expect(event.to_hash[:country]).to eq(
|
245
|
+
expect(event.to_hash[:country]).to eq("United States")
|
206
246
|
end
|
207
247
|
|
208
|
-
it
|
248
|
+
it "does not include the country if it is not set" do
|
209
249
|
event = described_class.new(
|
210
250
|
user_id: 123,
|
211
|
-
event_type:
|
251
|
+
event_type: "clicked on home"
|
212
252
|
)
|
213
253
|
expect(event.to_hash).not_to have_key(:country)
|
214
254
|
end
|
215
255
|
end
|
216
256
|
|
217
|
-
describe
|
218
|
-
it
|
257
|
+
describe "revenue params" do
|
258
|
+
it "includes the price if it is set" do
|
219
259
|
price = 100_000.99
|
220
260
|
event = described_class.new(
|
221
261
|
user_id: 123,
|
222
|
-
event_type:
|
262
|
+
event_type: "clicked on home",
|
223
263
|
price: price
|
224
264
|
)
|
225
265
|
expect(event.to_hash[:price]).to eq(price)
|
226
266
|
end
|
227
267
|
|
228
|
-
it
|
229
|
-
|
268
|
+
it "includes the quantity if it is set" do
|
269
|
+
quantity = 100
|
230
270
|
event = described_class.new(
|
231
271
|
user_id: 123,
|
232
|
-
event_type:
|
233
|
-
|
272
|
+
event_type: "clicked on home",
|
273
|
+
quantity: quantity,
|
274
|
+
price: 10.99
|
234
275
|
)
|
235
|
-
expect(event.to_hash[:quantity]).to eq(
|
276
|
+
expect(event.to_hash[:quantity]).to eq(quantity)
|
236
277
|
end
|
237
278
|
|
238
|
-
it
|
239
|
-
|
279
|
+
it "includes the revenue if it is set" do
|
280
|
+
revenue = 100
|
240
281
|
event = described_class.new(
|
241
282
|
user_id: 123,
|
242
|
-
event_type:
|
243
|
-
quantity:
|
244
|
-
|
283
|
+
event_type: "clicked on home",
|
284
|
+
quantity: 456,
|
285
|
+
revenue: revenue
|
245
286
|
)
|
246
|
-
expect(event.to_hash[:
|
287
|
+
expect(event.to_hash[:revenue]).to eq(revenue)
|
247
288
|
end
|
248
289
|
|
249
|
-
it
|
250
|
-
product_id =
|
290
|
+
it "includes the productID if set" do
|
291
|
+
product_id = "hopscotch.subscriptions.rule"
|
251
292
|
event = described_class.new(
|
252
293
|
user_id: 123,
|
253
|
-
event_type:
|
294
|
+
event_type: "clicked on home",
|
254
295
|
price: 199.99,
|
255
296
|
product_id: product_id
|
256
297
|
)
|
257
298
|
expect(event.to_hash[:productId]).to eq(product_id)
|
258
299
|
end
|
259
300
|
|
260
|
-
it
|
261
|
-
revenue_type =
|
301
|
+
it "includes the revenueType if set" do
|
302
|
+
revenue_type = "income"
|
262
303
|
event = described_class.new(
|
263
304
|
user_id: 123,
|
264
|
-
event_type:
|
305
|
+
event_type: "clicked on home",
|
265
306
|
price: 199.99,
|
266
307
|
revenue_type: revenue_type
|
267
308
|
)
|
268
309
|
expect(event.to_hash[:revenueType]).to eq(revenue_type)
|
269
310
|
end
|
270
311
|
|
271
|
-
it
|
312
|
+
it "does not include revenue params if they are not set" do
|
272
313
|
event = described_class.new(
|
273
314
|
user_id: 123,
|
274
|
-
event_type:
|
315
|
+
event_type: "clicked on home"
|
275
316
|
)
|
276
317
|
expect(event.to_hash).not_to have_key(:quantity)
|
277
318
|
expect(event.to_hash).not_to have_key(:revenueType)
|
@@ -280,4 +321,63 @@ describe AmplitudeAPI::Event do
|
|
280
321
|
end
|
281
322
|
end
|
282
323
|
end
|
324
|
+
|
325
|
+
describe "arbitrary properties" do
|
326
|
+
# We need to create a class for each test because the methods we are calling
|
327
|
+
# in this test group are modifying the class
|
328
|
+
let(:klass) { Class.new described_class }
|
329
|
+
|
330
|
+
let(:event) {
|
331
|
+
klass.new(
|
332
|
+
user_id: 123,
|
333
|
+
event_type: "bad event"
|
334
|
+
)
|
335
|
+
}
|
336
|
+
|
337
|
+
it "creates properties on initialization" do
|
338
|
+
property_value = "an arbitrary value"
|
339
|
+
creation_data = {
|
340
|
+
user_id: "whatever",
|
341
|
+
event_type: "something happened",
|
342
|
+
arbitrary_property: property_value
|
343
|
+
}
|
344
|
+
|
345
|
+
initialized_event = klass.new(creation_data)
|
346
|
+
|
347
|
+
expect(initialized_event.arbitrary_property).to eq property_value
|
348
|
+
end
|
349
|
+
|
350
|
+
it "creates arbitrary properties when assigning values" do
|
351
|
+
event.arbitrary_property = "arbitrary value"
|
352
|
+
|
353
|
+
expect(event.arbitrary_property).to eq "arbitrary value"
|
354
|
+
end
|
355
|
+
|
356
|
+
it "responds_to? arbitrary properties" do
|
357
|
+
event.arbitrary_property = "arbitrary value"
|
358
|
+
|
359
|
+
expect(event.respond_to?(:arbitrary_property)).to be true
|
360
|
+
expect(event.respond_to?(:arbitrary_property=)).to be true
|
361
|
+
end
|
362
|
+
|
363
|
+
it "does not define property until assigned" do
|
364
|
+
expect {
|
365
|
+
event.undefined_property
|
366
|
+
}.to raise_error NoMethodError, /undefined_property/
|
367
|
+
end
|
368
|
+
|
369
|
+
it "do not accepts blocks when assigning values to create properties" do
|
370
|
+
expect do
|
371
|
+
event.arbitrary_property { puts "whatever" }
|
372
|
+
end.to raise_error NoMethodError
|
373
|
+
end
|
374
|
+
|
375
|
+
it "includes arbitrary properties in the generated hash" do
|
376
|
+
event.arbitrary_property = "arbitrary value"
|
377
|
+
|
378
|
+
hash = event.to_hash
|
379
|
+
|
380
|
+
expect(hash).to include(arbitrary_property: "arbitrary value")
|
381
|
+
end
|
382
|
+
end
|
283
383
|
end
|