class MingleEvents::Processors::CardData
Provides ability to lookup card data, e.g., card’s type name, for any card serving as a source for the stream of events. Implements two interfaces: the standard event processing interface, handle_events, and also, for_card_event
, which returns of has of card data for the given event. See the project README for additional information on using this class in a processing pipeline.
Public Class Methods
# File lib/mingle_events/processors/card_data.rb 11 def initialize(mingle_access, project_identifier, custom_properties = ProjectCustomProperties.new(mingle_access, project_identifier)) 12 @mingle_access = mingle_access 13 @project_identifier = project_identifier 14 @custom_properties = custom_properties 15 @card_data_by_number_and_version = nil 16 end
Public Instance Methods
Return a hash of data for the card that sourced the passed event. The data will be for the version of the card that was created by the event and not the current state of the card. Currently supported data keys are: :number, :version, :card_type_name
# File lib/mingle_events/processors/card_data.rb 29 def for_card_event(card_event) 30 if @card_data_by_number_and_version.nil? 31 load_bulk_card_data 32 end 33 key = data_key(card_event.card_number, card_event.version) 34 @card_data_by_number_and_version[key] ||= load_card_data_for_event(card_event) 35 end
Capture which events are card events and might require data lookup. The actual data retrieval is lazy and will only occur as needed.
# File lib/mingle_events/processors/card_data.rb 20 def process_events(events) 21 @card_events = events.select(&:card?) 22 events 23 end
Private Instance Methods
# File lib/mingle_events/processors/card_data.rb 39 def data_key(number, version) 40 "#{number}:#{version}" 41 end
# File lib/mingle_events/processors/card_data.rb 43 def load_bulk_card_data 44 @card_data_by_number_and_version = {} 45 46 card_numbers = @card_events.map(&:card_number).uniq 47 path = "/api/v2/projects/#{@project_identifier}/cards/execute_mql.xml?mql=WHERE number IN (#{card_numbers.join(',')})" 48 49 # TODO: figure out whether it makes sense to chunk a large count of card numbers 50 # into multiple requests so that the MQL "IN" clause doesn't explode. For now, we'll 51 # just punt by logging the error and letting the individual card data load explode 52 # if there's a real problem. In most polling scenarios, this is a highly unlikely 53 # problem as there will usually be 1 or a few events. 54 begin 55 raw_xml = @mingle_access.fetch_page(URIParser.escape(path)) 56 rescue 57 msg = %{ 58 59 There was an error while attempting bulk load of card data. 60 Individual data loads for each card will still be attempted. 61 62 Root cause: 63 64 #{$!.message} 65 66 Stack Trace: 67 68 #{($!.backtrace || []).join("\n")} 69 70 } 71 MingleEvents.log.info(msg) 72 return 73 end 74 doc = Xml.parse(raw_xml) 75 76 doc.select_all('/results/result').map do |card_result| 77 card_number = card_result.inner_text('number').to_i 78 card_version = card_result.inner_text('version').to_i 79 custom_properties = {} 80 @card_data_by_number_and_version[data_key(card_number, card_version)] = { 81 :number => card_number, 82 :version => card_version, 83 :card_type_name => card_result.inner_text('card_type_name'), 84 :custom_properties => custom_properties 85 } 86 card_result.children.each do |child| 87 if child.tag_name.index("cp_") == 0 88 custom_properties[@custom_properties.property_name_for_column(child.tag_name)] = 89 nullable_value_from_element(child) 90 end 91 end 92 end 93 end
# File lib/mingle_events/processors/card_data.rb 95 def load_card_data_for_event(card_event) 96 begin 97 page_xml = @mingle_access.fetch_page(card_event.card_version_resource_uri) 98 doc = Xml.parse(page_xml) 99 custom_properties = {} 100 result = { 101 :number => card_event.card_number, 102 :version => card_event.version, 103 :card_type_name => doc.select('/card/card_type/name').inner_text, 104 :custom_properties => custom_properties 105 } 106 doc.select_all('/card/properties/property').each do |property| 107 custom_properties[property.inner_text('name')] = 108 nullable_value_from_element(property.select('value')) 109 end 110 111 result 112 rescue HttpError => httpError 113 raise httpError unless httpError.not_found? 114 115 end 116 end
# File lib/mingle_events/processors/card_data.rb 118 def nullable_value_from_element(element) 119 element.attr('nil') == 'true' ? nil : element.inner_text 120 end