diff --git a/.gitignore b/.gitignore index da32bd1..a4d3cd7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ /gems_dev /.rbx .*.swp +.idea/ +temp/ +.rvmrc diff --git a/Gemfile b/Gemfile index aa02545..610eb4b 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ source 'http://rubygems.org' gem 'activemodel', '~> 3.0' gem 'activesupport', '~> 3.0' gem 'guid', '~> 0.1' -gem 'savon', '~> 0.9' +gem 'savon', '~> 2.1.0' group :rake do gem 'simple_gem', :require => 'tasks/simple_gem' @@ -12,4 +12,3 @@ end group :test do gem 'rspec', '~> 2.8' end - diff --git a/Gemfile.lock b/Gemfile.lock index ddf4a08..754eb99 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,48 +1,50 @@ GEM remote: http://rubygems.org/ specs: - activemodel (3.2.1) - activesupport (= 3.2.1) + activemodel (3.2.6) + activesupport (= 3.2.6) builder (~> 3.0.0) - activesupport (3.2.1) + activesupport (3.2.6) i18n (~> 0.6) multi_json (~> 1.0) - akami (1.0.0) + akami (1.2.0) gyoku (>= 0.4.0) - builder (3.0.0) + nokogiri (>= 1.4.0) + builder (3.0.4) diff-lcs (1.1.3) guid (0.1.1) - gyoku (0.4.4) + gyoku (1.0.0) builder (>= 2.1.2) - httpi (0.9.5) + httpi (2.0.2) rack i18n (0.6.0) - multi_json (1.0.4) - nokogiri (1.5.0) - nori (1.0.2) - rack (1.4.1) + multi_json (1.3.6) + nokogiri (1.5.6) + nori (2.0.3) + rack (1.5.2) rake (0.9.2.2) - rspec (2.8.0) - rspec-core (~> 2.8.0) - rspec-expectations (~> 2.8.0) - rspec-mocks (~> 2.8.0) - rspec-core (2.8.0) - rspec-expectations (2.8.0) - diff-lcs (~> 1.1.2) - rspec-mocks (2.8.0) - savon (0.9.7) - akami (~> 1.0) + rspec (2.11.0) + rspec-core (~> 2.11.0) + rspec-expectations (~> 2.11.0) + rspec-mocks (~> 2.11.0) + rspec-core (2.11.0) + rspec-expectations (2.11.1) + diff-lcs (~> 1.1.3) + rspec-mocks (2.11.0) + savon (2.1.0) + akami (~> 1.2.0) builder (>= 2.1.2) - gyoku (>= 0.4.0) - httpi (~> 0.9) + gyoku (~> 1.0.0) + httpi (~> 2.0.2) nokogiri (>= 1.4.0) - nori (~> 1.0) - wasabi (~> 2.0) + nori (~> 2.0.3) + wasabi (~> 3.0.0) simple_gem (0.0.2) activesupport (~> 3.0) rake (>= 0.8.7) rspec (~> 2.8) - wasabi (2.0.0) + wasabi (3.0.0) + httpi (~> 2.0) nokogiri (>= 1.4.0) PLATFORMS @@ -53,5 +55,5 @@ DEPENDENCIES activesupport (~> 3.0) guid (~> 0.1) rspec (~> 2.8) - savon (~> 0.9) + savon (~> 2.1.0) simple_gem diff --git a/exact_target_sdk.gemspec b/exact_target_sdk.gemspec index 0cb54c0..44bc6d1 100644 --- a/exact_target_sdk.gemspec +++ b/exact_target_sdk.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.email = %q{daws23@gmail.com} s.homepage = %q{https://github.com/daws/exact_target_sdk} s.require_paths = [ 'lib' ] - + # documentation s.has_rdoc = true s.extra_rdoc_files = %w( README.rdoc CHANGELOG.rdoc LICENSE.txt ) @@ -27,6 +27,6 @@ Gem::Specification.new do |s| s.add_dependency 'activemodel', '~> 3.0' s.add_dependency 'activesupport', '~> 3.0' s.add_dependency 'guid', '~> 0.1' - s.add_dependency 'savon', '~> 0.9' + s.add_dependency 'savon', '~> 2.1.0' end diff --git a/lib/exact_target_sdk.rb b/lib/exact_target_sdk.rb index 8443c8e..fbd5c48 100644 --- a/lib/exact_target_sdk.rb +++ b/lib/exact_target_sdk.rb @@ -4,27 +4,73 @@ module ExactTargetSDK autoload :APIObject, 'exact_target_sdk/api_object' + autoload :AsyncResponse, 'exact_target_sdk/async_response' + autoload :AsyncResponseType, 'exact_target_sdk/async_response_type' autoload :APIProperty, 'exact_target_sdk/api_property' autoload :Attribute, 'exact_target_sdk/attribute' + autoload :Base, 'exact_target_sdk/base' + autoload :BounceEvent, 'exact_target_sdk/bounce_event' autoload :Client, 'exact_target_sdk/client' + autoload :ClickEvent, 'exact_target_sdk/click_event' autoload :ComplexFilterPart, 'exact_target_sdk/complex_filter_part' autoload :ContentArea, 'exact_target_sdk/content_area' + autoload :ContentValidation, 'exact_target_sdk/content_validation' + autoload :CreateOptions, 'exact_target_sdk/create_options' autoload :CreateResponse, 'exact_target_sdk/create_response' autoload :CreateResult, 'exact_target_sdk/create_result' + autoload :DailyRecurrence, 'exact_target_sdk/daily_recurrence' + autoload :DailyRecurrencePatternTypeEnum, 'exact_target_sdk/daily_recurrence_pattern_type_enum' autoload :DataExtension, 'exact_target_sdk/data_extension' autoload :DataExtensionField, 'exact_target_sdk/data_extension_field' + autoload :DataExtensionFieldType, 'exact_target_sdk/data_extension_field_type' autoload :DataExtensionObject, 'exact_target_sdk/data_extension_object' + autoload :DeleteOptions, 'exact_target_sdk/delete_options' autoload :DeleteResponse, 'exact_target_sdk/delete_response' autoload :DeleteResult, 'exact_target_sdk/delete_result' + autoload :DeliveryProfile, 'exact_target_sdk/delivery_profile' + autoload :DeliveryProfileSourceAddressTypeEnum, 'exact_target_sdk/delivery_profile_source_address_type_enum' + autoload :Email, 'exact_target_sdk/email' + autoload :EmailSendDefinition, 'exact_target_sdk/email_send_definition' autoload :FilterPart, 'exact_target_sdk/filter_part' autoload :PerformResponse, 'exact_target_sdk/perform_response' + autoload :PerformResult, 'exact_target_sdk/perform_result' + autoload :List, 'exact_target_sdk/list' + autoload :LogicalOperators, 'exact_target_sdk/logical_operators' + autoload :OpenEvent, 'exact_target_sdk/open_event' + autoload :Options, 'exact_target_sdk/options' + autoload :Priority, 'exact_target_sdk/priority' + autoload :PerformOptions, 'exact_target_sdk/perform_options' + autoload :RecurrenceRangeTypeEnum, 'exact_target_sdk/recurrence_range_type_enum' + autoload :RecurrenceTypeEnum, 'exact_target_sdk/recurrence_type_enum' autoload :Result, 'exact_target_sdk/result' + autoload :ResultMessage, 'exact_target_sdk/result_message' + autoload :RespondWhen, 'exact_target_sdk/respond_when' autoload :RetrieveResponse, 'exact_target_sdk/retrieve_response' + autoload :RetrieveOptions, 'exact_target_sdk/retrieve_options' autoload :RetrieveResult, 'exact_target_sdk/retrieve_result' + autoload :RequestType, 'exact_target_sdk/request_type' + autoload :SalutationSourceEnum, 'exact_target_sdk/salutation_source_enum' + autoload :SaveAction, 'exact_target_sdk/save_action' + autoload :SaveOption, 'exact_target_sdk/save_option' autoload :SimpleFilterPart, 'exact_target_sdk/simple_filter_part' + autoload :Send, 'exact_target_sdk/send' + autoload :ScheduleDefinition, 'exact_target_sdk/schedule_definition' + autoload :ScheduleOptions, 'exact_target_sdk/schedule_options' + autoload :ScheduleResponse, 'exact_target_sdk/schedule_response' + autoload :ScheduleResult, 'exact_target_sdk/schedule_result' + autoload :SendClassification, 'exact_target_sdk/send_classification' + autoload :SendDefinitionList, 'exact_target_sdk/send_definition_list' + autoload :SendDefinitionListTypeEnum, 'exact_target_sdk/send_definition_list_enum' + autoload :SenderProfile, 'exact_target_sdk/sender_profile' + autoload :SentEvent, 'exact_target_sdk/sent_event' + autoload :SimpleOperator, 'exact_target_sdk/simple_operator' + autoload :SpamAssassinValidation, 'exact_target_sdk/spam_assassin_validation' autoload :Subscriber, 'exact_target_sdk/subscriber' + autoload :SubscriberList, 'exact_target_sdk/subscriber_list' autoload :TriggeredSend, 'exact_target_sdk/triggered_send' autoload :TriggeredSendDefinition, 'exact_target_sdk/triggered_send_definition' + autoload :ValidationAction, 'exact_target_sdk/validation_action' + autoload :UpdateOptions, 'exact_target_sdk/update_options' autoload :UpdateResponse, 'exact_target_sdk/update_response' autoload :UpdateResult, 'exact_target_sdk/update_result' diff --git a/lib/exact_target_sdk/api_object.rb b/lib/exact_target_sdk/api_object.rb index 4cfa462..ebff4c8 100644 --- a/lib/exact_target_sdk/api_object.rb +++ b/lib/exact_target_sdk/api_object.rb @@ -1,163 +1,14 @@ -require 'active_model' -require 'active_support/inflector' - module ExactTargetSDK # Parent class of all ExactTarget API objects (listed here: # http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects). Provides # class-level declarations, validation, and rendering that makes modeling # each object easy. -class APIObject - - include ::ActiveModel::Validations - include ::ActiveModel::Validations::Callbacks - - class << self - - # Declares a property of this object, optionally requiring it upon - # validation. - # - # Provides a getter and setter for this property, keeping track of - # whether or not it has been set and registering it for rendering. - def property(name, options = {}) - options = { - :required => false - }.merge(options) - - name = name.to_s - attr_reader name.to_sym - class_eval <<-__EOF__ - def #{name}=(value) - @_set_#{name} = true - @#{name} = value - end - __EOF__ - if options[:required] - validates name.to_sym, :presence => true - end - register_property!(name, options) - end - - # Declares a property as an array of values. - # - # Provides a getter and setter for this property. The getter will - # always return an array (not null), so the client may simply append - # to this property. - # - # Note that once the property has been either read or written to, it - # will be rendered. - def array_property(name, options = {}) - # TODO: type validation would be nice - name = name.to_s - - options = { - :nest_children => false, - :singular => name.singularize - }.merge(options) - - class_eval <<-__EOF__ - def #{name} - @_set_#{name} = true - @#{name} ||= [] - end - def #{name}=(value) - @_set_#{name} = true - @#{name} = value - end - __EOF__ - register_property!(name, options) - end - - # Same as #property, adding validation the the provided value is an - # integer. - def int_property(name, options = {}) - options = { - :required => false - }.merge(options) - property(name, options) - validates name.to_sym, :numericality => { :allow_nil => true, :only_integer => true } - end - - # Returns an array of all registered properties. - def properties - @properties || {} - end - - def type_name - name.split('::').last - end - - private - - # Stores the given property name to be used at render time. - def register_property!(name, options = {}) - @properties ||= {} - @properties[name] = options - end + class APIObject < Base - end - - # By default, any properties may be passed and set. - # - # May be overridden. - def initialize(properties = {}) - properties.each do |key, value| - self.send "#{key}=", value - end - end - - # By default, returns the name of the class. - # - # May be overridden. - def type_name - self.class.type_name - end - - # By default, runs validation and executes #render_properties!. - # - # If overridden, the child class should check wehter or not the - # object is valid, and then render the object. In general, - # the render_properties! method should be overridden instead. - def render!(xml) - raise(InvalidAPIObject, self) if invalid? - render_properties!(xml) - end + property 'ID' + property 'ObjectID' + property 'CustomerKey' - # By default, loops through all registered properties, and renders - # each that has been explicitly set. - # - # May be overridden. - def render_properties!(xml) - self.class.properties.each do |property, options| - next unless instance_variable_get("@_set_#{property}") - property_value = self.send(property) - render_property!(property, property_value, xml, options) - end end - - def render_property_array!(property_name, array, xml) - array.each do |current| - render_property!(property_name, current, xml) - end - end - - def render_property!(property_name, property_value, xml, options = {}) - if property_value.is_a?(APIObject) - xml.__send__(property_name, { "xsi:type" => property_value.type_name } ) do - property_value.render!(xml) - end - elsif property_value.is_a?(Array) - if options[:nest_children] - xml.__send__(property_name) do - render_property_array!(options[:singular], property_value, xml) - end - else - render_property_array!(property_name, property_value, xml) - end - else - xml.__send__(property_name, property_value.to_s) - end - end - -end end diff --git a/lib/exact_target_sdk/async_response.rb b/lib/exact_target_sdk/async_response.rb new file mode 100644 index 0000000..eed93bd --- /dev/null +++ b/lib/exact_target_sdk/async_response.rb @@ -0,0 +1,24 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/AsyncResponse +module ExactTargetSDK +class AsyncResponse < Base + + # Indicates whether the APIObject should be included in the response. + property 'IncludeObjects' + + # Determines whether the Result objects will be included in the response when an asynchronous API call completes processing. + property 'IncludeResults' + + # Reduce object to base APIObject information. Includes basic reference data associating object and request. + property 'OnlyIncludeBase' + + # Specifies event triggers the AsyncResponse object action. + property 'RespondWhen' + + # Email address or public URL to receive POST response to asynchronous request. + property 'ResponseAddress' + + # Specifies type of response associated with an asynchronous operation. + property 'ResponseType' + +end +end diff --git a/lib/exact_target_sdk/async_response_type.rb b/lib/exact_target_sdk/async_response_type.rb new file mode 100644 index 0000000..3a36904 --- /dev/null +++ b/lib/exact_target_sdk/async_response_type.rb @@ -0,0 +1,17 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/AsyncResponseType +module ExactTargetSDK +module AsyncResponseType + # Default email address for object. Indicates if subscriber information can be used for email sends. + EMAIL = "email" + + # Reserved for future use. + FTP = "FTP" + + # Indicates the response will be sent using the Post method of the HTTP protocol to the URL specified in the call when the asynchronous API call completes processing. + + HTTP_POST = "HTTPPost" + + #Default response type for AsyncResponseType. + NONE = "None" +end +end diff --git a/lib/exact_target_sdk/base.rb b/lib/exact_target_sdk/base.rb new file mode 100644 index 0000000..4d69537 --- /dev/null +++ b/lib/exact_target_sdk/base.rb @@ -0,0 +1,177 @@ +require 'active_model' +require 'active_support/inflector' + +module ExactTargetSDK + + class Base + # Properties that can be used as object id to perform update, delete, + # perform and schedule actions + ID_PROPERTIES = %w(ID ObjectID CustomerKey) + + include ::ActiveModel::Validations + include ::ActiveModel::Validations::Callbacks + + class << self + + # Declares a property of this object, optionally requiring it upon + # validation. + # + # Provides a getter and setter for this property, keeping track of + # whether or not it has been set and registering it for rendering. + def property(name, options = {}) + options = { + :required => false + }.merge(options) + + name = name.to_s + attr_reader name.to_sym + class_eval <<-__EOF__ + def #{name}=(value) + @_set_#{name} = true + @#{name} = value + end + __EOF__ + if options[:required] + validates name.to_sym, :presence => true, :if => :new_record? + end + register_property!(name, options) + end + + # Declares a property as an array of values. + # + # Provides a getter and setter for this property. The getter will + # always return an array (not null), so the client may simply append + # to this property. + # + # Note that once the property has been either read or written to, it + # will be rendered. + def array_property(name, options = {}) + # TODO: type validation would be nice + name = name.to_s + + options = { + :nest_children => false, + :singular => name.singularize + }.merge(options) + + class_eval <<-__EOF__ + def #{name} + @_set_#{name} = true + @#{name} ||= [] + end + def #{name}=(value) + @_set_#{name} = true + @#{name} = value + end + __EOF__ + register_property!(name, options) + end + + # Same as #property, adding validation the the provided value is an + # integer. + def int_property(name, options = {}) + options = { + :required => false + }.merge(options) + property(name, options) + validates name.to_sym, :numericality => {:allow_nil => true, :only_integer => true}, :if => :new_record? + end + + # Returns an array of all registered properties of current class. + def properties + @properties || {} + end + + # Returns an array of all registered properties, including parent classes + def all_properties + properties = {} + self.ancestors.each do |klass| + properties.merge!(klass.properties) if klass.respond_to? :properties + end + properties + end + + def type_name + name.split('::').last + end + + private + + # Stores the given property name to be used at render time. + def register_property!(name, options = {}) + @properties ||= {} + @properties[name] = options + end + + end + + # By default, any properties may be passed and set. + # + # May be overridden. + def initialize(properties = {}) + properties.each do |key, value| + self.send "#{key}=", value + end + end + + # By default, returns the name of the class. + # + # May be overridden. + def type_name + self.class.type_name + end + + # By default, runs validation and executes #render_properties!. + # + # If overridden, the child class should check wehter or not the + # object is valid, and then render the object. In general, + # the render_properties! method should be overridden instead. + def render!(xml) + raise(InvalidAPIObject, self) if invalid? + render_properties!(xml) + end + + # By default, loops through all registered properties, and renders + # each that has been explicitly set. + # + # May be overridden. + def render_properties!(xml) + self.class.all_properties.each do |property, options| + next unless instance_variable_get("@_set_#{property}") + property_value = self.send(property) + render_property!(property, property_value, xml, options) + end + end + + def render_property_array!(property_name, array, xml) + array.each do |current| + render_property!(property_name, current, xml) + end + end + + def render_property!(property_name, property_value, xml, options = {}) + if property_value.is_a?(APIObject) + xml.__send__(property_name, {"xsi:type" => property_value.type_name}) do + property_value.render!(xml) + end + elsif property_value.is_a?(Array) + if options[:nest_children] + xml.__send__(property_name) do + render_property_array!(options[:singular], property_value, xml) + end + else + render_property_array!(property_name, property_value, xml) + end + else + xml.__send__(property_name, property_value.to_s) + end + end + + def new_record? + ID_PROPERTIES.each {|property| return false if instance_variable_get("@_set_#{property}")} + true + end + + end + +end \ No newline at end of file diff --git a/lib/exact_target_sdk/bounce_event.rb b/lib/exact_target_sdk/bounce_event.rb new file mode 100644 index 0000000..eb2fe41 --- /dev/null +++ b/lib/exact_target_sdk/bounce_event.rb @@ -0,0 +1,25 @@ +module ExactTargetSDK +class BounceEvent < APIObject + + # Date when a tracking event occurred. + property "EventDate" + + # Contains identifier for a specific send. + property "SendID" + + # Contains SMTP code related to a bounced email. + property "SMTPCode" + + # Contains SMTP reason associated with a bounced email. + property "SMTPReason" + + # Identification of a specific subscriber. + property "SubscriberKey" + + # Identifies the triggered send definition associated with an event. + # This value also appears in tracking events to allow you to tie those events to a specific triggered send. + property "TriggeredSendDefinitionObjectID " + + +end +end diff --git a/lib/exact_target_sdk/click_event.rb b/lib/exact_target_sdk/click_event.rb new file mode 100644 index 0000000..75fde29 --- /dev/null +++ b/lib/exact_target_sdk/click_event.rb @@ -0,0 +1,14 @@ +module ExactTargetSDK +class ClickEvent < APIObject + + # Date when a tracking event occurred. + property 'EventDate' + + # Contains identifier for a specific send. + property 'SendID' + + # Identification of a specific subscriber. + property 'SubscriberKey' + +end +end diff --git a/lib/exact_target_sdk/client.rb b/lib/exact_target_sdk/client.rb index 07b7e88..80af881 100644 --- a/lib/exact_target_sdk/client.rb +++ b/lib/exact_target_sdk/client.rb @@ -11,6 +11,7 @@ module ExactTargetSDK # outlined in the guide linked above are used. This is done in an attempt to be # as transparent as possible, so that the API may be used by referring only to # the guide linked above. + class Client # Constructs a client. @@ -21,13 +22,7 @@ class Client # Since ExactTarget's API is stateless, constructing a client object will not # make any remote calls. def initialize(options = {}) - self.config = { - }.merge!(ExactTargetSDK.config).merge!(options) - - Savon.configure do |c| - c.logger = config[:logger] - c.raise_errors = false - end + self.config = {}.merge!(ExactTargetSDK.config).merge!(options) initialize_client! end @@ -47,17 +42,23 @@ def initialize(options = {}) def Create(*args) # TODO: implement and accept CreateOptions - api_objects = args + api_objects, options = filter_options(args) response = execute_request 'Create' do |xml| xml.CreateRequest do - xml.Options # TODO: support CreateOptions api_objects.each do |api_object| xml.Objects "xsi:type" => api_object.type_name do api_object.render!(xml) end end + + options.each do |option| + xml.Options do + option.render!(xml) + end + end + end end @@ -80,12 +81,14 @@ def Create(*args) # InvalidAPIObject if any of the provided objects don't pass validation # # Returns a RetrieveResponse object. - def Retrieve(object_type_name, filter, *properties) + def Retrieve(object_type_name, filter, *args) object_type_name = object_type_name.type_name if object_type_name.respond_to?(:type_name) + + properties, options = filter_options(args) + response = execute_request 'Retrieve' do |xml| xml.RetrieveRequestMsg do xml.RetrieveRequest do - xml.Options xml.ObjectType object_type_name @@ -96,6 +99,13 @@ def Retrieve(object_type_name, filter, *properties) xml.Filter "xsi:type" => filter.type_name do filter.render!(xml) end + + options.each do |option| + xml.Options do + option.render!(xml) + end + end + end end end @@ -116,19 +126,24 @@ def Retrieve(object_type_name, filter, *properties) # # Returns an UpdateResponse object. def Update(*args) - # TODO: implement and accept UpdateOptions - api_objects = args + api_objects, options = filter_options(args) response = execute_request 'Update' do |xml| xml.UpdateRequest do - xml.Options # TODO: support UpdateOptions api_objects.each do |api_object| xml.Objects "xsi:type" => api_object.type_name do api_object.render!(xml) end end + + options.each do |option| + xml.Options do + option.render!(xml) + end + end + end end @@ -148,19 +163,23 @@ def Update(*args) # # Returns a DeleteResponse object. def Delete(*args) - # TODO: implement and accept DeleteOptions - api_objects = args + api_objects, options = filter_options(args) response = execute_request 'Delete' do |xml| xml.DeleteRequest do - xml.Options # TODO: support DeleteOptions - api_objects.each do |api_object| xml.Objects "xsi:type" => api_object.type_name do api_object.render!(xml) end end + + options.each do |option| + xml.Options do + option.render!(xml) + end + end + end end @@ -180,9 +199,7 @@ def Delete(*args) # # Returns a PerformResponse object. def Perform(action, *args) - # TODO: implement and accept PerformOptions - - definitions = args + definitions, options = filter_options(args) response = execute_request 'Perform' do |xml| xml.PerformRequestMsg do @@ -194,31 +211,83 @@ def Perform(action, *args) definition.render!(xml) end end - end + end - xml.Options # TODO: support PerformOptions + options.each do |option| + xml.Options do + option.render!(xml) + end + end end end PerformResponse.new(response) end + + # Invokes the Schedule method. + # + # The provided arguments should each be definitions that are sub-classes + # of APIObject. + # + # Possible exceptions are: + # HTTPError if an HTTP error (such as a timeout) occurs + # SOAPFault if a SOAP fault occurs + # Timeout if there is a timeout waiting for the response + # InvalidAPIObject if any of the provided objects don't pass validation + # + # Returns a ScheduleResponse object. + def Schedule(action, schedule, *args) + + interactions, options = filter_options(args) + + response = execute_request 'Schedule' do |xml| + xml.ScheduleRequestMsg do + xml.Action action + + xml.Schedule do + schedule.render!(xml) + end + + xml.Interactions do + interactions.each do |interaction| + xml.Interaction "xsi:type" => interaction.type_name do + interaction.render!(xml) + end + end + end + + options.each do |option| + xml.Options do + option.render!(xml) + end + end + + end + end + + ScheduleResponse.new(response) + end + + def logger - config[:logger] + ExactTargetSDK.config[:logger] end private attr_accessor :config, :client - + # Constructs and saves the savon client using provided config. def initialize_client! - self.client = ::Savon::Client.new do - wsdl.endpoint = config[:endpoint] - wsdl.namespace = config[:namespace] - http.open_timeout = config[:open_timeout] - http.read_timeout = config[:read_timeout] - end + self.client = ::Savon::Client.new({ + wsdl: ExactTargetSDK.config[:endpoint], + namespace: ExactTargetSDK.config[:namespace], + logger: ExactTargetSDK.config[:logger], + raise_errors: false, + open_timeout: ExactTargetSDK.config[:open_timeout], + read_timeout: ExactTargetSDK.config[:read_timeout] + }) end # Builds the SOAP request for the given method, delegating body @@ -229,11 +298,10 @@ def initialize_client! # # Returns the raw savon response. def execute_request(method) - begin response = client.request(method) do soap.xml do |xml| xml.s :Envelope, - "xmlns" => config[:namespace], + "xmlns" => ExactTargetSDK.config[:namespace], "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema", "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xmlns:s" => "http://www.w3.org/2003/05/soap-envelope", @@ -246,11 +314,11 @@ def execute_request(method) xml.a :ReplyTo do xml.a :Address, "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" end - xml.a :To, config[:endpoint], "s:mustUnderstand" => "1" + xml.a :To, ExactTargetSDK.config[:endpoint], "s:mustUnderstand" => "1" xml.o :Security, "s:mustUnderstand" => "1" do xml.o :UsernameToken, "o:Id" => "test" do - xml.o :Username, config[:username] - xml.o :Password, config[:password] + xml.o :Username, ExactTargetSDK.config[:username] + xml.o :Password, ExactTargetSDK.config[:password] end end end @@ -271,13 +339,23 @@ def execute_request(method) end response - rescue ::Timeout::Error => e - timeout = ::ExactTargetSDK::TimeoutError.new("#{e.message}; open_timeout: #{config[:open_timeout]}; read_timeout: #{config[:read_timeout]}") - timeout.set_backtrace(e.backtrace) - raise timeout - rescue Exception => e - raise ::ExactTargetSDK::UnknownError, e - end + rescue ::Timeout::Error => e + timeout = ::ExactTargetSDK::TimeoutError.new("#{e.message}; open_timeout: #{config[:open_timeout]}; read_timeout: #{config[:read_timeout]}") + timeout.set_backtrace(e.backtrace) + raise timeout + end + + def filter_options(args) + options = find_options(args) + options.each{|opt| args.delete(opt)} + [args,options] + end + + + def find_options(args) + options = [] + args.each { |arg| options << arg if arg.is_a? Options } + options end end diff --git a/lib/exact_target_sdk/content_validation.rb b/lib/exact_target_sdk/content_validation.rb new file mode 100644 index 0000000..5ec7e38 --- /dev/null +++ b/lib/exact_target_sdk/content_validation.rb @@ -0,0 +1,14 @@ +module ExactTargetSDK +class ContentValidation < APIObject + + # Default email address for object. Indicates if subscriber information can be used for email sends. + property 'Email' + + # Specifies the validation action to run on content. Valid values include SpamAssassinValidation. + property 'ValidationAction' + + # Indicates subscribers associated with an object. + array_property 'Subscribers', :nest_children => true + +end +end diff --git a/lib/exact_target_sdk/create_options.rb b/lib/exact_target_sdk/create_options.rb new file mode 100644 index 0000000..528959b --- /dev/null +++ b/lib/exact_target_sdk/create_options.rb @@ -0,0 +1,6 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/CreateOptions +module ExactTargetSDK +class CreateOptions < Options + +end +end diff --git a/lib/exact_target_sdk/daily_recurrence.rb b/lib/exact_target_sdk/daily_recurrence.rb new file mode 100644 index 0000000..6820e95 --- /dev/null +++ b/lib/exact_target_sdk/daily_recurrence.rb @@ -0,0 +1,10 @@ +module ExactTargetSDK +class DailyRecurrence < APIObject + + # Specifies the type of daily recurrence (either Interval or EveryWeekDay) + property "DailyRecurrencePatternType" + + # Specifies the number of days on which to execute a daily recurrence. + property "DayInterval" +end +end diff --git a/lib/exact_target_sdk/daily_recurrence_pattern_type_enum.rb b/lib/exact_target_sdk/daily_recurrence_pattern_type_enum.rb new file mode 100644 index 0000000..d864721 --- /dev/null +++ b/lib/exact_target_sdk/daily_recurrence_pattern_type_enum.rb @@ -0,0 +1,6 @@ +module ExactTargetSDK +module DailyRecurrencePatternTypeEnum + EVERY_WEEK_DAY = "EveryWeekDay" + INTERVAL = "Interval" +end +end diff --git a/lib/exact_target_sdk/data_extension.rb b/lib/exact_target_sdk/data_extension.rb index 087b43e..52be515 100644 --- a/lib/exact_target_sdk/data_extension.rb +++ b/lib/exact_target_sdk/data_extension.rb @@ -5,5 +5,21 @@ class DataExtension < APIObject array_property 'Fields', :nest_children => true property 'Name' + # Indicates whether you can use a data extension as part of an audience for a message send. + property 'IsSendable' + + # Indicates whether a sendable data extension can be used within tests sends for a message. + property 'IsTestable' + + # Indicates the field within a sendable data extension to use as an address as part of a send. + # Possible values include SubscriberID, CustomerKey, or EmailAddress. + # The application uses this field to establish a data relationship between a value specified by the + # SendableSubscriberField property and a value within a sendable data extension. + property 'SendableDataExtensionField' + + # Indicates field to use as sending address. The application uses this field to establish a data relationship + # between a subscriber field and a value specified by the SendableDataExtentionField property. + property 'SendableSubscriberField' + end end diff --git a/lib/exact_target_sdk/data_extension_field_type.rb b/lib/exact_target_sdk/data_extension_field_type.rb new file mode 100644 index 0000000..5efb3db --- /dev/null +++ b/lib/exact_target_sdk/data_extension_field_type.rb @@ -0,0 +1,14 @@ +module ExactTargetSDK +module DataExtensionFieldType + + TEXT = "Text" + NUMBER = "Number" + DATA = "Data" + BOOLEAN = "Boolean" + EMAIL_ADDRESS = "EmailAddress" + PHONE = "Phone" + DECIMAL = "Decimal" + LOCALE = "Locale" + +end +end diff --git a/lib/exact_target_sdk/data_extension_object.rb b/lib/exact_target_sdk/data_extension_object.rb index ce22d2b..ffd3d43 100644 --- a/lib/exact_target_sdk/data_extension_object.rb +++ b/lib/exact_target_sdk/data_extension_object.rb @@ -2,7 +2,9 @@ module ExactTargetSDK class DataExtensionObject < APIObject property 'CustomerKey' + property 'SubscriberKey' array_property 'Properties', :nest_children => true + array_property 'Keys', :nest_children => true end end diff --git a/lib/exact_target_sdk/delete_options.rb b/lib/exact_target_sdk/delete_options.rb new file mode 100644 index 0000000..84abc05 --- /dev/null +++ b/lib/exact_target_sdk/delete_options.rb @@ -0,0 +1,6 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/DeleteOptions +module ExactTargetSDK +class DeleteOptions < Options + +end +end diff --git a/lib/exact_target_sdk/delivery_profile.rb b/lib/exact_target_sdk/delivery_profile.rb new file mode 100644 index 0000000..dce0f47 --- /dev/null +++ b/lib/exact_target_sdk/delivery_profile.rb @@ -0,0 +1,15 @@ +module ExactTargetSDK +class DeliveryProfile < APIObject + + property 'Name', :required => true + + property 'SourceAddressType' + + # Defines source of a footer salutation to use as part of a delivery profile or send definition (Default, Content Library, or None) + property 'FooterSalutationSource' + + # Defines source of header salutation for a delivery profile or send definition. + property 'HeaderSalutationSource' + +end +end diff --git a/lib/exact_target_sdk/delivery_profile_source_address_type_enum.rb b/lib/exact_target_sdk/delivery_profile_source_address_type_enum.rb new file mode 100644 index 0000000..0ffb913 --- /dev/null +++ b/lib/exact_target_sdk/delivery_profile_source_address_type_enum.rb @@ -0,0 +1,6 @@ +module ExactTargetSDK +module DeliveryProfileSourceAddressTypeEnum + CUSTOM_PRIVATE_IP_ADDRESS = "CustomPrivateIPAddress" + DEFAULT_PRIVATE_IP_ADDRESS = "DefaultPrivateIPAddress" +end +end diff --git a/lib/exact_target_sdk/email.rb b/lib/exact_target_sdk/email.rb new file mode 100644 index 0000000..7353333 --- /dev/null +++ b/lib/exact_target_sdk/email.rb @@ -0,0 +1,21 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/Email +module ExactTargetSDK +class Email < APIObject + + # Name of the object or property. + property 'Name', :required => true + + # Contains subject area information for a message. + property 'Subject', :required => true + + # Contains HTML body of an email message. + property 'HTMLBody' + + # Contains raw text body of a message. + property 'TextBody' + + # Indicates if email message was created via pasted HTML. + property 'IsHTMLPaste' + +end +end diff --git a/lib/exact_target_sdk/email_send_definition.rb b/lib/exact_target_sdk/email_send_definition.rb new file mode 100644 index 0000000..decccc4 --- /dev/null +++ b/lib/exact_target_sdk/email_send_definition.rb @@ -0,0 +1,51 @@ +module ExactTargetSDK +class EmailSendDefinition < APIObject + + property 'Name', :required => true + + # Indicates the send classification to use as part of a send definition. + property 'SendClassification' + + # Indicates the subscriber list to use as part of an email send definition. + array_property 'SendDefinitionList' + + # Default email address for object. Indicates if subscriber information can be used for email sends. + property 'Email' + + # Defines an email address to which to send a test message as part + # of an email send definition. Use the Test action when sending a + # test email to an email send definition. + property 'TestEmailAddr' + + # Defines account users with access to tracking information for that send definition. + property 'TrackingUsers' + + # Defines blind carbon copy email address to which to send a message as + # part of an email send definition. + property 'AutoBccEmail' + + # Indicates email addresses to receive blind carbon copy of a message. + property 'BccEmail' + + # CC + property 'CCEmail' + + # Defines scheduled data and time for a send related to an email send definition. + property 'DeliveryScheduledTime' + + # Subject for an email send + property 'EmailSubject' + + # Defines an email address to which to send a test message as part of an email send definition. + # Use the Test action when sending a test email to an email send definition. + property "TestEmailAddr" + + #Identifies the sender profile included in the send classificiation. + property 'SenderProfile' + + +end +end + + + diff --git a/lib/exact_target_sdk/list.rb b/lib/exact_target_sdk/list.rb new file mode 100644 index 0000000..95bf88c --- /dev/null +++ b/lib/exact_target_sdk/list.rb @@ -0,0 +1,8 @@ +module ExactTargetSDK +class List < APIObject + + property 'ListName', :required => true + array_property 'Subscribers' + +end +end diff --git a/lib/exact_target_sdk/logical_operators.rb b/lib/exact_target_sdk/logical_operators.rb new file mode 100644 index 0000000..759d810 --- /dev/null +++ b/lib/exact_target_sdk/logical_operators.rb @@ -0,0 +1,6 @@ +module ExactTargetSDK +module LogicalOperators + AND = "AND" + OR = "OR" +end +end diff --git a/lib/exact_target_sdk/open_event.rb b/lib/exact_target_sdk/open_event.rb new file mode 100644 index 0000000..9fd618f --- /dev/null +++ b/lib/exact_target_sdk/open_event.rb @@ -0,0 +1,14 @@ +module ExactTargetSDK +class OpenEvent < APIObject + + # Date when a tracking event occurred. + property 'EventDate' + + # Contains identifier for a specific send. + property 'SendID' + + # Identification of a specific subscriber. + property 'SubscriberKey' + +end +end diff --git a/lib/exact_target_sdk/options.rb b/lib/exact_target_sdk/options.rb new file mode 100644 index 0000000..0ba626f --- /dev/null +++ b/lib/exact_target_sdk/options.rb @@ -0,0 +1,33 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/Options +module ExactTargetSDK +class Options < Base + + # Represents the number of calls that must be received before the conversation support in the asynchronous API will begin processing. + int_property "CallsInConversation" + + # Specifies the account ownership and context of an object. + property "Client" + + # Unique ID of initial async API call; All requests that should be processed as a single unit will have the same ConversationID. + property "ConversationID" + + # Defines the priority for a triggered send. Valid values include Low, Medium, and High. + property "Priority" + + # Defines request as synchronous or asynchronous API. + property "RequestType" + + # Keeps requests in asynchronous queue until time specified in the call. + property "ScheduledTime" + + # Specifies the processing sequence of a multi-step conversation. This optional property requires the use of ConversationID. + property "SequenceCode" + + # Allows upsert on selected objects. + array_property "SaveOptions" + + # Defines how responses are returned and under what conditions. Optional. + array_property "SendResponseTo" + +end +end diff --git a/lib/exact_target_sdk/perform_options.rb b/lib/exact_target_sdk/perform_options.rb new file mode 100644 index 0000000..6daae3e --- /dev/null +++ b/lib/exact_target_sdk/perform_options.rb @@ -0,0 +1,6 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/PerformOptions +module ExactTargetSDK +class PerformOptions < Options + +end +end diff --git a/lib/exact_target_sdk/perform_response.rb b/lib/exact_target_sdk/perform_response.rb index 460698e..d6e53db 100644 --- a/lib/exact_target_sdk/perform_response.rb +++ b/lib/exact_target_sdk/perform_response.rb @@ -1,13 +1,25 @@ module ExactTargetSDK class PerformResponse - attr_reader :OverallStatus, :OverallStatusMessage, :RequestID + attr_reader :OverallStatus, :OverallStatusMessage, :RequestID, :Results def initialize(response) response = response.to_hash[:perform_response_msg] @OverallStatus = response[:overall_status] @OverallStatusMessage = response[:overall_status_message] @RequestID = response[:request_id] + @Results = [] + + results = if response[:results].is_a? Array + response[:results] + elsif response[:results].is_a? Hash + [response[:results]] + else + [] + end + results.each do |result| + @Results << PerformResult.new(result) + end end end diff --git a/lib/exact_target_sdk/perform_result.rb b/lib/exact_target_sdk/perform_result.rb new file mode 100644 index 0000000..035dffe --- /dev/null +++ b/lib/exact_target_sdk/perform_result.rb @@ -0,0 +1,4 @@ +module ExactTargetSDK +class PerformResult < Result +end +end diff --git a/lib/exact_target_sdk/priority.rb b/lib/exact_target_sdk/priority.rb new file mode 100644 index 0000000..a98bf22 --- /dev/null +++ b/lib/exact_target_sdk/priority.rb @@ -0,0 +1,13 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/Priority +module ExactTargetSDK +module Priority + # Specifies high priority for API process. + HIGH = "High" + + # Specifies medium priority on API processes. + MEDIUM = "Medium" + + # Specifies low priority for API process. + LOW = "Low" +end +end diff --git a/lib/exact_target_sdk/recurrence_range_type_enum.rb b/lib/exact_target_sdk/recurrence_range_type_enum.rb new file mode 100644 index 0000000..f1742db --- /dev/null +++ b/lib/exact_target_sdk/recurrence_range_type_enum.rb @@ -0,0 +1,6 @@ +module ExactTargetSDK +module RecurrenceRangeTypeEnum + END_AFTER = "EndAfter" + END_ON = "EndOn" +end +end diff --git a/lib/exact_target_sdk/recurrence_type_enum.rb b/lib/exact_target_sdk/recurrence_type_enum.rb new file mode 100644 index 0000000..c22069b --- /dev/null +++ b/lib/exact_target_sdk/recurrence_type_enum.rb @@ -0,0 +1,11 @@ +module ExactTargetSDK +module RecurrenceTypeEnum + DAILY = "Daily" + HOURLY = "Hourly" + MINUTELY = "Minutely" + MONTHLY = "Monthly" + SECONDLY = "Secondly" + WEEKLY = "Weekly" + YEARLY = "Yearly" +end +end diff --git a/lib/exact_target_sdk/request_type.rb b/lib/exact_target_sdk/request_type.rb new file mode 100644 index 0000000..1e271b7 --- /dev/null +++ b/lib/exact_target_sdk/request_type.rb @@ -0,0 +1,10 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/RequestType +module ExactTargetSDK +module RequestType + # Specifies that the API request should be processed asynchronously. + ASYNCHRONOUS = "Asynchronous" + + # Specifies that the process should be processed synchronously. + SYNCHRONOUS = "Synchronous" +end +end diff --git a/lib/exact_target_sdk/respond_when.rb b/lib/exact_target_sdk/respond_when.rb new file mode 100644 index 0000000..80e6d1a --- /dev/null +++ b/lib/exact_target_sdk/respond_when.rb @@ -0,0 +1,23 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/RespondWhen +module ExactTargetSDK +module RespondWhen + # Always send the response. + ALWAYS = "Always" + + # Specifies that a response should never be set for an asynchronous process. + NEVER = "Never" + + # Specifies that a response should be sent when an asynchronous call is complete. + ON_CALL_COMPLETE = "OnCallComplete" + + # Specifies that a response should be sent when an asynchronous conversation is complete. + ON_CONVERSATION_COMPLETE = "OnConversationComplete" + + # Specifies that a response should be sent when an asynchronous conversation returns an error. + ON_CONVERSATION_ERROR = "OnConversationError" + + # Specifies that a response should be send when an asynchronous process returns an error. + ON_ERROR = "OnError" +end +end + diff --git a/lib/exact_target_sdk/result.rb b/lib/exact_target_sdk/result.rb index fc611e1..ffcb8b8 100644 --- a/lib/exact_target_sdk/result.rb +++ b/lib/exact_target_sdk/result.rb @@ -1,15 +1,23 @@ require 'active_support/inflector' module ExactTargetSDK -class Result + class Result - def initialize(hash) - @result = hash - end + def id + @result[:id] || self.id + end - def method_missing(symbol, *args) - @result[symbol.to_s.underscore.to_sym] - end + def object_id + @result[:object_id] || self.object_id + end -end + def initialize(hash) + @result = hash + end + + def method_missing(symbol, *args) + @result[symbol.to_s.underscore.to_sym] + end + + end end diff --git a/lib/exact_target_sdk/result_message.rb b/lib/exact_target_sdk/result_message.rb new file mode 100644 index 0000000..620341a --- /dev/null +++ b/lib/exact_target_sdk/result_message.rb @@ -0,0 +1,63 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/ResultMessage +module ExactTargetSDK +class ResultMessage < APIObject + + # Number of calls within an async API conversation. + property 'CallsInConversation' + + # Specifies the account ownership and context of an object. + property 'Client' + + # Unique ID of initial async API call. + property 'ConversationID' + + # Identifies correlation of objects across several requests. + property 'CorrelationID' + + # Read-only date and time of the objects creation. + property 'CreatedDate' + + # Identifies the error of an API request. + property 'ErrorCode' + + # Last time object information was modified. + property 'ModifiedDate' + + # Reserved for future use. + property 'ObjectState' + + # Represents overall status of conversation via async API. + property 'OverallStatusCode' + + # Describes account ownership of subscriber in an on-your-behalf account. + property 'Owner' + + # Unique identifier provided by partner for an object, accessible only via API. + property 'PartnerKey' + + # A collection of metadata supplied by client and stored by system - only accessible via API. + array_property 'PartnerProperties' + + # Unique ID of initial async API call. + property 'RequestID' + + # Defines request as synchronous or asynchronous API. + property 'RequestType' + + # Contains details of operation result in XML format. + property 'ResultDetailXML' + + # Defines result as coming from synchronous or asynchronous API. + property 'ResultType' + + # Specifies the processing sequence of a multi-step conversation. + property 'SequenceCode' + + # Status of async API request. + property 'StatusCode' + + # Describes the status of an API call. + property 'StatusMessage' + +end +end diff --git a/lib/exact_target_sdk/retrieve_options.rb b/lib/exact_target_sdk/retrieve_options.rb new file mode 100644 index 0000000..ec9d715 --- /dev/null +++ b/lib/exact_target_sdk/retrieve_options.rb @@ -0,0 +1,15 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/RetrieveOptions +module ExactTargetSDK +class RetrieveOptions < Options + + # Number of records to return in each batch as part of a Retrieve call. Reserved for future use. + property 'BatchSize' + + # Indicates whether the APIObject should be included in the response. + property 'IncludeObjects' + + # Reduce object to base APIObject information. Includes basic reference data associating object and request. + property 'OnlyIncludeBase' + +end +end diff --git a/lib/exact_target_sdk/salutation_source_enum.rb b/lib/exact_target_sdk/salutation_source_enum.rb new file mode 100644 index 0000000..7ec4374 --- /dev/null +++ b/lib/exact_target_sdk/salutation_source_enum.rb @@ -0,0 +1,7 @@ +module ExactTargetSDK +module SalutationSourceEnum + CONTENT_LIBRARY = "ContentLibrary" + DEFAULT = "Default" + NONE = "None" +end +end diff --git a/lib/exact_target_sdk/save_action.rb b/lib/exact_target_sdk/save_action.rb new file mode 100644 index 0000000..fd32384 --- /dev/null +++ b/lib/exact_target_sdk/save_action.rb @@ -0,0 +1,23 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/SaveAction +module ExactTargetSDK +module SaveAction + # Indicates that data should only be added and not updated during a save action. + ADD_ONLY = "AddOnly" + + #Specifies default source of salutation when building an email (SalutationSourceEnum) Use the default action when saving an object. (SaveAction) + DEFAULT = "Default" + + #Indicates whether an object should be deleted. + DELETE = "Delete" + + #Indicates no save action should take place. + NOTHING = "Nothing" + + #Indicates an UpdateAdd type for a save action. If this property is specified, the save action will update existing information and add new information. + UPDATE_ADD = "UpdateAdd" + + #Indicates an UpdateOnly type for a save action. If this property is specified, the save action with update existing information only. + UPDATE_ONLY = "UpdateOnly" + +end +end diff --git a/lib/exact_target_sdk/save_option.rb b/lib/exact_target_sdk/save_option.rb new file mode 100644 index 0000000..c633b47 --- /dev/null +++ b/lib/exact_target_sdk/save_option.rb @@ -0,0 +1,12 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/SaveOption +module ExactTargetSDK +class SaveOption < Base + + # String object to which the SaveOption object applies. + property "PropertyName" + + # Behavior specified for SaveAction object. + property "SaveAction" + +end +end diff --git a/lib/exact_target_sdk/schedule_definition.rb b/lib/exact_target_sdk/schedule_definition.rb new file mode 100644 index 0000000..9087688 --- /dev/null +++ b/lib/exact_target_sdk/schedule_definition.rb @@ -0,0 +1,20 @@ +module ExactTargetSDK +class ScheduleDefinition < APIObject + + # Specifies type of recurrence, such as daily or hourly. + property "RecurrenceType" + + # Defines how a recurrence type ends. + property "RecurrenceRangeType" + + # Specifies start time of schedule definition + property "StartDateTime" + + # Specifies number of times to run a schedule definition. + property "Occurrences" + + # Interval of recurrence type. + property "Recurrence" + +end +end diff --git a/lib/exact_target_sdk/schedule_options.rb b/lib/exact_target_sdk/schedule_options.rb new file mode 100644 index 0000000..8c31cb7 --- /dev/null +++ b/lib/exact_target_sdk/schedule_options.rb @@ -0,0 +1,8 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/ScheduleOptions +module ExactTargetSDK +class ScheduleOptions < Options + +end +end + + diff --git a/lib/exact_target_sdk/schedule_response.rb b/lib/exact_target_sdk/schedule_response.rb new file mode 100644 index 0000000..9f45743 --- /dev/null +++ b/lib/exact_target_sdk/schedule_response.rb @@ -0,0 +1,26 @@ +module ExactTargetSDK +class ScheduleResponse + + attr_reader :OverallStatus, :OverallStatusMessage, :RequestID, :Results + + def initialize(response) + response = response.to_hash[:schedule_response_msg] + @OverallStatus = response[:overall_status] + @OverallStatusMessage = response[:overall_status_message] + @RequestID = response[:request_id] + @Results = [] + + results = if response[:results].is_a? Array + response[:results] + elsif response[:results].is_a? Hash + [response[:results]] + else + [] + end + results.each do |result| + @Results << ScheduleResult.new(result) + end + end + +end +end diff --git a/lib/exact_target_sdk/schedule_result.rb b/lib/exact_target_sdk/schedule_result.rb new file mode 100644 index 0000000..bcfc87d --- /dev/null +++ b/lib/exact_target_sdk/schedule_result.rb @@ -0,0 +1,5 @@ +module ExactTargetSDK +class ScheduleResult < Result + +end +end diff --git a/lib/exact_target_sdk/send.rb b/lib/exact_target_sdk/send.rb new file mode 100644 index 0000000..2f60183 --- /dev/null +++ b/lib/exact_target_sdk/send.rb @@ -0,0 +1,71 @@ +module ExactTargetSDK +class Send < APIObject + + property 'Name' + + # Default email address for object. Indicates if subscriber information can be used for email sends. + property "Email" + + # Specifies the name of an email message associated with a send. + property "EmailName" + + # Indicates email send definition to which the send object is attached. + property "EmailSendDefinition" + + # Indicates From address associated with a object. + # Deprecated for email send definitions and triggered send definitions. + property "FromAddress" + + # Specifies the default email message From Name. + # Deprecated for email send definitions and triggered send definitions. + property "FromName" + + # Indicates number of hard bounces associated with a send. + property "HardBounces" + + # List associated with an object. + property "List" + + # Number of sent emails that did not bounce. + property "NumberDelivered" + + # Number of emails not sent as part of a send because an error occurred while trying to build the email. + property "NumberErrored" + + # Indicates the number recipients excluded from an email send because of a held, + # unsubscribed, master unsubscribed, or global unsubscribed status. + property "NumberExcluded" + + # Number of emails actually sent as part of an email send. + # This number reflects all of the sent messages and may include bounced messages. + property "NumberSent" + + # Specifies number of Other-type bounces in a send. + property "OtherBounces" + + # Indicates URL used to preview the message associated with a send. + property "PreviewURL" + + # Indicates date on which a send took place. + property "SentDate" + + # Indicates number of soft bounces associated with a specific send. + property "SoftBounces" + + # Defines status of object. Status of an address. + property "Status" + + # Contains subject area information for a message. + property "Subject" + + # Indicates number of unique clicks on message. + property "UniqueClicks" + + # Indicates number of unique opens resulting from a triggered send. + property "UniqueOpens" + + # Indicates the number of unsubscribe events associated with a send. + property 'Unsubscribes' + +end +end diff --git a/lib/exact_target_sdk/send_classification.rb b/lib/exact_target_sdk/send_classification.rb new file mode 100644 index 0000000..5b50dfb --- /dev/null +++ b/lib/exact_target_sdk/send_classification.rb @@ -0,0 +1,11 @@ +module ExactTargetSDK +class SendClassification < APIObject + + property 'Name', :required => true + + property 'SenderProfile', :required => true + + property 'DeliveryProfile', :required => true + +end +end diff --git a/lib/exact_target_sdk/send_definition_list.rb b/lib/exact_target_sdk/send_definition_list.rb new file mode 100644 index 0000000..f0ba04e --- /dev/null +++ b/lib/exact_target_sdk/send_definition_list.rb @@ -0,0 +1,19 @@ +module ExactTargetSDK +class SendDefinitionList < APIObject + + property 'DataSourceTypeID' + property 'List' + + property 'Name' + + # Indicates the specified send is a test send. A value of true indicates a test send. + property 'IsTestObject' + + # Represents the ID of the sendable data extension used as part of a send. + property 'CustomObjectID' + + # Defines type of send definition list. + property 'SendDefinitionListType' + +end +end diff --git a/lib/exact_target_sdk/send_definition_list_type_enum.rb b/lib/exact_target_sdk/send_definition_list_type_enum.rb new file mode 100644 index 0000000..a0ffe7c --- /dev/null +++ b/lib/exact_target_sdk/send_definition_list_type_enum.rb @@ -0,0 +1,12 @@ +module ExactTargetSDK +module SendDefinitionListTypeEnum + + LIST = "List" + CUSTOM_OBJECT = "CustomObject" + DOMAIN_EXCLUSION = "DomainExclusion" + SALES_FORCE_CAMPAIGN = "SalesForceCampaign" + FILTER_DEFINITION = "FilterDefinition" + OPT_OUT_LIST = "OptOutList" + +end +end diff --git a/lib/exact_target_sdk/sender_profile.rb b/lib/exact_target_sdk/sender_profile.rb new file mode 100644 index 0000000..58fec19 --- /dev/null +++ b/lib/exact_target_sdk/sender_profile.rb @@ -0,0 +1,22 @@ +module ExactTargetSDK +class SenderProfile < APIObject + + property 'Name' + + # Specifies the default email message From Name. Deprecated for email send definitions and triggered send definitions. + property 'FromName' + + # Indicates From address associated with a object. Deprecated for email send definitions and triggered send definitions. + property 'FromAddress' + + # Indicates the To name to use on automatically forwarded email messages. + property 'AutoForwardToName' + + # Indicates the email address to use with automatically forwarded email messages. + property 'AutoForwardToEmailAddress' + + # Indicates whether a sender profile uses the default RMM rules for that account. + property 'UseDefaultRMMRules' + +end +end diff --git a/lib/exact_target_sdk/sent_event.rb b/lib/exact_target_sdk/sent_event.rb new file mode 100644 index 0000000..66d0afa --- /dev/null +++ b/lib/exact_target_sdk/sent_event.rb @@ -0,0 +1,14 @@ +module ExactTargetSDK +class SentEvent < APIObject + + # Date when a tracking event occurred. + property 'EventDate' + + # Contains identifier for a specific send. + property 'SendID' + + # Identification of a specific subscriber. + property 'SubscriberKey' + +end +end diff --git a/lib/exact_target_sdk/simple_operator.rb b/lib/exact_target_sdk/simple_operator.rb new file mode 100644 index 0000000..f6a725c --- /dev/null +++ b/lib/exact_target_sdk/simple_operator.rb @@ -0,0 +1,15 @@ +module ExactTargetSDK +module SimpleOperator + EQUALS = 'equals' + NOT_EQUALS = 'notEquals' + GREATER_THAN = 'greaterThan' + LESS = 'lessThan' + IS_NOT_NULL = 'isNotNull' + IS_NULL = 'isNull' + GREATER_THAN_EQUAL = 'greaterThanOrEqual' + LESS_THAN_EQUAL = 'lessThanOrEqual' + BETWEEN = 'between' + IN = 'IN' + LIKE = 'like' +end +end diff --git a/lib/exact_target_sdk/spam_assassin_validation.rb b/lib/exact_target_sdk/spam_assassin_validation.rb new file mode 100644 index 0000000..ffa1447 --- /dev/null +++ b/lib/exact_target_sdk/spam_assassin_validation.rb @@ -0,0 +1,5 @@ +module ExactTargetSDK +class SpamAssassinValidation < ValidationAction + +end +end diff --git a/lib/exact_target_sdk/subscriber.rb b/lib/exact_target_sdk/subscriber.rb index 0fcf592..093d9ba 100644 --- a/lib/exact_target_sdk/subscriber.rb +++ b/lib/exact_target_sdk/subscriber.rb @@ -17,14 +17,9 @@ class Subscriber < APIObject property 'EmailAddress', :required => true property 'EmailTypePreference' array_property 'Attributes' + array_property 'Lists' - before_validation :sync_subscriber_key_and_email_address - - validates 'EmailTypePreference', :inclusion => { :allow_nil => true, :in => %w( HTML Text ) } - - private - - def sync_subscriber_key_and_email_address + before_validation do self.SubscriberKey = self.EmailAddress if self.SubscriberKey.nil? self.EmailAddress = self.SubscriberKey if self.EmailAddress.nil? end diff --git a/lib/exact_target_sdk/subscriber_list.rb b/lib/exact_target_sdk/subscriber_list.rb new file mode 100644 index 0000000..22f39f4 --- /dev/null +++ b/lib/exact_target_sdk/subscriber_list.rb @@ -0,0 +1,17 @@ +module ExactTargetSDK +class SubscriberList < APIObject + + # Defines the action to take for the specified object. + property 'Action' + + # List associated with an object. + property 'List' + + # Defines status of object. Status of an address. + property 'Status' + + # Defines status of object. Status of an address. + property 'Subscriber' + +end +end diff --git a/lib/exact_target_sdk/update_options.rb b/lib/exact_target_sdk/update_options.rb new file mode 100644 index 0000000..6856777 --- /dev/null +++ b/lib/exact_target_sdk/update_options.rb @@ -0,0 +1,7 @@ +# http://docs.code.exacttarget.com/020_Web_Service_Guide/Objects/UpdateOptions +module ExactTargetSDK +class UpdateOptions < Options + #Defines the action to take for the specified object. + property 'Action' +end +end diff --git a/lib/exact_target_sdk/update_result.rb b/lib/exact_target_sdk/update_result.rb index 7ce2d23..75dbaf0 100644 --- a/lib/exact_target_sdk/update_result.rb +++ b/lib/exact_target_sdk/update_result.rb @@ -1,4 +1,11 @@ module ExactTargetSDK -class UpdateResult < Result -end + class UpdateResult < Result + def object + @result[:object] + end + + def id + object && object[:id] + end + end end diff --git a/lib/exact_target_sdk/validation_action.rb b/lib/exact_target_sdk/validation_action.rb new file mode 100644 index 0000000..b801d37 --- /dev/null +++ b/lib/exact_target_sdk/validation_action.rb @@ -0,0 +1,11 @@ +module ExactTargetSDK +class ValidationAction < APIObject + + # Options for validation object. Type: APIProperty[] + array_property 'ValidationOptions' + + # Specified type of validation + property 'ValidationType' + +end +end diff --git a/spec/delivery_profile_spec.rb b/spec/delivery_profile_spec.rb new file mode 100644 index 0000000..bb46452 --- /dev/null +++ b/spec/delivery_profile_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' +include ExactTargetSDK + +describe DeliveryProfile do + context 'a bare DeliveryProfile' do + + it { should_not be_valid } + + end + + context ' a validated DeliveryProfile with Name and SourceAddressType' do + before(:each) do + @delivery_profile = DeliveryProfile.new 'Name' => 'Test Profile', + 'SourceAddressType' => DeliveryProfileSourceAddressTypeEnum::DEFAULT_PRIVATE_IP_ADDRESS + @delivery_profile.valid? + end + + subject { @delivery_profile } + + it { should be_valid } + + end + + +end \ No newline at end of file diff --git a/spec/email_send_definition_spec.rb b/spec/email_send_definition_spec.rb new file mode 100644 index 0000000..8668686 --- /dev/null +++ b/spec/email_send_definition_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' +include ExactTargetSDK + +describe EmailSendDefinition do + context 'a bare EmailSendDefinition' do + + it { should_not be_valid } + + end + + context 'a validated EmailSendDefinition' do + before(:each) do + + # Create List + list = ExactTargetSDK::List.new('ID' => "273", + 'ListName' => "Test list") + + # Create SendClassification + send_classification = ExactTargetSDK::SendClassification.new('ObjectID' => "1234b99-b592-e111-b32b-984be17c0e6c") + + # Create SendDefinitionList + send_definition_list = ExactTargetSDK::SendDefinitionList.new('DataSourceTypeID' => 'List', 'List' => list) + + + #Create Email + email = ExactTargetSDK::Email.new('ID' => '239', + 'Name' => 'test', + 'Subject' => 'TestSubject', + 'HTMLBody' => '

Body

', + 'TextBody' => 'Body') + + @email_send_definition = ExactTargetSDK::EmailSendDefinition.new('Name' => 'test', + 'Email' => email, + 'SendClassification' => send_classification, + 'SendDefinitionList' => send_definition_list) + @email_send_definition.valid? + end + + subject { @email_send_definition } + + it { should be_valid } + + end + +end \ No newline at end of file diff --git a/spec/email_spec.rb b/spec/email_spec.rb new file mode 100644 index 0000000..6154880 --- /dev/null +++ b/spec/email_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' +include ExactTargetSDK + +describe Email do + context 'a bare Email' do + + it { should_not be_valid } + + end + + context ' a validated Email with Name and Subject set' do + before(:each) do + @email = Email.new 'Name' => 'Welcome email', 'Subject' => 'Welcome to the Dark Side' + @email.valid? + end + + subject { @email } + + it {should be_valid} + + end + + context ' an invalid Email with Name and Subject set' do + before(:each) do + @email = Email.new 'Subject' => 'Welcome to the Dark Side' + @email.valid? + end + + subject { @email } + + it {should_not be_valid} + + end + + context 'a Email object with given ID' do + before(:each) do + @email = Email.new 'ID' => '587' + @email.valid? + end + + subject { @email } + + it {should be_valid} + + end + +end \ No newline at end of file diff --git a/spec/list_spec.rb b/spec/list_spec.rb new file mode 100644 index 0000000..a3c5c01 --- /dev/null +++ b/spec/list_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' +include ExactTargetSDK + +describe List do + context 'a bare List' do + + it { should_not be_valid } + + end + + context ' a validated List with ListName set' do + before(:each) do + @list = List.new 'ListName' => 'My Favourite List' + @list.valid? + end + + subject { @list } + + it {should be_valid} + + end + + context ' an invalid List without ListName' do + before(:each) do + @list = List.new + @list.valid? + end + + subject { @list } + + it {should_not be_valid} + + end + +end \ No newline at end of file diff --git a/spec/result_spec.rb b/spec/result_spec.rb index 3e020ec..0ed8126 100644 --- a/spec/result_spec.rb +++ b/spec/result_spec.rb @@ -22,4 +22,14 @@ end + context 'a result initialized with id field ' do + + subject { Result.new(:id => "42") } + + it 'should respond to id with "42"' do + subject.id.should == "42" + end + + end + end diff --git a/spec/send_classification_spec.rb b/spec/send_classification_spec.rb new file mode 100644 index 0000000..79e8e5e --- /dev/null +++ b/spec/send_classification_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' +include ExactTargetSDK + +describe SendClassification do + context 'a bare SendClassification' do + + it { should_not be_valid } + + end + + context 'a validated SendClassification' do + before(:each) do + sp = ExactTargetSDK::SenderProfile.new 'ObjectID' => "some-object-id" + dp = ExactTargetSDK::DeliveryProfile.new 'ObjectID' => "some-object-id" + @send_classification = ExactTargetSDK::SendClassification.new('Name' => 'Test Classification', + 'SenderProfile' => sp, + 'DeliveryProfile' => dp ) + @send_classification.valid? + end + + subject { @send_classification } + + it { should be_valid } + + end + +end \ No newline at end of file diff --git a/spec/sender_profile.rb b/spec/sender_profile.rb new file mode 100644 index 0000000..8b10124 --- /dev/null +++ b/spec/sender_profile.rb @@ -0,0 +1,25 @@ +require 'spec_helper' +include ExactTargetSDK + +describe SenderProfile do + context 'a bare SenderProfile' do + + it { should_not be_valid } + + end + + context ' a validated SenderProfile ' do + before(:each) do + @sender_profile = SenderProfile.new 'Name' => 'Test Profile', + 'FromName' => 'Bob', + 'FromAddress' => 'bob@example.com' + @sender_profile.valid? + end + + subject { @sender_profile } + + it { should be_valid } + + end + +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6c77f7c..5790e55 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,3 +6,12 @@ # clear environment variables to have clean environment ENV['EXACT_TARGET_SDK_USERNAME'] = nil ENV['EXACT_TARGET_SDK_PASSWORD'] = nil + + +RSpec.configure do |config| + # Use color in STDOUT + config.color_enabled = true + + # Use color not only in STDOUT but also in pagers and files + config.tty = true +end \ No newline at end of file