class Argos::Soap
Constants
- ARGOS_NS
- NAMESPACES
- SOAP_NS
- URI
- WSDL
Alternative: “ws-argos.clsamerica.com/argosDws/services/DixService”
Attributes
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
username [String] password [String]
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
username [String] password [String]
client [Savon] (version 3) request [String] Soap:Envelope (XML request body) response [Savon::Response] operation [Savon::Operation] log [Logger] xml [String] (Extracted, inner) XML filter platformId [String] Comma-separated list of platforms programNumber [String] Comma-separated list of programs nbDaysFromNow period
Public Class Methods
Constructor soap = Argos::Soap.new
({username: “argos-system-user”, password: “argos-system-pw”})
# File lib/argos/soap.rb, line 39 def initialize(config={}) config.each do |k,v| case k.to_sym when :username @username=v when :password @password=v when :wsdl @wsdl=v when :programNumber @programNumber = v when :platformId @platformId = v when :nbDaysFromNow @nbDaysFromNow = v.to_i when :period @period = v when :filter @filter = v else #raise ArgumentError, "Unkown config key: #{k}" end end end
Public Instance Methods
Build baseRequest Hash The service requires programNumber or PlatformId, but if you do not provide any, this method will call the service (@see programs
) and get the current user’s programs @return [Hash]
# File lib/argos/soap.rb, line 68 def baseRequest # if override key is platformId... delete programNumber... # if override key is period... delete nbDaysFromNow... baseRequest = { username: _username, password: _password } # First choice (program or platform) if @programNumber.nil? and @platformId.nil? # Fetch all programs if neither is provided baseRequest[:programNumber] = programs.map {|p|p.to_s}.join(",") elsif @programNumber.to_s =~ /\d+/ and @platformId.to_s =~ /\d+/ baseRequest[:platformId] = @platformId # ignores programNumber elsif @programNumber.to_s =~ /\d+/ baseRequest[:programNumber] = @programNumber elsif @platformId.to_s =~ /\d+/ baseRequest[:platformId] = @platformId end # 2nd choice (time) if @nbDaysFromNow.nil? and @period.nil? # Default to 20 days of data (the maximum) baseRequest[:nbDaysFromNow] = 20 elsif @nbDaysFromNow =~ /\d+/ and not @period.nil? raise "Cannot provide both nbDaysFromNow and period" elsif @nbDaysFromNow.to_s =~ /\d+/ baseRequest[:nbDaysFromNow] = @nbDaysFromNow.to_i else baseRequest[:period] = @period end #baseRequest = baseRequest.merge({ # @todo #<xs:element minOccurs="0" name="referenceDate" type="tns:referenceDateType"/> #<xs:element minOccurs="0" name="locClass" type="xs:string"/> #<xs:element minOccurs="0" name="geographicArea" type="xs:string"/> #<xs:element minOccurs="0" name="compression" type="xs:int"/> #<xs:element minOccurs="0" name="mostRecentPassages" type="xs:boolean"/> #}) baseRequest end
# File lib/argos/soap.rb, line 129 def filter? not @filter.nil? and filter.respond_to?(:call) end
@return [String]
# File lib/argos/soap.rb, line 134 def getCsv o = _operation(:getCsv) o.body = { csvRequest: baseRequest.merge( showHeader: true).merge(xmlRequest) } @response = o.call @request = o.build # Handle faults (before extracting data) _envelope.xpath("soap:Body/soap:Fault", namespaces).each do | fault | raise fault.to_s end @text = _extract_escaped_xml("csvResponse").call(response) end
@return [Hash]
# File lib/argos/soap.rb, line 151 def getKml _call_xml_operation(:getKml, { kmlRequest: baseRequest.merge(xmlRequest)}, _extract_escaped_xml("kmlResponse")) end
@return [Text] choice: programNumber | platformId | wmo* nbMaxObs
# File lib/argos/soap.rb, line 214 def getObsCsv o = _operation(:getObsCsv) o.body = { observationRequest: baseRequest.merge(xmlRequest) } @response = o.call @request = o.build @text = _extract_escaped_xml("observationResponse").call(response) end
@return [Hash] choice: programNumber | platformId | wmo* nbMaxObs
# File lib/argos/soap.rb, line 205 def getObsXml _call_xml_operation(:getObsXml, { observationRequest: baseRequest.merge(xmlRequest)}, _extract_escaped_xml("observationResponse")) end
@return [Hash] {“data”:{“program”:[{“programNumber”:“9660”,“platform”:[{ .. },{ .. }]}],“@version”:“1.0”}} Each platform Hash (.. above): {“platformId”:“129990”,“lastLocationClass”:“3”,“lastCollectDate”:“2013-10-03T08:32:24.000Z”,“lastLocationDate”:“2013-05-22T04:55:15.000Z”,“lastLatitude”:“47.67801”,“lastLongitude”:“-122.13419”}
# File lib/argos/soap.rb, line 158 def getPlatformList platformList = _call_xml_operation(:getPlatformList, { platformListRequest: # Cannot use #baseRequest here because that methods calls #programs which also calls #getPlatformList... { username: _username, password: _password }, }, _extract_escaped_xml("platformListResponse")) # Raise error if no programs if platformList["data"]["program"].nil? raise platformList.to_json end # Force Arrays if not platformList["data"]["program"].is_a? Array platformList["data"]["program"] = [platformList["data"]["program"]] end platformList["data"]["program"].map! {|program| if program["platform"].is_a? Hash program["platform"] = [program["platform"]] end program } platformList end
@return [Hash]
# File lib/argos/soap.rb, line 191 def getStreamXml _call_xml_operation(:getStreamXml, { streamXmlRequest: baseRequest.merge(xmlRequest)}, _extract_motm) end
@return [Hash]
# File lib/argos/soap.rb, line 184 def getXml _call_xml_operation(:getXml, { xmlRequest: baseRequest.merge(xmlRequest)}, _extract_escaped_xml("xmlResponse")) end
@return [Hash]
# File lib/argos/soap.rb, line 198 def getXsd _call_xml_operation(:getXsd, { xsdRequest: {} }, _extract_escaped_xml("xsdResponse")) end
# File lib/argos/soap.rb, line 306 def namespaces NAMESPACES end
@return [Array] [:getCsv, :getStreamXml, :getKml, :getXml, :getXsd, :getPlatformList, :getObsCsv, :getObsXml]
# File lib/argos/soap.rb, line 302 def operations @response = client.operations(:DixService, :DixServicePort) end
Platforms: array of platformId integers @return [Array] of [Integer]
# File lib/argos/soap.rb, line 225 def platforms platforms = [] platformListPrograms = getPlatformList["data"]["program"] if @programNumber.to_s =~ /\d+/ platformListPrograms.select! {|p| p["programNumber"].to_i == @programNumber.to_i } end platformListPrograms.each do |program| if program.key?("platform") and not program["platform"].is_a?(Array) platforms << program["platform"]["platformId"].to_i else platforms += program["platform"].map {|p| p["platformId"].to_i} end end platforms end
Programs: Array of programNumber integers @return [Array]
# File lib/argos/soap.rb, line 251 def programs platformList = getPlatformList if platformList.key?("data") and platformList["data"].key?("program") platformList_data_program = platformList["data"]["program"].is_a?(Array) ? platformList["data"]["program"] : [platformList["data"]["program"]] platformList_data_program.map {|p| p["programNumber"].to_i } else raise platformList end end
@return [String]
# File lib/argos/soap.rb, line 266 def raw response.raw end
# File lib/argos/soap.rb, line 284 def schema Nokogiri::XML::Schema(File.read("#{__dir__}/_xsd/argos-data.xsd")) end
@return [Hash] {“DixService”:{“ports”:{“DixServicePort”:{“type”:“schemas.xmlsoap.org/wsdl/soap12/”,“location”:“http://ws-argos.cls.fr/argosDws/services/DixService”}}}}
# File lib/argos/soap.rb, line 280 def services client.services end
@return [String]
# File lib/argos/soap.rb, line 297 def text @text||="" end
# File lib/argos/soap.rb, line 288 def validate(xml) if xml.is_a? String xml = Nokogiri.XML(xml) end schema.validate(xml) end
# File lib/argos/soap.rb, line 310 def xmlRequest { displayLocation: true, displayDiagnostic: true, displayMessage: true, displayCollect: true, displayRawData: true, displaySensor: true, #argDistrib: "", displayImageLocation: true, displayHexId: true } end
Protected Instance Methods
Build and call @operation, set @response, @request, and @xml @raise on faults @return [Hash]
# File lib/argos/soap.rb, line 329 def _call_xml_operation(op_sym, body, extract=nil) @operation = _operation(op_sym) @operation.body = body @response = operation.call # Check for http errors? # Handle faults (before extracting data) _envelope.xpath("soap:Body/soap:Fault", namespaces).each do | fault | raise Exception, fault.to_s end # Extract data if extract.respond_to?(:call) @xml = extract.call(response) else @xml = response.raw end # Handle errors ng = Nokogiri.XML(xml) ng.xpath("/data/errors/error").each do | error | if error.key?("code") case error["code"].to_i when 4 raise NodataException end #<error code="2">max response reached</error> #<error code="3">authentification error</error> #<error code="9">start date upper than end date</error> else raise Exception, error end end # Validation - only :getXml if [:getXml].include? op_sym # Validation against getXSD schema does not work: ["Element 'data': No matching global declaration available for the validation root."] # See https://github.com/npolar/argos-ruby/commit/219e4b3761e5265f8f9e8b924bcfc23607902428 for the fix schema = Nokogiri::XML::Schema(File.read("#{__dir__}/_xsd/argos-data.xsd")) v = schema.validate(ng) if v.any? log.debug "#{v.size} errors: #{v.map{|v|v.to_s}.uniq.to_json}" end end # Convert XML to Hash nori = Nori.new nori.parse(xml) end
@return [Nokogiri:*]
# File lib/argos/soap.rb, line 413 def _envelope ng = Nokogiri.XML(response.raw).xpath("/soap:Envelope", namespaces) if not ng.any? # Again, this is a shame... envstr = '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">' extracted = envstr + response.raw.split(envstr)[1].split("</soap:Envelope>")[0] + "</soap:Envelope>" ng = Nokogiri.XML(extracted).xpath("/soap:Envelope", namespaces) end ng end
This is a shame, but who’s to blame when there’s multiple XML prologs (the second is escaped) and even mix of (declared) encodings (UTF-8 in soap envelope, ISO-8859-1 inside) Note: the inner data elements are non-namespaced (see da), so that recreating as proper XML would need to set xmlns=“”
# File lib/argos/soap.rb, line 384 def _extract_escaped_xml(responseElement) lambda {|response| CGI.unescapeHTML(response.raw.split("<#{responseElement} xmlns=\"http://service.dataxmldistribution.argos.cls.fr/types\"><return>")[1].split("</return>")[0])} end
@return [String]
# File lib/argos/soap.rb, line 394 def _extract_motm lambda {|response| # Scan for MOTM signature --uuid:* if response.raw =~ (/^(--[\w:-]+)--$/) # Get the last message, which is -2 because of the trailing -- xml = response.raw.split($1)[-2].strip # Get rid of HTTP headers if xml =~ /\r\n\r\n[<]/ xml = xml.split(/\r\n\r\n/)[-1] end else raise "Cannot parse MOTM" end } end
@return [Savon::Operation]
# File lib/argos/soap.rb, line 425 def _operation(operation_name) client.operation(:DixService, :DixServicePort, operation_name) end
@return [String]
# File lib/argos/soap.rb, line 435 def _password @password||=ENV["ARGOS_SOAP_PASSWORD"] end
@return [String]
# File lib/argos/soap.rb, line 430 def _username @username||=ENV["ARGOS_SOAP_USERNAME"] end