urbanairship 5.6.0 → 6.0.0

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +30 -0
  3. data/README.rst +1 -0
  4. data/docs/ab_tests.rst +162 -0
  5. data/docs/attributes.rst +52 -0
  6. data/docs/automations.rst +212 -0
  7. data/docs/index.rst +3 -0
  8. data/docs/push.rst +24 -0
  9. data/docs/sms.rst +19 -0
  10. data/docs/static_lists.rst +2 -2
  11. data/lib/urbanairship.rb +12 -0
  12. data/lib/urbanairship/ab_tests/ab_test.rb +88 -0
  13. data/lib/urbanairship/ab_tests/experiment.rb +45 -0
  14. data/lib/urbanairship/ab_tests/variant.rb +34 -0
  15. data/lib/urbanairship/automations/automation.rb +105 -0
  16. data/lib/urbanairship/automations/pipeline.rb +52 -0
  17. data/lib/urbanairship/client.rb +22 -9
  18. data/lib/urbanairship/common.rb +63 -16
  19. data/lib/urbanairship/configuration.rb +2 -1
  20. data/lib/urbanairship/custom_events/custom_event.rb +60 -0
  21. data/lib/urbanairship/custom_events/payload.rb +89 -0
  22. data/lib/urbanairship/devices/attribute.rb +54 -0
  23. data/lib/urbanairship/devices/channel_tags.rb +1 -1
  24. data/lib/urbanairship/devices/channel_uninstall.rb +10 -10
  25. data/lib/urbanairship/devices/create_and_send.rb +4 -4
  26. data/lib/urbanairship/devices/devicelist.rb +28 -7
  27. data/lib/urbanairship/devices/email.rb +5 -5
  28. data/lib/urbanairship/devices/email_notification.rb +11 -4
  29. data/lib/urbanairship/devices/named_user.rb +6 -6
  30. data/lib/urbanairship/devices/open_channel.rb +22 -23
  31. data/lib/urbanairship/devices/segment.rb +5 -5
  32. data/lib/urbanairship/devices/sms.rb +40 -9
  33. data/lib/urbanairship/devices/static_lists.rb +12 -12
  34. data/lib/urbanairship/push/location.rb +7 -7
  35. data/lib/urbanairship/push/push.rb +23 -13
  36. data/lib/urbanairship/push/schedule.rb +9 -0
  37. data/lib/urbanairship/reports/response_statistics.rb +9 -9
  38. data/lib/urbanairship/version.rb +1 -1
  39. metadata +16 -5
@@ -13,10 +13,12 @@ module Urbanairship
13
13
  #
14
14
  # @param [Object] key Application Key
15
15
  # @param [Object] secret Application Secret
16
+ # @param [String] token Application Auth Token (for custom events endpoint)
16
17
  # @return [Object] Client
17
- def initialize(key: required('key'), secret: required('secret'))
18
+ def initialize(key: required('key'), secret: required('secret'), token: nil)
18
19
  @key = key
19
20
  @secret = secret
21
+ @token = token
20
22
  end
21
23
 
22
24
  # Send a request to Airship's API
@@ -25,10 +27,11 @@ module Urbanairship
25
27
  # @param [Object] body Request Body
26
28
  # @param [Object] url Request URL
27
29
  # @param [Object] content_type Content-Type
28
- # @param [Object] version API Version
30
+ # @param [Object] encoding Encoding
31
+ # @param [Symbol] auth_type (:basic|:bearer)
29
32
  # @return [Object] Push Response
30
33
  def send_request(method: required('method'), url: required('url'), body: nil,
31
- content_type: nil, encoding: nil)
34
+ content_type: nil, encoding: nil, auth_type: :basic)
32
35
  req_type = case method
33
36
  when 'GET'
34
37
  :get
@@ -46,9 +49,14 @@ module Urbanairship
46
49
  headers['Accept'] = 'application/vnd.urbanairship+json; version=3'
47
50
  headers['Content-type'] = content_type unless content_type.nil?
48
51
  headers['Content-Encoding'] = encoding unless encoding.nil?
52
+
53
+ if auth_type == :bearer
54
+ raise ArgumentError.new('token must be provided as argument if auth_type=bearer') if @token.nil?
55
+ headers['X-UA-Appkey'] = @key
56
+ headers['Authorization'] = "Bearer #{@token}"
57
+ end
49
58
 
50
- debug = "Making #{method} request to #{url}.\n"+
51
- "\tHeaders:\n"
59
+ debug = "Making #{method} request to #{url}.\n"+ "\tHeaders:\n"
52
60
  debug += "\t\tcontent-type: #{content_type}\n" unless content_type.nil?
53
61
  debug += "\t\tcontent-encoding: gzip\n" unless encoding.nil?
54
62
  debug += "\t\taccept: application/vnd.urbanairship+json; version=3\n"
@@ -56,15 +64,20 @@ module Urbanairship
56
64
 
57
65
  logger.debug(debug)
58
66
 
59
- response = RestClient::Request.execute(
67
+ params = {
60
68
  method: method,
61
69
  url: url,
62
70
  headers: headers,
63
- user: @key,
64
- password: @secret,
65
71
  payload: body,
66
72
  timeout: Urbanairship.configuration.timeout
67
- )
73
+ }
74
+
75
+ if auth_type == :basic
76
+ params[:user] = @key
77
+ params[:password] = @secret
78
+ end
79
+
80
+ response = RestClient::Request.execute(params)
68
81
 
69
82
  logger.debug("Received #{response.code} response. Headers:\n\t#{response.headers}\nBody:\n\t#{response.body}")
70
83
  Response.check_code(response.code, response)
@@ -4,22 +4,69 @@ 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/'
7
+ def url_for(path)
8
+ "https://#{Urbanairship.configuration.server}/api#{path}"
9
+ end
10
+
11
+ def apid_url(path='')
12
+ url_for("/apids/#{path}")
13
+ end
14
+
15
+ def channel_url(path='')
16
+ url_for("/channels/#{path}")
17
+ end
18
+
19
+ def create_and_send_url(path='')
20
+ url_for("/create-and-send/#{path}")
21
+ end
22
+
23
+ def custom_events_url(path='')
24
+ url_for("/custom-events/#{path}")
25
+ end
26
+
27
+ def device_token_url(path='')
28
+ url_for("/device_tokens/#{path}")
29
+ end
30
+
31
+ def experiments_url(path='')
32
+ url_for("/experiments/#{path}")
33
+ end
34
+
35
+ def lists_url(path='')
36
+ url_for("/lists/#{path}")
37
+ end
38
+
39
+ def location_url(path='')
40
+ url_for("/location/#{path}")
41
+ end
42
+
43
+ def named_users_url(path='')
44
+ url_for("/named_users/#{path}")
45
+ end
46
+
47
+ def open_channel_url(path='')
48
+ channel_url("/open/#{path}")
49
+ end
50
+
51
+ def pipelines_url(path='')
52
+ url_for("/pipelines/#{path}")
53
+ end
54
+
55
+ def push_url(path='')
56
+ url_for("/push/#{path}")
57
+ end
58
+
59
+ def reports_url(path='')
60
+ url_for("/reports/#{path}")
61
+ end
62
+
63
+ def schedules_url(path='')
64
+ url_for("/schedules/#{path}")
65
+ end
66
+
67
+ def segments_url(path='')
68
+ url_for("/segments/#{path}")
69
+ end
23
70
 
24
71
  # Helper method for required keyword args in Ruby 2.0 that is compatible with 2.1+
25
72
  # @example
@@ -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
+ url: custom_events_url
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
@@ -0,0 +1,89 @@
1
+ require 'urbanairship'
2
+ require 'urbanairship/common'
3
+
4
+ module Urbanairship
5
+ module CustomEvents
6
+ module Payload
7
+ include Urbanairship::Common
8
+
9
+ def custom_events(
10
+ body: required('body'),
11
+ occurred: required('occurred'),
12
+ user: required('user')
13
+ )
14
+ compact_helper({
15
+ body: body,
16
+ occurred: format_timestamp(occurred),
17
+ user: user
18
+ })
19
+ end
20
+
21
+ # Body specific portion of CustomEvent Object
22
+ def custom_events_body(
23
+ interaction_id: nil, interaction_type: nil, name: required('name'),
24
+ properties: nil, session_id: nil, transaction: nil, value: nil
25
+ )
26
+
27
+ validates_name_format(name)
28
+ validates_value_format(value)
29
+
30
+ compact_helper({
31
+ interaction_id: interaction_id,
32
+ interaction_type: interaction_type,
33
+ name: name,
34
+ properties: properties,
35
+ session_id: session_id,
36
+ transaction: transaction,
37
+ value: value
38
+ })
39
+ end
40
+
41
+ # User specific portion of CustomEvent Object
42
+ def custom_events_user(
43
+ amazon_channel: nil, android_channel: nil, channel: nil,
44
+ ios_channel: nil, named_user_id: nil, web_channel: nil
45
+ )
46
+ res = compact_helper({
47
+ amazon_channel: amazon_channel,
48
+ android_channel: android_channel,
49
+ channel: channel,
50
+ ios_channel: ios_channel,
51
+ named_user_id: named_user_id,
52
+ web_channel: web_channel,
53
+ })
54
+
55
+ fail ArgumentError, 'at least one user identifier must be defined' if res.empty?
56
+
57
+ res
58
+ end
59
+
60
+
61
+ # Formatters
62
+ # ------------------------------------------------------------------------
63
+
64
+ def format_timestamp(timestamp)
65
+ return timestamp if timestamp.is_a?(String)
66
+
67
+ timestamp.strftime('%Y-%m-%dT%H:%M:%S')
68
+ end
69
+
70
+
71
+ # Validators
72
+ # ------------------------------------------------------------------------
73
+
74
+ NAME_REGEX = /^[a-z0-9_\-]+$/
75
+ def validates_name_format(name)
76
+ return if name =~ NAME_REGEX
77
+
78
+ fail ArgumentError, 'invalid "name": it must follows this pattern /^[a-z0-9_\-]+$/'
79
+ end
80
+
81
+ def validates_value_format(value)
82
+ return if value.nil?
83
+ return if value.is_a?(Numeric)
84
+
85
+ fail ArgumentError, 'invalid "value": must be a number'
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,54 @@
1
+ require 'urbanairship'
2
+
3
+ module Urbanairship
4
+ module Devices
5
+ class Attribute
6
+ include Urbanairship::Common
7
+ include Urbanairship::Loggable
8
+ attr_accessor :attribute,
9
+ :operator,
10
+ :precision,
11
+ :value
12
+
13
+ def initialize(client: required('client'))
14
+ @client = client
15
+ end
16
+
17
+ def payload
18
+ if precision
19
+ date_attribute
20
+ elsif value.is_a? String
21
+ text_attribute
22
+ elsif value.is_a? Integer
23
+ number_attribute
24
+ end
25
+ end
26
+
27
+ def number_attribute
28
+ {
29
+ 'attribute': attribute,
30
+ 'operator': operator,
31
+ 'value': value
32
+ }
33
+ end
34
+
35
+ def text_attribute
36
+ {
37
+ 'attribute': attribute,
38
+ 'operator': operator,
39
+ 'value': value
40
+ }
41
+ end
42
+
43
+ def date_attribute
44
+ {
45
+ 'attribute': attribute,
46
+ 'operator': operator,
47
+ 'precision': precision,
48
+ 'value': value
49
+ }
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -15,7 +15,7 @@ module Urbanairship
15
15
  @add_group = {}
16
16
  @remove_group = {}
17
17
  @set_group = {}
18
- @url = CHANNEL_URL + 'tags/'
18
+ @url = channel_url('tags/')
19
19
  end
20
20
 
21
21
  def set_audience(ios: nil, android: nil, amazon: nil)
@@ -23,7 +23,7 @@ module Urbanairship
23
23
  response = @client.send_request(
24
24
  method: 'POST',
25
25
  body: JSON.dump(channels),
26
- url: CHANNEL_URL + 'uninstall/',
26
+ url: channel_url('uninstall/'),
27
27
  content_type: 'application/json'
28
28
  )
29
29
 
@@ -31,33 +31,33 @@ module Urbanairship
31
31
  response
32
32
  end
33
33
  end
34
-
35
-
34
+
35
+
36
36
  class OpenChannelUninstall
37
37
  include Urbanairship::Common
38
38
  include Urbanairship::Loggable
39
39
  attr_reader :client
40
-
40
+
41
41
  def initialize(client: required('client'))
42
42
  @client = client
43
43
  end
44
-
44
+
45
45
  def uninstall(address: required('address'),
46
46
  open_platform: required('open_platform'))
47
-
47
+
48
48
  body = {
49
49
  address: address,
50
50
  open_platform_name: open_platform
51
51
  }
52
-
52
+
53
53
  response = @client.send_request(
54
54
  method: 'POST',
55
55
  body: JSON.dump(body),
56
- url: OPEN_CHANNEL_URL + 'uninstall/',
56
+ url: open_channel_url('uninstall/'),
57
57
  content_type: 'application/json'
58
58
  )
59
-
60
- logger.info { "Successfully unintalled open channel with address: #{address}"}
59
+
60
+ logger.info { "Successfully uninstalled open channel with address: #{address}"}
61
61
  response
62
62
  end
63
63
  end
@@ -44,7 +44,7 @@ module Urbanairship
44
44
  campaign_object = {'categories': campaigns}
45
45
  full_payload[:campaigns] = campaign_object
46
46
  end
47
-
47
+
48
48
  full_payload
49
49
  end
50
50
 
@@ -52,7 +52,7 @@ module Urbanairship
52
52
  response = @client.send_request(
53
53
  method: 'POST',
54
54
  body: JSON.dump(payload),
55
- url: CREATE_AND_SEND_URL,
55
+ url: create_and_send_url,
56
56
  content_type: 'application/json'
57
57
  )
58
58
  logger.info("Running create and send for addresses #{@addresses}")
@@ -63,7 +63,7 @@ module Urbanairship
63
63
  response = @client.send_request(
64
64
  method: 'POST',
65
65
  body: JSON.dump(payload),
66
- url: CREATE_AND_SEND_URL + 'validate',
66
+ url: create_and_send_url('validate'),
67
67
  content_type: 'application/json'
68
68
  )
69
69
  logger.info("Validating payload for create and send")
@@ -84,7 +84,7 @@ module Urbanairship
84
84
  response = @client.send_request(
85
85
  method: 'POST',
86
86
  body: JSON.dump(scheduled_payload),
87
- url: SCHEDULES_URL + 'create-and-send',
87
+ url: schedules_url('create-and-send'),
88
88
  content_type: 'application/json'
89
89
  )
90
90
  logger.info("Scheduling create and send operation with name #{@name}")