=begin Copyright (c) 2013 ExactTarget, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =end module MarketingCloudSDK module Objects module Soap module Read attr_accessor :filter def get _id=nil client.soap_get _id||id, properties, filter end def info client.soap_describe id end end module CUD #create, update, delete def post if self.respond_to?('folder_property') && !self.folder_id.nil? properties[self.folder_property] = self.folder_id elsif self.respond_to?('folder_property') && !self.folder_property.nil? && !client.package_name.nil? then if client.package_folders.nil? then getPackageFolder = ET_Folder.new getPackageFolder.authStub = client getPackageFolder.properties = ["ID", "ContentType"] getPackageFolder.filter = {"Property" => "Name", "SimpleOperator" => "equals", "Value" => client.package_name} resultPackageFolder = getPackageFolder.get if resultPackageFolder.status then client.package_folders = {} resultPackageFolder.results.each do |value| client.package_folders[value[:content_type]] = value[:id] end else raise "Unable to retrieve folders from account due to: #{resultPackageFolder.message}" end end if !client.package_folders.has_key?(self.folder_media_type) then if client.parentFolders.nil? then parentFolders = ET_Folder.new parentFolders.authStub = client parentFolders.properties = ["ID", "ContentType"] parentFolders.filter = {"Property" => "ParentFolder.ID", "SimpleOperator" => "equals", "Value" => "0"} resultParentFolders = parentFolders.get if resultParentFolders.status then client.parent_folders = {} resultParentFolders.results.each do |value| client.parent_folders[value[:content_type]] = value[:id] end else raise "Unable to retrieve folders from account due to: #{resultParentFolders.message}" end end newFolder = ET_Folder.new newFolder.authStub = client newFolder.properties = {"Name" => client.package_name, "Description" => client.package_name, "ContentType"=> self.folder_media_type, "IsEditable"=>"true", "ParentFolder" => {"ID" => client.parentFolders[self.folder_media_type]}} folderResult = newFolder.post if folderResult.status then client.package_folders[self.folder_media_type] = folderResult.results[0][:new_id] else raise "Unable to create folder for Post due to: #{folderResult.message}" end end properties[self.folder_property] = client.package_folders[self.folder_media_type] end client.soap_post id, properties end def patch client.soap_patch id, properties end def delete client.soap_delete id, properties end end module Upsert def put client.soap_put id, properties end end end module Rest module Read def get client.rest_get id, properties end end module CUD def post client.rest_post id, properties end def patch client.rest_patch id, properties end def delete client.rest_delete id, properties end end end class Base attr_accessor :properties, :client attr_reader :id alias props= properties= # backward compatibility alias authStub= client= # backward compatibility def properties #@properties = [@properties].compact unless @properties.kind_of? Array @properties end #Backwards compatibility def props @properties end def id self.class.id end class << self def id self.name.split('::').pop end end end end class BounceEvent < Objects::Base attr_accessor :get_since_last_batch include Objects::Soap::Read end class ClickEvent < Objects::Base attr_accessor :get_since_last_batch include Objects::Soap::Read end class ContentArea < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD attr_accessor :folder_id def folder_property 'CategoryID' end def folder_media_type 'content' end end class DataFolder < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD end class Folder < DataFolder class << self def id DataFolder.id end end end class Email < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD attr_accessor :folder_id def folder_property 'CategoryID' end def folder_media_type 'email' end class SendDefinition < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD attr_accessor :folder_id def id 'EmailSendDefinition' end def folder_property 'CategoryID' end def folder_media_type 'userinitiatedsends' end def send perform_response = client.soap_perform id, 'start' , properties if perform_response.status then @last_task_id = perform_response.results[0][:result][:task][:id] end perform_response end def status client.soap_get "Send", ['ID','CreatedDate', 'ModifiedDate', 'Client.ID', 'Email.ID', 'SendDate','FromAddress','FromName','Duplicates','InvalidAddresses','ExistingUndeliverables','ExistingUnsubscribes','HardBounces','SoftBounces','OtherBounces','ForwardedEmails','UniqueClicks','UniqueOpens','NumberSent','NumberDelivered','NumberTargeted','NumberErrored','NumberExcluded','Unsubscribes','MissingAddresses','Subject','PreviewURL','SentDate','EmailName','Status','IsMultipart','SendLimit','SendWindowOpen','SendWindowClose','BCCEmail','EmailSendDefinition.ObjectID','EmailSendDefinition.CustomerKey'], {'Property' => 'ID','SimpleOperator' => 'equals','Value' => @last_task_id} end private attr_accessor :last_task_id end end class Import < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD def id 'ImportDefinition' end def post originalProp = properties cleanProps obj = super properties = originalProp return obj end def patch originalProp = properties cleanProps obj = super properties = originalProp return obj end def start perform_response = client.soap_perform id, 'start' , properties if perform_response.status then @last_task_id = perform_response.results[0][:result][:task][:id] end perform_response end def status client.soap_get "ImportResultsSummary", ['ImportDefinitionCustomerKey','TaskResultID','ImportStatus','StartDate','EndDate','DestinationID','NumberSuccessful','NumberDuplicated','NumberErrors','TotalRows','ImportType'], {'Property' => 'TaskResultID','SimpleOperator' => 'equals','Value' => @last_task_id} end private attr_accessor :last_task_id def cleanProps # If the ID property is specified for the destination then it must be a list import if properties.has_key?('DestinationObject') then if properties['DestinationObject'].has_key?('ID') then properties[:attributes!] = { 'DestinationObject' => { 'xsi:type' => 'tns:List'}} end end end end class List < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD attr_accessor :folder_id def folder_property 'Category' end def folder_media_type 'list' end class Subscriber < Objects::Base include Objects::Soap::Read def id 'ListSubscriber' end end end class OpenEvent < Objects::Base attr_accessor :get_since_last_batch include Objects::Soap::Read end class SentEvent < Objects::Base attr_accessor :get_since_last_batch include Objects::Soap::Read end class Subscriber < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD include Objects::Soap::Upsert end class UnsubEvent < Objects::Base attr_accessor :get_since_last_batch include Objects::Soap::Read end class ProfileAttribute < Objects::Base def get client.soap_describe "Subscriber" end def post client.soap_configure "PropertyDefinition","create", properties end def delete client.soap_configure "PropertyDefinition","delete", properties end def patch client.soap_configure "PropertyDefinition","update", properties end end class TriggeredSend < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD attr_accessor :folder_id, :subscribers, :attributes def id 'TriggeredSendDefinition' end def folder_property 'CategoryID' end def folder_media_type 'triggered_send' end def send if self.properties.is_a? Array then tscall = [] self.properties.each{ |p| tscall.push({"TriggeredSendDefinition" => {"CustomerKey" => p["CustomerKey"]}, "Subscribers" => p["Subscribers"], "Attributes" => p["Attributes"]}) } else tscall = {"TriggeredSendDefinition" => self.properties, "Subscribers" => @subscribers, "Attributes" => @attributes } end client.soap_post 'TriggeredSend', tscall end end class DataExtension < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD attr_accessor :fields, :folder_id def folder_property 'CategoryID' end def folder_media_type 'dataextension' end alias columns= fields= # backward compatibility def post munge_fields self.properties super end def patch munge_fields self.properties super end class Column < Objects::Base include Objects::Soap::Read def id 'DataExtensionField' end def get if filter and filter.kind_of? Hash and \ filter.include? 'Property' and filter['Property'] == 'CustomerKey' filter['Property'] = 'DataExtension.CustomerKey' end super end end class Row < Objects::Base include Objects::Soap::Read include Objects::Soap::CUD include Objects::Soap::Upsert attr_accessor :name, :customer_key # backward compatibility alias Name= name= alias CustomerKey= customer_key= def id 'DataExtensionObject' end def get super "#{id}[#{name}]" end def name unless @name retrieve_required end @name end def customer_key unless @customer_key retrieve_required end @customer_key end def post munge_properties self.properties super end def patch munge_properties self.properties super end def put munge_properties self.properties super end def delete munge_keys self.properties super end private #::TODO:: # opportunity for meta programming here... but need to get this out the door def munge_keys d if d.kind_of? Array d.each do |o| next if explicit_keys(o) && explicit_customer_key(o) formatted = [] o['CustomerKey'] = customer_key unless explicit_customer_key o unless explicit_properties(o) o.each do |k, v| next if k == 'CustomerKey' formatted.concat MarketingCloudSDK.format_name_value_pairs k => v o.delete k end o['Keys'] = {'Key' => formatted } end end else formatted = [] d.each do |k, v| next if k == 'CustomerKey' formatted.concat MarketingCloudSDK.format_name_value_pairs k => v d.delete k end d['CustomerKey'] = customer_key d['Keys'] = {'Key' => formatted } end end def explicit_keys h h['Keys'] and h['Keys']['Key'] end def munge_properties d if d.kind_of? Array d.each do |o| next if explicit_properties(o) && explicit_customer_key(o) formatted = [] o['CustomerKey'] = customer_key unless explicit_customer_key o unless explicit_properties(o) o.each do |k, v| next if k == 'CustomerKey' formatted.concat MarketingCloudSDK.format_name_value_pairs k => v o.delete k end o['Properties'] = {'Property' => formatted } end end else formatted = [] d.each do |k, v| formatted.concat MarketingCloudSDK.format_name_value_pairs k => v d.delete k end d['CustomerKey'] = customer_key d['Properties'] = {'Property' => formatted } end end def explicit_properties h h['Properties'] and h['Properties']['Property'] end def explicit_customer_key h h['CustomerKey'] end def retrieve_required # have to use instance variables so we don't recursivelly retrieve_required if !@name && !@customer_key raise 'Unable to process DataExtension::Row ' \ 'request due to missing CustomerKey and Name' end if !@name || !@customer_key filter = { 'Property' => @name.nil? ? 'CustomerKey' : 'Name', 'SimpleOperator' => 'equals', 'Value' => @customer_key || @name } rsp = client.soap_get 'DataExtension', ['Name', 'CustomerKey'], filter if rsp.success? && rsp.results.count == 1 self.name = rsp.results.first[:name] self.customer_key = rsp.results.first[:customer_key] else raise 'Unable to process DataExtension::Row' end end end end private def munge_fields d # maybe one day will make it smart enough to zip properties and fields if count is same? if d.kind_of? Array and d.count > 1 and (fields and !fields.empty?) # we could map the field to all DataExtensions, but lets make user be explicit. # if they are going to use fields attribute properties should # be a single DataExtension Defined in a Hash raise 'Unable to handle muliple DataExtension definitions and a field definition' end if d.kind_of? Array d.each do |de| if (explicit_fields(de) and (de['columns'] || de['fields'] || has_fields)) or (de['columns'] and (de['fields'] || has_fields)) or (de['fields'] and has_fields) raise 'Fields are defined in too many ways. Please only define once.' # ahhh what, to do... end # let users who chose, to define fields explicitly within the hash definition next if explicit_fields de de['Fields'] = {'Field' => de['columns'] || de['fields'] || fields} # sanitize raise 'DataExtension needs atleast one field.' unless de['Fields']['Field'] end else self.properties['Fields'] = {'Field' => self.properties['columns'] || self.properties['fields'] || fields} raise 'DataExtension needs atleast one field.' unless self.properties['Fields']['Field'] self.properties.delete 'columns' self.properties.delete 'fields' end end def explicit_fields h h['Fields'] and h['Fields']['Field'] end def has_fields fields and !fields.empty? end end class Campaign < Objects::Base include Objects::Rest::Read include Objects::Rest::CUD def properties @properties ||= {} @properties.merge! 'id' => '' unless @properties.include? 'id' @properties end def id "https://www.exacttargetapis.com/hub/v1/campaigns/%{id}" end class Asset < Objects::Base include Objects::Rest::Read include Objects::Rest::CUD def properties @properties ||= {} @properties.merge! 'assetId' => '' unless @properties.include? 'assetId' @properties end def id 'https://www.exacttargetapis.com/hub/v1/campaigns/%{id}/assets/%{assetId}' end end end # Direct Verb Access Section class Get < Objects::Base include Objects::Soap::Read attr_accessor :id def initialize client, id, properties, filter self.properties = properties self.filter = filter self.client = client self.id = id end def get super id end class << self def new client, id, properties=nil, filter=nil o = self.allocate o.send :initialize, client, id, properties, filter return o.get end end end class Post < Objects::Base include Objects::Soap::CUD attr_accessor :id def initialize client, id, properties self.properties = properties self.client = client self.id = id end def post super end class << self def new client, id, properties=nil o = self.allocate o.send :initialize, client, id, properties return o.post end end end class Delete < Objects::Base include Objects::Soap::CUD attr_accessor :id def initialize client, id, properties self.properties = properties self.client = client self.id = id end def delete super end class << self def new client, id, properties=nil o = self.allocate o.send :initialize, client, id, properties return o.delete end end end class Patch < Objects::Base include Objects::Soap::CUD attr_accessor :id def initialize client, id, properties self.properties = properties self.client = client self.id = id end def patch super end class << self def new client, id, properties=nil o = self.allocate o.send :initialize, client, id, properties return o.patch end end end end