isbm_adaptor 1.0.rc8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +24 -0
- data/lib/isbm_adaptor/channel.rb +30 -0
- data/lib/isbm_adaptor/channel_management.rb +83 -0
- data/lib/isbm_adaptor/consumer_publication.rb +95 -0
- data/lib/isbm_adaptor/duration.rb +69 -0
- data/lib/isbm_adaptor/message.rb +20 -0
- data/lib/isbm_adaptor/provider_publication.rb +94 -0
- data/lib/isbm_adaptor/service.rb +42 -0
- data/lib/isbm_adaptor/version.rb +3 -0
- data/lib/isbm_adaptor.rb +10 -0
- data/wsdls/ISBMChannelManagementService.wsdl +377 -0
- data/wsdls/ISBMConsumerPublicationService.wsdl +294 -0
- data/wsdls/ISBMProviderPublicationService.wsdl +292 -0
- metadata +170 -0
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Assetricity ISBM-Adaptor Gem
|
2
|
+

|
3
|
+
|
4
|
+
Copyright 2013 Assetricity, LLC
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module IsbmAdaptor
|
2
|
+
class Channel
|
3
|
+
TYPES = ['Publication', 'Request']
|
4
|
+
|
5
|
+
attr_accessor :uri, :type, :description
|
6
|
+
|
7
|
+
# Creates a new Channel.
|
8
|
+
#
|
9
|
+
# @param uri [String] the channel URI
|
10
|
+
# @param type [String] the channel type, either 'Publication' or 'Request'
|
11
|
+
# @param description [String] the channel description
|
12
|
+
def initialize(uri, type, description)
|
13
|
+
@uri = uri.to_s
|
14
|
+
@type = type
|
15
|
+
@description = description.to_s unless description.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a new Channel based on a hash.
|
19
|
+
#
|
20
|
+
# @options hash [String] :channel_uri the channel URI
|
21
|
+
# @options hash [String] :channel_type the channel type, either 'Publication' or 'Request'
|
22
|
+
# @options hash [String] :channel_description the channel description
|
23
|
+
def self.from_hash(hash)
|
24
|
+
uri = hash[:channel_uri]
|
25
|
+
type = hash[:channel_type]
|
26
|
+
description = hash[:channel_description]
|
27
|
+
new(uri, type, description)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'isbm_adaptor/service'
|
2
|
+
require 'isbm_adaptor/channel'
|
3
|
+
|
4
|
+
module IsbmAdaptor
|
5
|
+
class ChannelManagement
|
6
|
+
include IsbmAdaptor::Service
|
7
|
+
|
8
|
+
# Creates a new ISBM ChannelManagement client.
|
9
|
+
#
|
10
|
+
# @param endpoint [String] the SOAP endpoint URI
|
11
|
+
# @option options [Object] :logger (Rails.logger or $stdout) location where log should be output
|
12
|
+
# @option options [Boolean] :log (true) specify whether requests are logged
|
13
|
+
# @option options [Boolean] :pretty_print_xml (false) specify whether request and response XML are formatted
|
14
|
+
def initialize(endpoint, options = {})
|
15
|
+
options[:wsdl] = wsdl_dir + 'ISBMChannelManagementService.wsdl'
|
16
|
+
options[:endpoint] = endpoint
|
17
|
+
default_savon_options(options)
|
18
|
+
@client = Savon.client(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates a new channel.
|
22
|
+
#
|
23
|
+
# @param uri [String] the channel URI
|
24
|
+
# @param type [Symbol] the channel type, either publication or request (symbol or titleized string)
|
25
|
+
# @param description [String] the channel description, defaults to nil
|
26
|
+
# @return [void]
|
27
|
+
# @raise [ArgumentError] if uri or type are nil/empty or type is not a valid Symbol
|
28
|
+
def create_channel(uri, type, description = nil)
|
29
|
+
validate_presence_of uri, 'Channel URI'
|
30
|
+
validate_presence_of type, 'Channel Type'
|
31
|
+
channel_type = type.to_s.downcase.capitalize
|
32
|
+
raise ArgumentError, "#{channel_type} is not a valid type. Must be either Publication or Request." unless IsbmAdaptor::Channel::TYPES.include?(channel_type)
|
33
|
+
|
34
|
+
message = { 'ChannelURI' => uri,
|
35
|
+
'ChannelType' => channel_type }
|
36
|
+
message['ChannelDescription'] = description unless description.nil?
|
37
|
+
|
38
|
+
@client.call(:create_channel, message: message)
|
39
|
+
|
40
|
+
return true
|
41
|
+
end
|
42
|
+
|
43
|
+
# Deletes the specified channel.
|
44
|
+
#
|
45
|
+
# @param uri [String] the channel URI
|
46
|
+
# @return [void]
|
47
|
+
# @raise [ArgumentError] if uri is nil/empty
|
48
|
+
def delete_channel(uri)
|
49
|
+
validate_presence_of uri, 'Channel URI'
|
50
|
+
|
51
|
+
@client.call(:delete_channel, message: { 'ChannelURI' => uri })
|
52
|
+
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
|
56
|
+
# Gets information about the specified channel
|
57
|
+
#
|
58
|
+
# @param uri [String] the channel URI
|
59
|
+
# @return [Channel] the queried channel
|
60
|
+
# @raise [ArgumentError] if uri is nil/empty
|
61
|
+
def get_channel(uri)
|
62
|
+
validate_presence_of uri, 'Channel URI'
|
63
|
+
|
64
|
+
response = @client.call(:get_channel, message: { 'ChannelURI' => uri })
|
65
|
+
|
66
|
+
hash = response.to_hash[:get_channel_response][:channel]
|
67
|
+
IsbmAdaptor::Channel.from_hash(hash)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Gets information about all channels
|
71
|
+
#
|
72
|
+
# @return [Array<Channel>] all channels on the ISBM
|
73
|
+
def get_channels
|
74
|
+
response = @client.call(:get_channels)
|
75
|
+
|
76
|
+
channels = response.to_hash[:get_channels_response][:channel]
|
77
|
+
channels = [channels].compact unless channels.is_a?(Array)
|
78
|
+
channels.map do |hash|
|
79
|
+
IsbmAdaptor::Channel.from_hash(hash)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'isbm_adaptor/service'
|
2
|
+
require 'isbm_adaptor/message'
|
3
|
+
|
4
|
+
module IsbmAdaptor
|
5
|
+
class ConsumerPublication
|
6
|
+
include IsbmAdaptor::Service
|
7
|
+
|
8
|
+
# Creates a new ISBM ConsumerPublication client.
|
9
|
+
#
|
10
|
+
# @param endpoint [String] the SOAP endpoint URI
|
11
|
+
# @option options [Object] :logger (Rails.logger or $stdout) location where log should be output
|
12
|
+
# @option options [Boolean] :log (true) specify whether requests are logged
|
13
|
+
# @option options [Boolean] :pretty_print_xml (false) specify whether request and response XML are formatted
|
14
|
+
def initialize(endpoint, options = {})
|
15
|
+
options[:wsdl] = wsdl_dir + 'ISBMConsumerPublicationService.wsdl'
|
16
|
+
options[:endpoint] = endpoint
|
17
|
+
default_savon_options(options)
|
18
|
+
@client = Savon.client(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Opens a subscription session for a channel.
|
22
|
+
#
|
23
|
+
# @param topics [Array<String>] an array of topics
|
24
|
+
# @return [String] the session id
|
25
|
+
# @raise [ArgumentError] if uri or topics are nil/empty
|
26
|
+
def open_session(uri, topics, listener_uri = nil)
|
27
|
+
validate_presence_of uri, 'Channel URI'
|
28
|
+
validate_presence_of topics, 'Topics'
|
29
|
+
|
30
|
+
# Use Builder to generate XML body as we may have multiple Topic elements
|
31
|
+
xml = Builder::XmlMarkup.new
|
32
|
+
xml.isbm :ChannelURI, uri
|
33
|
+
topics.each do |topic|
|
34
|
+
xml.isbm :Topic, topic
|
35
|
+
end
|
36
|
+
xml.isbm :ListenerURI, listener_uri unless listener_uri.nil?
|
37
|
+
|
38
|
+
response = @client.call(:open_subscription_session, message: xml.target!)
|
39
|
+
|
40
|
+
response.to_hash[:open_subscription_session_response][:session_id].to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reads the first message after the specified last message in the message
|
44
|
+
# queue.
|
45
|
+
#
|
46
|
+
# @param session_id [String] the session id
|
47
|
+
# @param last_message_id [String] the id of the last message. When set to
|
48
|
+
# nil, returns the first publication in the message queue
|
49
|
+
# @return [Message] first message after specified last message. nil if no message.
|
50
|
+
# @raise [ArgumentError] if session_id is nil/empty
|
51
|
+
def read_publication(session_id, last_message_id)
|
52
|
+
validate_presence_of session_id, 'Session Id'
|
53
|
+
|
54
|
+
message = { 'SessionID' => session_id }
|
55
|
+
message['LastMessageID'] = last_message_id unless last_message_id.nil?
|
56
|
+
|
57
|
+
response = @client.call(:read_publication, message: message)
|
58
|
+
|
59
|
+
hash = response.to_hash[:read_publication_response][:publication_message]
|
60
|
+
message = nil
|
61
|
+
if hash
|
62
|
+
id = hash[:message_id]
|
63
|
+
topics = hash[:topic]
|
64
|
+
|
65
|
+
# Extract the child element in message content
|
66
|
+
# //isbm:ReadPublicationResponse/isbm:PublicationMessage/isbm:MessageContent/child::*
|
67
|
+
content = response.doc.root.element_children.first.element_children.first.element_children.first.element_children[1].element_children.first
|
68
|
+
|
69
|
+
# Retain any ancestor namespaces in case they are applicable for the element and/or children
|
70
|
+
# This is because content#to_xml does not output ancestor namespaces
|
71
|
+
content.namespaces.each do |key, value|
|
72
|
+
prefix = key.gsub(/xmlns:?/, '')
|
73
|
+
prefix = nil if prefix.empty?
|
74
|
+
content.add_namespace_definition(prefix, value)
|
75
|
+
end
|
76
|
+
|
77
|
+
message = IsbmAdaptor::Message.new(id, content, topics)
|
78
|
+
end
|
79
|
+
message
|
80
|
+
end
|
81
|
+
|
82
|
+
# Closes a subscription session.
|
83
|
+
#
|
84
|
+
# @param session_id [String] the session id
|
85
|
+
# @return [void]
|
86
|
+
# @raise [ArgumentError] if session_id is nil/empty
|
87
|
+
def close_session(session_id)
|
88
|
+
validate_presence_of session_id, 'Session Id'
|
89
|
+
|
90
|
+
@client.call(:close_subscription_session, message: { 'SessionID' => session_id })
|
91
|
+
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module IsbmAdaptor
|
2
|
+
class Duration
|
3
|
+
attr_accessor :years, :months, :days, :hours, :minutes, :seconds
|
4
|
+
|
5
|
+
# Creates a new Duration based on specified time components.
|
6
|
+
#
|
7
|
+
# @option duration [Numeric] :years duration in years
|
8
|
+
# @option duration [Numeric] :months duration in months
|
9
|
+
# @option duration [Numeric] :days duration in days
|
10
|
+
# @option duration [Numeric] :hours duration in hours
|
11
|
+
# @option duration [Numeric] :minutes duration in minutes
|
12
|
+
# @option duration [Numeric] :seconds duration in seconds
|
13
|
+
def initialize(duration)
|
14
|
+
duration.keys.each do |key|
|
15
|
+
raise ArgumentError.new "Invalid key: #{key}" unless VALID_SYMBOLS.include?(key)
|
16
|
+
end
|
17
|
+
|
18
|
+
duration.each do |key, value|
|
19
|
+
raise ArgumentError.new "Value for #{key} cannot be less than 0" if value < 0
|
20
|
+
end
|
21
|
+
|
22
|
+
@years = duration[:years]
|
23
|
+
@months = duration[:months]
|
24
|
+
@days = duration[:days]
|
25
|
+
@hours = duration[:hours]
|
26
|
+
@minutes = duration[:minutes]
|
27
|
+
@seconds = duration[:seconds]
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String] ISO 8601 formatted duration
|
31
|
+
def to_s
|
32
|
+
date = []
|
33
|
+
date << "#{@years}Y" unless @years.nil?
|
34
|
+
date << "#{@months}M" unless @months.nil?
|
35
|
+
date << "#{@days}D" unless @days.nil?
|
36
|
+
|
37
|
+
time = []
|
38
|
+
time << "#{@hours}H" unless @hours.nil?
|
39
|
+
time << "#{@minutes}M" unless @minutes.nil?
|
40
|
+
time << "#{@seconds}S" unless @seconds.nil?
|
41
|
+
|
42
|
+
result = nil
|
43
|
+
|
44
|
+
if !date.empty? || !time.empty?
|
45
|
+
result = 'P'
|
46
|
+
result += date.join unless date.empty?
|
47
|
+
result += 'T' + time.join unless time.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Hash] all specified time components
|
54
|
+
def to_hash
|
55
|
+
hash = {}
|
56
|
+
hash.merge!(years: @years) if @years
|
57
|
+
hash.merge!(months: @months) if @months
|
58
|
+
hash.merge!(days: @days) if @days
|
59
|
+
hash.merge!(hours: @hours) if @hours
|
60
|
+
hash.merge!(minutes: @minutes) if @minutes
|
61
|
+
hash.merge!(seconds: @seconds) if @seconds
|
62
|
+
hash
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
VALID_SYMBOLS = [:years, :months, :days, :hours, :minutes, :seconds]
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module IsbmAdaptor
|
2
|
+
class Message
|
3
|
+
attr_accessor :id, :content, :topics
|
4
|
+
|
5
|
+
# Creates a new ISBM Message container.
|
6
|
+
#
|
7
|
+
# @param id [String] message id
|
8
|
+
# @param content [String] XML content
|
9
|
+
# @param topics [Array<String>] collection of topics
|
10
|
+
def initialize(id, content, topics)
|
11
|
+
@id = id.to_s
|
12
|
+
@content = content
|
13
|
+
if (topics.is_a?(Array))
|
14
|
+
@topics.each { |t| t.to_s }
|
15
|
+
else
|
16
|
+
@topics = [topics.to_s]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'isbm_adaptor/service'
|
2
|
+
require 'isbm_adaptor/duration'
|
3
|
+
|
4
|
+
module IsbmAdaptor
|
5
|
+
class ProviderPublication
|
6
|
+
include IsbmAdaptor::Service
|
7
|
+
|
8
|
+
# Creates a new ISBM ProviderPublication client.
|
9
|
+
#
|
10
|
+
# @param endpoint [String] the SOAP endpoint URI
|
11
|
+
# @option options [Object] :logger (Rails.logger or $stdout) location where log should be output
|
12
|
+
# @option options [Boolean] :log (true) specify whether requests are logged
|
13
|
+
# @option options [Boolean] :pretty_print_xml (false) specify whether request and response XML are formatted
|
14
|
+
def initialize(endpoint, options = {})
|
15
|
+
options[:wsdl] = wsdl_dir + 'ISBMProviderPublicationService.wsdl'
|
16
|
+
options[:endpoint] = endpoint
|
17
|
+
default_savon_options(options)
|
18
|
+
@client = Savon.client(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Opens a publication session for a channel.
|
22
|
+
#
|
23
|
+
# @param [String] uri the channel URI
|
24
|
+
# @return [String] the session id
|
25
|
+
# @raise [ArgumentError] if uri is nil/empty
|
26
|
+
def open_session(uri)
|
27
|
+
validate_presence_of uri, 'Channel URI'
|
28
|
+
|
29
|
+
response = @client.call(:open_publication_session, message: { 'ChannelURI' => uri })
|
30
|
+
|
31
|
+
response.to_hash[:open_publication_session_response][:session_id].to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
# Posts a publication message.
|
35
|
+
#
|
36
|
+
# @param content [String] a valid XML string as message contents
|
37
|
+
# @param topics [Array<String>, String] a collection of topics or single topic
|
38
|
+
# @param expiry [Duration] when the message should expire
|
39
|
+
# @return [String] the message id
|
40
|
+
# @raise [ArgumentError] if session_id, content, topics is nil/empty or content is not valid XML
|
41
|
+
def post_publication(session_id, content, topics, expiry = nil)
|
42
|
+
validate_presence_of session_id, 'Session Id'
|
43
|
+
validate_presence_of content, 'Content'
|
44
|
+
validate_presence_of topics, 'Topics'
|
45
|
+
validate_xml content
|
46
|
+
|
47
|
+
topics = [topics] unless topics.is_a?(Array)
|
48
|
+
|
49
|
+
# Use Builder to generate XML body as we need to concatenate XML message content
|
50
|
+
xml = Builder::XmlMarkup.new
|
51
|
+
xml.isbm :SessionID, session_id
|
52
|
+
xml.isbm :MessageContent do
|
53
|
+
xml << content
|
54
|
+
end
|
55
|
+
topics.each do |topic|
|
56
|
+
xml.isbm :Topic, topic
|
57
|
+
end
|
58
|
+
duration = expiry.to_s
|
59
|
+
xml.isbm :Expiry, duration unless duration.nil?
|
60
|
+
|
61
|
+
response = @client.call(:post_publication, message: xml.target!)
|
62
|
+
|
63
|
+
response.to_hash[:post_publication_response][:message_id].to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
# Expires a posted publication message.
|
67
|
+
#
|
68
|
+
# @param session_id [String] the session id used to post the publication
|
69
|
+
# @param message_id [String] the message id received after posting the publication
|
70
|
+
# @return [void]
|
71
|
+
# @raise [ArgumentError] if session_id or message_id are nil/empty
|
72
|
+
def expire_publication(session_id, message_id)
|
73
|
+
validate_presence_of session_id, 'Session Id'
|
74
|
+
validate_presence_of message_id, 'Message Id'
|
75
|
+
|
76
|
+
@client.call(:expire_publication, message: { 'SessionID' => session_id, 'MessageID' => message_id })
|
77
|
+
|
78
|
+
return true
|
79
|
+
end
|
80
|
+
|
81
|
+
# Closes a publication session.
|
82
|
+
#
|
83
|
+
# @param session_id [String] the session id
|
84
|
+
# @return [void]
|
85
|
+
# @raise [ArgumentError] if session_id is nil/empty
|
86
|
+
def close_session(session_id)
|
87
|
+
validate_presence_of session_id, 'Session Id'
|
88
|
+
|
89
|
+
@client.call(:close_publication_session, message: { 'SessionID' => session_id })
|
90
|
+
|
91
|
+
return true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module IsbmAdaptor
|
2
|
+
module Service
|
3
|
+
# Indicates the directory of the ISBM WSDL files
|
4
|
+
#
|
5
|
+
# @return [String] directory of ISBM WSDL files
|
6
|
+
def wsdl_dir
|
7
|
+
File.expand_path(File.dirname(__FILE__)) + '/../../wsdls/'
|
8
|
+
end
|
9
|
+
|
10
|
+
# Sets default values for certain Savon options.
|
11
|
+
#
|
12
|
+
# @param options [Hash] the options to set defaults on
|
13
|
+
# @return [Hash] options hash with defaults set
|
14
|
+
def default_savon_options(options)
|
15
|
+
options[:logger] = Rails.logger if defined?(Rails)
|
16
|
+
options[:log] ||= true
|
17
|
+
options[:pretty_print_xml] ||= false
|
18
|
+
end
|
19
|
+
|
20
|
+
# Validates the presence of the passed value.
|
21
|
+
#
|
22
|
+
# @param value [Object] presence of object to validate
|
23
|
+
# @param name [String] name of value to include in error message if not present
|
24
|
+
# @return [void]
|
25
|
+
# @raises [ArgumentError] if value is not present
|
26
|
+
def validate_presence_of(value, name)
|
27
|
+
if value.blank?
|
28
|
+
raise ArgumentError, "#{name} must be specified"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Validates the well formedness of the XML string and raises an error if
|
33
|
+
# any errors are encountered.
|
34
|
+
#
|
35
|
+
# @param xml [String] the XML string to parse
|
36
|
+
# @return [void]
|
37
|
+
def validate_xml(xml)
|
38
|
+
doc = Nokogiri.XML(xml)
|
39
|
+
raise ArgumentError, "XML is not well formed: #{xml}" unless doc.errors.empty?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/isbm_adaptor.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'isbm_adaptor/version'
|
3
|
+
require 'savon'
|
4
|
+
|
5
|
+
module IsbmAdaptor
|
6
|
+
autoload :ChannelManagement, 'isbm_adaptor/channel_management'
|
7
|
+
autoload :ProviderPublication, 'isbm_adaptor/provider_publication'
|
8
|
+
autoload :ConsumerPublication, 'isbm_adaptor/consumer_publication'
|
9
|
+
autoload :Duration, 'isbm_adaptor/duration'
|
10
|
+
end
|