urbanairship 5.7.0 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +2 -2
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG +33 -0
  5. data/README.rst +95 -38
  6. data/docs/attributes.rst +73 -0
  7. data/docs/index.rst +3 -0
  8. data/docs/named_user.rst +22 -0
  9. data/docs/push.rst +24 -0
  10. data/docs/sms.rst +19 -0
  11. data/docs/static_lists.rst +2 -2
  12. data/lib/urbanairship.rb +7 -0
  13. data/lib/urbanairship/ab_tests/ab_test.rb +8 -9
  14. data/lib/urbanairship/automations/automation.rb +11 -11
  15. data/lib/urbanairship/client.rb +34 -12
  16. data/lib/urbanairship/common.rb +110 -43
  17. data/lib/urbanairship/configuration.rb +2 -1
  18. data/lib/urbanairship/custom_events/custom_event.rb +60 -0
  19. data/lib/urbanairship/custom_events/payload.rb +89 -0
  20. data/lib/urbanairship/devices/attribute.rb +54 -0
  21. data/lib/urbanairship/devices/attributes.rb +53 -0
  22. data/lib/urbanairship/devices/channel_tags.rb +2 -2
  23. data/lib/urbanairship/devices/channel_uninstall.rb +10 -10
  24. data/lib/urbanairship/devices/create_and_send.rb +4 -4
  25. data/lib/urbanairship/devices/devicelist.rb +28 -7
  26. data/lib/urbanairship/devices/email.rb +5 -5
  27. data/lib/urbanairship/devices/named_user.rb +22 -12
  28. data/lib/urbanairship/devices/open_channel.rb +22 -23
  29. data/lib/urbanairship/devices/segment.rb +6 -8
  30. data/lib/urbanairship/devices/sms.rb +40 -9
  31. data/lib/urbanairship/devices/static_lists.rb +12 -12
  32. data/lib/urbanairship/push/location.rb +18 -18
  33. data/lib/urbanairship/push/push.rb +5 -5
  34. data/lib/urbanairship/push/schedule.rb +9 -0
  35. data/lib/urbanairship/reports/response_statistics.rb +42 -31
  36. data/lib/urbanairship/version.rb +1 -1
  37. data/urbanairship.gemspec +1 -0
  38. metadata +25 -6
@@ -11,7 +11,7 @@ module Urbanairship
11
11
  :offset,
12
12
  :experiment_object,
13
13
  :experiment_id
14
-
14
+
15
15
  def initialize(client: required('client'))
16
16
  @client = client
17
17
  end
@@ -19,7 +19,7 @@ module Urbanairship
19
19
  def list_ab_test
20
20
  response = @client.send_request(
21
21
  method: 'GET',
22
- url: EXPERIMENTS_URL + format_url_with_params
22
+ path: experiments_path(format_url_with_params)
23
23
  )
24
24
  logger.info("Looking up A/B Tests for project")
25
25
  response
@@ -29,7 +29,7 @@ module Urbanairship
29
29
  response = @client.send_request(
30
30
  method: 'POST',
31
31
  body: JSON.dump(experiment_object),
32
- url: EXPERIMENTS_URL,
32
+ path: experiments_path,
33
33
  content_type: 'application/json'
34
34
  )
35
35
  logger.info("Created A/B Test")
@@ -39,7 +39,7 @@ module Urbanairship
39
39
  def list_scheduled_ab_test
40
40
  response = @client.send_request(
41
41
  method: 'GET',
42
- url: EXPERIMENTS_URL + 'scheduled' + format_url_with_params
42
+ path: experiments_path('scheduled' + format_url_with_params)
43
43
  )
44
44
  logger.info("Looking up scheduled A/B Tests for project")
45
45
  response
@@ -49,7 +49,7 @@ module Urbanairship
49
49
  fail ArgumentError, 'experiment_id must be set to delete individual A/B test' if @experiment_id.nil?
50
50
  response = @client.send_request(
51
51
  method: 'DELETE',
52
- url: EXPERIMENTS_URL + 'scheduled/' + experiment_id
52
+ path: experiments_path('scheduled/' + experiment_id)
53
53
  )
54
54
  logger.info("Deleting A/B test with ID #{experiment_id}")
55
55
  response
@@ -59,7 +59,7 @@ module Urbanairship
59
59
  response = @client.send_request(
60
60
  method: 'POST',
61
61
  body: JSON.dump(experiment_object),
62
- url: EXPERIMENTS_URL + 'validate',
62
+ path: experiments_path('validate'),
63
63
  content_type: 'application/json'
64
64
  )
65
65
  logger.info("Validating A/B Test")
@@ -70,7 +70,7 @@ module Urbanairship
70
70
  fail ArgumentError, 'experiment_id must be set to lookup individual A/B Test' if @experiment_id.nil?
71
71
  response = @client.send_request(
72
72
  method: 'GET',
73
- url: EXPERIMENTS_URL + experiment_id
73
+ path: experiments_path(experiment_id)
74
74
  )
75
75
  logger.info("Looking up A/B test with ID #{experiment_id}")
76
76
  response
@@ -85,5 +85,4 @@ module Urbanairship
85
85
  end
86
86
  end
87
87
  end
88
- end
89
-
88
+ end
@@ -22,7 +22,7 @@ module Urbanairship
22
22
  response = @client.send_request(
23
23
  method: 'POST',
24
24
  body: JSON.dump(pipeline_object),
25
- url: PIPELINES_URL,
25
+ path: pipelines_path,
26
26
  content_type: 'application/json'
27
27
  )
28
28
  logger.info("Created Automation")
@@ -32,7 +32,7 @@ module Urbanairship
32
32
  def list_automations
33
33
  response = @client.send_request(
34
34
  method: 'GET',
35
- url: PIPELINES_URL + format_url_with_params
35
+ path: pipelines_path(format_url_with_params)
36
36
  )
37
37
  logger.info("Looking up automations for project")
38
38
  response
@@ -41,7 +41,7 @@ module Urbanairship
41
41
  def list_deleted_automations
42
42
  response = @client.send_request(
43
43
  method: 'GET',
44
- url: PIPELINES_URL + 'deleted' + format_url_with_params
44
+ path: pipelines_path('deleted' + format_url_with_params)
45
45
  )
46
46
  logger.info("Looking up deleted automations for project")
47
47
  response
@@ -51,18 +51,18 @@ module Urbanairship
51
51
  response = @client.send_request(
52
52
  method: 'POST',
53
53
  body: JSON.dump(pipeline_object),
54
- url: PIPELINES_URL + 'validate',
54
+ path: pipelines_path('validate'),
55
55
  content_type: 'application/json'
56
56
  )
57
57
  logger.info("Validating Automation")
58
58
  response
59
59
  end
60
60
 
61
- def lookup_automation
61
+ def lookup_automation
62
62
  fail ArgumentError, 'pipeline_id must be set to lookup individual automation' if @pipeline_id.nil?
63
63
  response = @client.send_request(
64
64
  method: 'GET',
65
- url: PIPELINES_URL + pipeline_id
65
+ path: pipelines_path(pipeline_id)
66
66
  )
67
67
  logger.info("Looking up automation with id #{pipeline_id}")
68
68
  response
@@ -70,22 +70,22 @@ module Urbanairship
70
70
 
71
71
  def update_automation
72
72
  fail ArgumentError, 'pipeline_id must be set to update individual automation' if @pipeline_id.nil?
73
-
73
+
74
74
  response = @client.send_request(
75
75
  method: 'PUT',
76
76
  body: JSON.dump(pipeline_object),
77
- url: PIPELINES_URL + pipeline_id,
77
+ path: pipelines_path(pipeline_id),
78
78
  content_type: 'application/json'
79
79
  )
80
80
  logger.info("Validating Automation")
81
81
  response
82
82
  end
83
83
 
84
- def delete_automation
84
+ def delete_automation
85
85
  fail ArgumentError, 'pipeline_id must be set to delete individual automation' if @pipeline_id.nil?
86
86
  response = @client.send_request(
87
87
  method: 'DELETE',
88
- url: PIPELINES_URL + pipeline_id
88
+ path: pipelines_path(pipeline_id)
89
89
  )
90
90
  logger.info("Deleting automation with id #{pipeline_id}")
91
91
  response
@@ -102,4 +102,4 @@ module Urbanairship
102
102
  end
103
103
  end
104
104
  end
105
- end
105
+ end
@@ -13,22 +13,30 @@ module Urbanairship
13
13
  #
14
14
  # @param [Object] key Application Key
15
15
  # @param [Object] secret Application Secret
16
+ # @param [String] server Airship server to use ("go.airship.eu" or "go.urbanairship.com").
17
+ # Used only when the request is sent with a "path", not an "url".
18
+ # @param [String] token Application Auth Token (for custom events endpoint)
16
19
  # @return [Object] Client
17
- def initialize(key: required('key'), secret: required('secret'))
20
+ def initialize(key: required('key'), secret: required('secret'),
21
+ server: Urbanairship.configuration.server, token: nil)
18
22
  @key = key
19
23
  @secret = secret
24
+ @server = server
25
+ @token = token
20
26
  end
21
27
 
22
28
  # Send a request to Airship's API
23
29
  #
24
30
  # @param [Object] method HTTP Method
25
31
  # @param [Object] body Request Body
32
+ # @param [Object] path Request path
26
33
  # @param [Object] url Request URL
27
34
  # @param [Object] content_type Content-Type
28
- # @param [Object] version API Version
35
+ # @param [Object] encoding Encoding
36
+ # @param [Symbol] auth_type (:basic|:bearer)
29
37
  # @return [Object] Push Response
30
- def send_request(method: required('method'), url: required('url'), body: nil,
31
- content_type: nil, encoding: nil)
38
+ def send_request(method: required('method'), path: nil, url: nil, body: nil,
39
+ content_type: nil, encoding: nil, auth_type: :basic)
32
40
  req_type = case method
33
41
  when 'GET'
34
42
  :get
@@ -42,13 +50,22 @@ module Urbanairship
42
50
  fail 'Method was not "GET" "POST" "PUT" or "DELETE"'
43
51
  end
44
52
 
45
- headers = {'User-agent' => 'UARubyLib/' + Urbanairship::VERSION}
53
+ raise ArgumentError.new("path and url can't be both nil") if path.nil? && url.nil?
54
+
55
+ headers = {'User-Agent' => 'UARubyLib/' + Urbanairship::VERSION}
46
56
  headers['Accept'] = 'application/vnd.urbanairship+json; version=3'
47
- headers['Content-type'] = content_type unless content_type.nil?
57
+ headers['Content-Type'] = content_type unless content_type.nil?
48
58
  headers['Content-Encoding'] = encoding unless encoding.nil?
49
59
 
50
- debug = "Making #{method} request to #{url}.\n"+
51
- "\tHeaders:\n"
60
+ if auth_type == :bearer
61
+ raise ArgumentError.new('token must be provided as argument if auth_type=bearer') if @token.nil?
62
+ headers['X-UA-Appkey'] = @key
63
+ headers['Authorization'] = "Bearer #{@token}"
64
+ end
65
+
66
+ url = "https://#{@server}/api#{path}" unless path.nil?
67
+
68
+ debug = "Making #{method} request to #{url}.\n"+ "\tHeaders:\n"
52
69
  debug += "\t\tcontent-type: #{content_type}\n" unless content_type.nil?
53
70
  debug += "\t\tcontent-encoding: gzip\n" unless encoding.nil?
54
71
  debug += "\t\taccept: application/vnd.urbanairship+json; version=3\n"
@@ -56,15 +73,20 @@ module Urbanairship
56
73
 
57
74
  logger.debug(debug)
58
75
 
59
- response = RestClient::Request.execute(
76
+ params = {
60
77
  method: method,
61
78
  url: url,
62
79
  headers: headers,
63
- user: @key,
64
- password: @secret,
65
80
  payload: body,
66
81
  timeout: Urbanairship.configuration.timeout
67
- )
82
+ }
83
+
84
+ if auth_type == :basic
85
+ params[:user] = @key
86
+ params[:password] = @secret
87
+ end
88
+
89
+ response = RestClient::Request.execute(params)
68
90
 
69
91
  logger.debug("Received #{response.code} response. Headers:\n\t#{response.headers}\nBody:\n\t#{response.body}")
70
92
  Response.check_code(response.code, response)
@@ -4,23 +4,67 @@ require 'urbanairship/loggable'
4
4
  module Urbanairship
5
5
  # Features mixed in to all classes
6
6
  module Common
7
- SERVER = 'go.urbanairship.com'
8
- BASE_URL = 'https://go.urbanairship.com/api'
9
- CHANNEL_URL = BASE_URL + '/channels/'
10
- OPEN_CHANNEL_URL = BASE_URL + '/channels/open/'
11
- DEVICE_TOKEN_URL = BASE_URL + '/device_tokens/'
12
- APID_URL = BASE_URL + '/apids/'
13
- PUSH_URL = BASE_URL + '/push/'
14
- SCHEDULES_URL = BASE_URL + '/schedules/'
15
- SEGMENTS_URL = BASE_URL + '/segments/'
16
- NAMED_USER_URL = BASE_URL + '/named_users/'
17
- REPORTS_URL = BASE_URL + '/reports/'
18
- LISTS_URL = BASE_URL + '/lists/'
19
- PIPELINES_URL = BASE_URL + '/pipelines/'
20
- FEEDS_URL = BASE_URL + '/feeds/'
21
- LOCATION_URL = BASE_URL + '/location/'
22
- CREATE_AND_SEND_URL = BASE_URL + '/create-and-send/'
23
- EXPERIMENTS_URL = BASE_URL + '/experiments/'
7
+ CONTENT_TYPE = 'application/json'
8
+
9
+ def apid_path(path='')
10
+ "/apids/#{path}"
11
+ end
12
+
13
+ def channel_path(path='')
14
+ "/channels/#{path}"
15
+ end
16
+
17
+ def create_and_send_path(path='')
18
+ "/create-and-send/#{path}"
19
+ end
20
+
21
+ def custom_events_path(path='')
22
+ "/custom-events/#{path}"
23
+ end
24
+
25
+ def device_token_path(path='')
26
+ "/device_tokens/#{path}"
27
+ end
28
+
29
+ def experiments_path(path='')
30
+ "/experiments/#{path}"
31
+ end
32
+
33
+ def lists_path(path='')
34
+ "/lists/#{path}"
35
+ end
36
+
37
+ def location_path(path='')
38
+ "/location/#{path}"
39
+ end
40
+
41
+ def named_users_path(path='')
42
+ "/named_users/#{path}"
43
+ end
44
+
45
+ def open_channel_path(path='')
46
+ "/open/#{path}"
47
+ end
48
+
49
+ def pipelines_path(path='')
50
+ "/pipelines/#{path}"
51
+ end
52
+
53
+ def push_path(path='')
54
+ "/push/#{path}"
55
+ end
56
+
57
+ def reports_path(path='')
58
+ "/reports/#{path}"
59
+ end
60
+
61
+ def schedules_path(path='')
62
+ "/schedules/#{path}"
63
+ end
64
+
65
+ def segments_path(path='')
66
+ "/segments/#{path}"
67
+ end
24
68
 
25
69
  # Helper method for required keyword args in Ruby 2.0 that is compatible with 2.1+
26
70
  # @example
@@ -115,36 +159,18 @@ module Urbanairship
115
159
 
116
160
  def initialize(client: required('client'))
117
161
  @client = client
118
- @next_page = nil
119
- @data_list = nil
120
- @data_attribute = nil
121
162
  @count = 0
122
- end
123
-
124
- def load_page
125
- return false unless @next_page
126
- response = @client.send_request(
127
- method: 'GET',
128
- url: @next_page
129
- )
130
- logger.info("Retrieving data from: #{@next_page}")
131
- check_next_page = response['body']['next_page']
132
- if check_next_page != @next_page
133
- @next_page = check_next_page
134
- elsif check_next_page
135
- # if check_page = next_page, we have repeats in the response.
136
- # and we don't want to load them
137
- return false
138
- else
139
- @next_page = nil
140
- end
141
- @data_list = response['body'][@data_attribute]
142
- true
163
+ @data_attribute = nil
164
+ @data_list = nil
165
+ @next_page_path = nil
166
+ @next_page_url = nil
143
167
  end
144
168
 
145
169
  def each
146
- while load_page
147
- @data_list.each do | value |
170
+ while @next_page_path || @next_page_url
171
+ load_page
172
+
173
+ @data_list.each do |value|
148
174
  @count += 1
149
175
  yield value
150
176
  end
@@ -154,6 +180,47 @@ module Urbanairship
154
180
  def count
155
181
  @count
156
182
  end
183
+
184
+ private
185
+
186
+ def load_page
187
+ logger.info("Retrieving data from: #{@next_page_url || @next_page_path}")
188
+ params = {
189
+ method: 'GET',
190
+ path: @next_page_path,
191
+ url: @next_page_url
192
+ }.select { |k, v| !v.nil? }
193
+ response = @client.send_request(params)
194
+
195
+ @data_list = get_new_data(response)
196
+ @next_page_url = get_next_page_url(response)
197
+ @next_page_path = nil
198
+ end
199
+
200
+ def extract_next_page_url(response)
201
+ response['body']['next_page']
202
+ end
203
+
204
+ def get_new_data(response)
205
+ potential_next_page_url = extract_next_page_url(response)
206
+
207
+ # if potential_next_page_url is the same as the current page, we have
208
+ # repeats in the response and we don't want to load them
209
+ return [] if potential_next_page_url && get_next_page_url(response).nil?
210
+
211
+ response['body'][@data_attribute]
212
+ end
213
+
214
+ def get_next_page_url(response)
215
+ potential_next_page_url = extract_next_page_url(response)
216
+ return nil if potential_next_page_url.nil?
217
+
218
+ # if potential_next_page_url is the same as the current page, we have
219
+ # repeats in the response and we don't want to check the next pages
220
+ return potential_next_page_url if @next_page_url && potential_next_page_url != @next_page_url
221
+ return potential_next_page_url if @next_page_path && !potential_next_page_url.end_with?(@next_page_path)
222
+ nil
223
+ end
157
224
  end
158
225
  end
159
226
  end
@@ -1,8 +1,9 @@
1
1
  module Urbanairship
2
2
  class Configuration
3
- attr_accessor :custom_logger, :log_path, :log_level, :timeout
3
+ attr_accessor :custom_logger, :log_path, :log_level, :server, :timeout
4
4
 
5
5
  def initialize
6
+ @server = 'go.urbanairship.com'
6
7
  @custom_logger = nil
7
8
  @log_path = nil
8
9
  @log_level = Logger::INFO
@@ -0,0 +1,60 @@
1
+ require 'uri'
2
+ require 'urbanairship'
3
+ require 'urbanairship/common'
4
+ require 'urbanairship/loggable'
5
+
6
+ module Urbanairship
7
+ module CustomEvents
8
+ class CustomEvent
9
+ include Urbanairship::Common
10
+ include Urbanairship::Loggable
11
+
12
+ attr_accessor :events
13
+
14
+ def initialize(client: required('client'))
15
+ @client = client
16
+ end
17
+
18
+ def create
19
+ fail ArgumentError, 'events must be an array of custom events' unless events.is_a?(Array)
20
+
21
+ response = @client.send_request(
22
+ auth_type: :bearer,
23
+ body: JSON.dump(events),
24
+ content_type: 'application/json',
25
+ method: 'POST',
26
+ path: custom_events_path
27
+ )
28
+ cer = CustomEventResponse.new(body: response['body'], code: response['code'])
29
+ logger.info { cer.format }
30
+
31
+ cer
32
+ end
33
+ end
34
+
35
+ # Response to a successful custom event creation.
36
+ class CustomEventResponse
37
+ attr_reader :ok, :operation_id, :payload, :status_code
38
+
39
+ def initialize(body: nil, code: nil)
40
+ @payload = (body.nil? || body.empty?) ? {} : body
41
+ @ok = payload['ok']
42
+ @operation_id = payload['operationId']
43
+ @status_code = code
44
+ end
45
+
46
+ # String Formatting of the CustomEventResponse
47
+ #
48
+ # @return [Object] String Formatted CustomEventResponse
49
+ def format
50
+ "Received [#{status_code}] response code.\nBody:\n#{formatted_body}"
51
+ end
52
+
53
+ def formatted_body
54
+ payload
55
+ .map { |key, value| "#{key}:\t#{value.to_s || 'None'}" }
56
+ .join("\n")
57
+ end
58
+ end
59
+ end
60
+ end