Skip to content

Commit d5f6431

Browse files
authored
Merge pull request #95 from urbanairship/TOOLSLIBS-309-b
Merge in Named user attributes branch from OLIOEX, prep for release
2 parents 039c4f3 + faa4540 commit d5f6431

16 files changed

+309
-18
lines changed

.github/PULL_REQUEST_TEMPLATE.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ Please include link to open issue if applicable.
1515

1616
* I've tested for Ruby versions:
1717

18-
- [ ] 2.2.5
19-
- [ ] 2.3.1
18+
- [ ] 2.6.7
19+
- [ ] 2.7.2
2020

2121
### Airship Contribution Agreement
2222
[Link here](https://docs.google.com/forms/d/e/1FAIpQLScErfiz-fXSPpVZ9r8Di2Tr2xDFxt5MgzUel0__9vqUgvko7Q/viewform)

.travis.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
language: ruby
22
rvm:
3-
- 2.2.5
4-
- 2.3.1
3+
- 2.6.7
54
- 2.7.2

CHANGELOG

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
--------------------
2+
8.0.0
3+
--------------------
4+
- Updates officially supported versions to 2.6.7 and 2.7.2
5+
- Fixes headers to reduce warning messages
6+
- Fixes for multiple mis-set named user urls
7+
- Fix to automatically convert an integer named user to a string so it can be successfully submitted
8+
- Adds attribute support for named users
9+
110
--------------------
211
7.0.0
312
--------------------

README.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ Requirements
1414

1515
We officially support the following Ruby versions::
1616

17-
2.2.5
18-
2.3.1
17+
2.6.7
18+
2.7.2
1919

20-
Newer versions should work as well.
20+
Newer 2.x versions should work as well.
2121

2222

2323
Functionality
2424
=============
2525

26-
Version 5.0 is a major upgrade, as some features have been removed that were present in earlier versions. A more detailed list of changes can be found in the CHANGELOG.
26+
Version 8.0 is a major upgrade, as we have changed the tested/supported versions of Ruby. A more detailed list of changes can be found in the CHANGELOG.
2727

2828

2929
Questions

docs/attributes.rst

+21
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@ The following will set an attribute for a given channel ID.
2424

2525
This should return a 200 response
2626

27+
Set or Remove Attributes for a Named User
28+
-----------------------------------------
29+
30+
The following example shows you how to set and remove attributes on a given named user.
31+
32+
.. code-block:: ruby
33+
34+
require 'urbanairship'
35+
airship = Urbanairship::Client.new(key: 'application_key', secret: 'master_secret')
36+
named_user = Urbanairship::NamedUser.new(client: airship)
37+
named_user.named_user_id = 'named_user'
38+
named_user.update_attributes(attributes: [
39+
{ action: 'set', key: 'first_name', value: 'Firstname' },
40+
{ action: 'remove', key: 'nickname' },
41+
{ action: 'set', key: 'last_name', value: 'Lastname', timestamp: Time.now.utc }
42+
])
43+
44+
.. note::
45+
46+
Timestamp is optional, if missing it will default to 'now'
47+
2748
Send Push to Audience with Attribute Specifications
2849
---------------------------------------------------
2950

docs/named_user.rst

+22
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,25 @@ see `the API documentation
100100

101101
A single request may contain an add or remove field, both, or a single set
102102
field.
103+
104+
Attributes
105+
----------
106+
107+
Set or remove attributes on a named user. For more information, see `the API documentation
108+
https://docs.airship.com/api/ua/#operation-api-named_users-named_user_id-attributes-post>`__
109+
110+
.. code-block:: ruby
111+
112+
require 'urbanairship'
113+
airship = Urbanairship::Client.new(key: 'application_key', secret: 'master_secret')
114+
named_user = Urbanairship::NamedUser.new(client: airship)
115+
named_user.named_user_id = 'named_user'
116+
named_user.update_attributes(attributes: [
117+
{ action: 'set', key: 'first_name', value: 'Firstname' },
118+
{ action: 'remove', key: 'nickname' },
119+
{ action: 'set', key: 'last_name', value: 'Lastname', timestamp: Time.now.utc }
120+
])
121+
122+
.. note::
123+
124+
Timestamp is optional, if missing it will default to 'now'.

lib/urbanairship.rb

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
require 'urbanairship/devices/mms_notification'
1414
require 'urbanairship/devices/create_and_send'
1515
require 'urbanairship/devices/attribute'
16+
require 'urbanairship/devices/attributes'
1617
require 'urbanairship/client'
1718
require 'urbanairship/common'
1819
require 'urbanairship/configuration'

lib/urbanairship/common.rb

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
module Urbanairship
55
# Features mixed in to all classes
66
module Common
7+
CONTENT_TYPE = 'application/json'
8+
79
def apid_path(path='')
810
"/apids/#{path}"
911
end
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
module Urbanairship
3+
module Devices
4+
class Attributes
5+
6+
SET = 'set'
7+
REMOVE = 'remove'
8+
9+
def initialize(attributes)
10+
@attributes = attributes
11+
end
12+
13+
def payload
14+
@payload ||= { attributes: attributes_list }
15+
end
16+
17+
private
18+
19+
def attributes_list
20+
@attributes.map{ |attribute| attribute_payload(attribute) }
21+
end
22+
23+
def attribute_payload(attribute)
24+
if REMOVE == attribute[:action]
25+
remove_payload(attribute)
26+
else
27+
set_payload(attribute)
28+
end
29+
end
30+
31+
def set_payload(attribute)
32+
{
33+
action: SET,
34+
key: attribute[:key],
35+
value: attribute[:value],
36+
timestamp: (attribute[:timestamp] || timestamp).iso8601,
37+
}
38+
end
39+
40+
def remove_payload(attribute)
41+
{
42+
action: REMOVE,
43+
key: attribute[:key],
44+
timestamp: (attribute[:timestamp] || timestamp).iso8601,
45+
}
46+
end
47+
48+
def timestamp
49+
@timestamp ||= Time.now.utc
50+
end
51+
end
52+
end
53+
end

lib/urbanairship/devices/named_user.rb

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
require 'urbanairship'
22

3-
43
module Urbanairship
54
module Devices
65
class NamedUser
@@ -13,6 +12,17 @@ def initialize(client: required('client'))
1312
@named_user_id = nil
1413
end
1514

15+
def update_attributes(attributes: required('attributes'))
16+
response = @client.send_request(
17+
method: 'POST',
18+
body: Urbanairship::Attributes.new(attributes).payload.to_json,
19+
path: named_users_path("#{@named_user_id}/attributes"),
20+
content_type: CONTENT_TYPE,
21+
)
22+
logger.info { "Updated attributes for named_user #{@named_user_id}" }
23+
response
24+
end
25+
1626
def associate(channel_id: required('channel_id'), device_type: nil)
1727
fail ArgumentError,
1828
'named_user_id is required for association' if @named_user_id.nil?
@@ -26,7 +36,7 @@ def associate(channel_id: required('channel_id'), device_type: nil)
2636
method: 'POST',
2737
body: JSON.dump(payload),
2838
path: named_users_path('associate'),
29-
content_type: 'application/json'
39+
content_type: CONTENT_TYPE
3040
)
3141
logger.info { "Associated channel_id #{channel_id} with named_user #{@named_user_id}" }
3242
response
@@ -40,8 +50,8 @@ def disassociate(channel_id: required('channel_id'), device_type: nil)
4050
response = @client.send_request(
4151
method: 'POST',
4252
body: JSON.dump(payload),
43-
path: named_users_path('/disassociate'),
44-
content_type: 'application/json'
53+
path: named_users_path('disassociate'),
54+
content_type: CONTENT_TYPE
4555
)
4656
logger.info { "Dissociated channel_id #{channel_id}" }
4757
response
@@ -51,8 +61,8 @@ def lookup
5161
fail ArgumentError,
5262
'named_user_id is required for lookup' if @named_user_id.nil?
5363
response = @client.send_request(
54-
method: 'GET',
55-
path: named_users_path('?id=' + @named_user_id),
64+
method: 'GET',
65+
path: named_users_path('?id=' + @named_user_id),
5666
)
5767
logger.info { "Retrieved information on named_user_id #{@named_user_id}" }
5868
response
@@ -101,8 +111,8 @@ def uninstall
101111
response = @client.send_request(
102112
method: 'POST',
103113
body: JSON.dump(payload),
104-
path: named_users_path('/uninstall'),
105-
content_type: 'application/json'
114+
path: named_users_path('uninstall'),
115+
content_type: CONTENT_TYPE
106116
)
107117
logger.info { "Uninstalled named_user_ids #{@named_user_ids} " }
108118
response

lib/urbanairship/version.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Urbanairship
2-
VERSION = '7.0.0'
2+
VERSION = '8.0.0'
33
end

spec/lib/urbanairship/common_spec.rb

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
require 'urbanairship'
33

44
describe Urbanairship::Common do
5+
it 'defines CONTENT_TYPE' do
6+
expect(described_class::CONTENT_TYPE).to eq 'application/json'
7+
end
8+
59
it 'has a PUSH_URL' do
610
expect(Urbanairship.push_path).not_to be nil
711
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
require 'spec_helper'
2+
require 'urbanairship'
3+
require 'urbanairship/devices/attributes'
4+
5+
describe Urbanairship::Devices::Attributes do
6+
let(:key_1) { 'first_name' }
7+
let(:value_1) { 'Airship' }
8+
let(:key_2) { 'last_name' }
9+
let(:value_2) { 'API' }
10+
let(:timestamp) { Time.now.utc - 3600 }
11+
12+
let(:attributes) do
13+
[
14+
{ key: key_1, value: value_1, action: described_class::SET, timestamp: timestamp },
15+
]
16+
end
17+
let(:expected) do
18+
{
19+
attributes: [
20+
{
21+
key: key_1,
22+
value: value_1,
23+
action: described_class::SET,
24+
timestamp: timestamp.iso8601,
25+
},
26+
],
27+
}
28+
end
29+
30+
before { Timecop.freeze(Time.now.utc) }
31+
after { Timecop.return }
32+
33+
describe 'Payload' do
34+
let(:payload) { described_class.new(attributes).payload }
35+
36+
context 'Action type is not set' do
37+
let(:attributes) do
38+
[
39+
{ key: key_1, value: value_1, timestamp: timestamp },
40+
]
41+
end
42+
43+
it 'defaults to `set` action' do
44+
puts timestamp.iso8601
45+
expect(payload).to eq expected
46+
end
47+
end
48+
49+
describe 'Set action' do
50+
it 'defaults to `set` action' do
51+
expect(payload).to eq expected
52+
end
53+
end
54+
55+
context 'Timestamp not provided' do
56+
let(:timestamp) { nil }
57+
let(:expected) do
58+
{
59+
attributes: [
60+
{
61+
key: key_1,
62+
value: value_1,
63+
action: described_class::SET,
64+
timestamp: Time.now.utc.iso8601,
65+
},
66+
],
67+
}
68+
end
69+
70+
it 'uses expected provided timestamp' do
71+
expect(payload).to eq expected
72+
end
73+
end
74+
75+
describe 'Remove action' do
76+
let(:attributes) do
77+
[
78+
{ key: key_1, action: described_class::REMOVE, timestamp: timestamp }
79+
]
80+
end
81+
let(:expected) do
82+
{
83+
attributes: [
84+
{
85+
key: key_1,
86+
action: described_class::REMOVE,
87+
timestamp: timestamp.iso8601,
88+
},
89+
],
90+
}
91+
end
92+
93+
it 'generates expected payload' do
94+
expect(payload).to eq expected
95+
end
96+
end
97+
98+
describe 'Loops over multiple records' do
99+
let(:attributes) do
100+
[
101+
{ key: key_1, action: described_class::REMOVE },
102+
{ key: key_2, value: value_2 }
103+
]
104+
end
105+
let(:expected) do
106+
{
107+
attributes: [
108+
{
109+
key: key_1,
110+
action: described_class::REMOVE,
111+
timestamp: Time.now.utc.iso8601,
112+
},
113+
{
114+
key: key_2,
115+
value: value_2,
116+
action: described_class::SET,
117+
timestamp: Time.now.utc.iso8601,
118+
},
119+
],
120+
}
121+
end
122+
123+
it 'generates expected payload' do
124+
expect(payload).to eq expected
125+
end
126+
end
127+
end
128+
end

0 commit comments

Comments
 (0)