CCHIT-xds-facade 0.1.1

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.
Files changed (42) hide show
  1. data/lib/apache-mime4j-0.5.jar +0 -0
  2. data/lib/commons-codec-1.3.jar +0 -0
  3. data/lib/commons-httpclient-3.1.jar +0 -0
  4. data/lib/commons-logging-1.1.1.jar +0 -0
  5. data/lib/mime/mime_message_parser.rb +52 -0
  6. data/lib/xds-facade.rb +77 -0
  7. data/lib/xds/affinity_domain_config.rb +42 -0
  8. data/lib/xds/author.rb +33 -0
  9. data/lib/xds/coded_attribute.rb +44 -0
  10. data/lib/xds/document_registry_service.rb +26 -0
  11. data/lib/xds/document_repository_service.rb +27 -0
  12. data/lib/xds/helper.rb +81 -0
  13. data/lib/xds/metadata.rb +179 -0
  14. data/lib/xds/mtom_xop_request.rb +29 -0
  15. data/lib/xds/provide_and_register_document_set_b.rb +29 -0
  16. data/lib/xds/provide_and_register_document_set_b_xop.rb +58 -0
  17. data/lib/xds/provide_and_register_document_set_b_xop_response.rb +45 -0
  18. data/lib/xds/registry_stored_query_request.rb +38 -0
  19. data/lib/xds/registry_stored_query_response.rb +31 -0
  20. data/lib/xds/retrieve_document_set_request.rb +66 -0
  21. data/lib/xds/retrieve_document_set_response.rb +41 -0
  22. data/lib/xds/source_patient_info.rb +52 -0
  23. data/lib/xds/xds_header.rb +26 -0
  24. data/lib/xds/xds_part.rb +44 -0
  25. data/lib/xds/xds_request.rb +55 -0
  26. data/lib/xds/xds_request_entity.rb +14 -0
  27. data/test/factories.rb +37 -0
  28. data/test/mime/mime_message_parser_test.rb +25 -0
  29. data/test/test_helper.rb +39 -0
  30. data/test/xds/affinity_domain_config_test.rb +26 -0
  31. data/test/xds/author_test.rb +30 -0
  32. data/test/xds/coded_attribute_test.rb +38 -0
  33. data/test/xds/metadata_test.rb +60 -0
  34. data/test/xds/provide_and_register_document_set_b_test.rb +31 -0
  35. data/test/xds/registry_stored_query_request_test.rb +32 -0
  36. data/test/xds/registry_stored_query_response_test.rb +28 -0
  37. data/test/xds/retrieve_document_set_request_test.rb +32 -0
  38. data/test/xds/retrieve_document_set_response_test.rb +43 -0
  39. data/test/xds/source_patient_info_test.rb +59 -0
  40. data/test/xds/xds_header.rb +20 -0
  41. data/test/xds/xml_helper_test.rb +124 -0
  42. metadata +122 -0
@@ -0,0 +1,29 @@
1
+ module XDS
2
+ class MTOMXopRequest < XdsRequest
3
+
4
+ def execute
5
+ client = create_client
6
+ parts = get_parts.to_java(Part)
7
+ post = PostMethod.new(endpoint_uri)
8
+
9
+ ent = XdsRequestEntity.new(parts, post.get_params)
10
+ ent.type="application/xop+xml"
11
+ ent.start=parts[0].id
12
+ ent.start_info="application/soap+xml"
13
+ ent.action=@header.action
14
+
15
+ post.set_request_entity(ent)
16
+ post.content_chunked=true
17
+ client.executeMethod(post)
18
+ post
19
+ end
20
+
21
+
22
+ def get_parts
23
+ raise "Impement me"
24
+ end
25
+
26
+ end
27
+
28
+
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'base64'
2
+ module XDS
3
+ class ProvideAndRegisterDocumentSetB < XdsRequest
4
+
5
+
6
+ def initialize(service_url,metadata,document)
7
+ super(service_url,"urn:ihe:iti:2007:ProvideAndRegisterDocumentSet-b")
8
+ @metadata = metadata
9
+ @document = document
10
+ end
11
+
12
+
13
+ def to_soap_body(builder, attributes={})
14
+ @metadata.id = "urn:uid:#{UUID.new.generate}" unless @metadata.id
15
+ builder.soapenv(:Body, attributes) do
16
+ builder.xdsb(:ProvideAndRegisterDocumentSetRequest,"xmlns:lcm"=>"urn:oasis:names:tc:ebxml-regrep:xsd:lcm:3.0",
17
+ "xmlns:rim"=>"urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0",
18
+ "xmlns:xdsb"=>"urn:ihe:iti:xds-b:2007") do
19
+ builder.lcm(:SubmitObjectsRequest) do
20
+ @metadata.to_soap(builder)
21
+ end
22
+
23
+ builder.xdsb(:Document, Base64.b64encode(@document),"id"=>@metadata.id)
24
+
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,58 @@
1
+ module XDS
2
+ require 'base64'
3
+ class ProvideAndRegisterDocumentSetBXop < MTOMXopRequest
4
+
5
+ def initialize(service_url,metadata,document)
6
+ super(service_url,"urn:ihe:iti:2007:ProvideAndRegisterDocumentSet-b")
7
+ @metadata = metadata
8
+ @document = document
9
+ end
10
+
11
+
12
+ def to_soap_body(builder, attributes={})
13
+ @metadata.id = "urn:uid:#{UUID.new.generate}" unless @metadata.id
14
+ builder.soapenv(:Body, attributes) do
15
+ builder.xdsb(:ProvideAndRegisterDocumentSetRequest,"xmlns:lcm"=>"urn:oasis:names:tc:ebxml-regrep:xsd:lcm:3.0",
16
+ "xmlns:rim"=>"urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0",
17
+ "xmlns:xdsb"=>"urn:ihe:iti:xds-b:2007") do
18
+ builder.lcm(:SubmitObjectsRequest) do
19
+
20
+ @metadata.to_soap(builder)
21
+
22
+ end
23
+ builder.xdsb(:Document, {"id"=>@metadata.id}, Base64.b64encode(@document) )
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+
30
+ def execute
31
+ post = super
32
+ XDS::ProvideAndRegisterDocumentSetBXopResponse.new(self,post)
33
+ end
34
+
35
+ def get_parts
36
+ body_part = XdsPart.new("body", to_soap)
37
+ body_part.char_set="UTF-8"
38
+ body_part.id = get_body_part_id
39
+ body_part.set_content_type(%{application/xop+xml; type="application/soap+xml"})
40
+ [body_part]
41
+ #document_part = XdsPart.new("document",@document.to_s)
42
+ # document_part.char_set="UTF-8"
43
+ # document_part.id=get_document_part_id
44
+ # [body_part,document_part]
45
+ end
46
+
47
+ private
48
+
49
+ def get_document_part_id
50
+ @document_part_id ||= "urn:uid:#{UUID.new.generate}"
51
+ end
52
+
53
+ def get_body_part_id
54
+ @body_part_id ||= "urn:uid:#{UUID.new.generate}"
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,45 @@
1
+ module XDS
2
+ class ProvideAndRegisterDocumentSetBXopResponse
3
+ attr_accessor :parts
4
+ def initialize(request,post)
5
+ @request = request
6
+ @parts = MIME::MimeMessageParser.parse(post.get_response_body_as_stream,
7
+ post.get_response_header("Content-Type").value)
8
+ parse_response
9
+ end
10
+
11
+ def status
12
+ @status
13
+ end
14
+
15
+ def errors
16
+ @errors
17
+ end
18
+
19
+ def success?
20
+ @status == "urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success"
21
+ end
22
+
23
+ def soap_resp
24
+ @soap_resp
25
+ end
26
+
27
+ private
28
+ def parse_response
29
+ @soap_resp = @parts.first[:content]
30
+ @response_document = REXML::Document.new(soap_resp)
31
+ @response_element = REXML::XPath.first(@response_document,"/soapenv:Envelope/soapenv:Body/rs:RegistryResponse",COMMON_NAMESPACES)
32
+ @status=@response_element.attributes["status"]
33
+ @errors = REXML::XPath.match(@response_element,"./rs:RegistryErrorList/rs:RegistryError",COMMON_NAMESPACES).collect do |err|
34
+ {:code_context=>err.attributes["codeContext"],
35
+ :error_code=>err.attributes["errorCode"],
36
+ :severity=>err.attributes["severity"] ,
37
+ :location=>err.attributes["location"],
38
+ :text=>err.text
39
+ }
40
+ end
41
+ @slot_list = REXML::XPath.first(@response_element,"/rs:ResponseSlotList",COMMON_NAMESPACES)
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ module XDS
2
+ class RegistryStoredQueryRequest < XdsRequest
3
+ include Helper
4
+
5
+ def initialize(service_url, query)
6
+ super(service_url,"urn:ihe:iti:2007:RegistryStoredQuery")
7
+ @query = query
8
+ end
9
+
10
+ def execute
11
+ post = super
12
+ rsqr = RegistryStoredQueryResponse.new
13
+ rsqr.parse_soap_response(post.response_body_as_string)
14
+ if rsqr.request_successful?
15
+ return rsqr.retrieved_metadata
16
+ else
17
+ return nil
18
+ end
19
+ end
20
+
21
+ def to_soap_body(builder,body_attributes = {})
22
+ builder.soapenv(:Body, body_attributes) do
23
+ builder.query(:AdhocQueryRequest, 'xmlns:query' => "urn:oasis:names:tc:ebxml-regrep:xsd:query:3.0",
24
+ 'xmlns:rs' => "urn:oasis:names:tc:ebxml-regrep:xsd:rs:3.0",
25
+ 'xmlns' => "urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0") do
26
+ builder.query(:ResponseOption, 'returnComposedObjects' => "true", 'returnType' => "LeafClass")
27
+ # Currently hardcoded to FindDocuments
28
+ builder.AdhocQuery('id' => "urn:uuid:14d4debf-8f97-4251-9a74-a90016b0af0d") do
29
+ @query.each_pair do |slot_name, values|
30
+ packed_values = values.kind_of?(Array) ? values : [values]
31
+ create_slot(builder, slot_name, packed_values)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module XDS
2
+ class RegistryStoredQueryResponse
3
+ attr_reader :retrieved_metadata
4
+
5
+ def initialize
6
+ @retrieved_metadata = []
7
+ @request_successful = false
8
+ end
9
+
10
+ def request_successful?
11
+ @request_successful
12
+ end
13
+
14
+ def parse_soap_response(soap_xml)
15
+ doc = REXML::Document.new(StringIO.new(soap_xml))
16
+ status = REXML::XPath.first(doc,
17
+ '/soapenv:Envelope/soapenv:Body/query:AdhocQueryResponse/@status',
18
+ COMMON_NAMESPACES)
19
+ if status.to_s.eql?('urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success')
20
+ @request_successful = true
21
+ REXML::XPath.each(doc,
22
+ '/soapenv:Envelope/soapenv:Body/query:AdhocQueryResponse/rim:RegistryObjectList/rim:ExtrinsicObject',
23
+ COMMON_NAMESPACES) do |eo|
24
+ metadata = Metadata.new
25
+ metadata.load_from_extrinsic_object(eo)
26
+ @retrieved_metadata << metadata
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,66 @@
1
+ module XDS
2
+ class RetrieveDocumentSetRequest < MTOMXopRequest
3
+
4
+ def initialize(service_url, doc_ids = [])
5
+ super(service_url,"urn:ihe:iti:2007:RetrieveDocumentSet")
6
+ @doc_ids = doc_ids
7
+ end
8
+
9
+ def add_ids_to_request(repository_unique_id, document_unique_id)
10
+ @doc_ids << {:repository_unique_id => repository_unique_id,
11
+ :document_unique_id => document_unique_id}
12
+ end
13
+
14
+ # Returns an array of hashes, with a hash for each document returned.
15
+ # The hash will contain
16
+ # * <tt>:repository_unique_id</tt> - The Repository Unique Id for the document
17
+ # * <tt>:document_unique_id</tt> - The Document Unique Id for the document
18
+ # * <tt>:content</tt> - The document as a String
19
+ def execute
20
+ post = super
21
+ parts = MIME::MimeMessageParser.parse(post.get_response_body_as_stream,
22
+ post.get_response_header("Content-Type").value)
23
+ rdsr = XDS::RetrieveDocumentSetResponse.new
24
+ rdsr.parse_soap_response(parts.first[:content])
25
+ if rdsr.request_successful?
26
+ docs = []
27
+ rdsr.retrieved_documents.each do |rd|
28
+ # hacky yes!, if rd has a content_id that means the response sent the document back as a separate entry in the multipart doc
29
+ # else it sent it back as a base64 encoded string that has already been parsed by the rd object and set in :content of the returned hash
30
+ part = rd[:content_id] ? parts.find {|candidate_part| candidate_part[:content_id].eql?('<' + rd[:content_id] + '>')} : rd
31
+ doc = {}
32
+ doc[:repository_unique_id] = rd[:repository_unique_id]
33
+ doc[:document_unique_id] = rd[:document_unique_id]
34
+ doc[:content] = part[:content]
35
+ docs << doc
36
+ end
37
+
38
+ return docs
39
+ else
40
+ return false
41
+ end
42
+ end
43
+
44
+ def get_parts
45
+ body_part = XdsPart.new("body", to_soap)
46
+ body_part.char_set="UTF-8"
47
+ body_part.id = UUID.new.generate
48
+ body_part.set_content_type(%{application/xop+xml; type="application/soap+xml"})
49
+ [body_part]
50
+ end
51
+
52
+
53
+ def to_soap_body(builder,body_attributes = {})
54
+ builder.soapenv(:Body, body_attributes) do
55
+ builder.RetrieveDocumentSetRequest('xmlns' => "urn:ihe:iti:xds-b:2007") do
56
+ @doc_ids.each do |doc_id|
57
+ builder.DocumentRequest do
58
+ builder.RepositoryUniqueId(doc_id[:repository_unique_id])
59
+ builder.DocumentUniqueId(doc_id[:document_unique_id])
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,41 @@
1
+ module XDS
2
+ class RetrieveDocumentSetResponse
3
+ require 'base64'
4
+ attr_reader :retrieved_documents
5
+
6
+ def initialize
7
+ @retrieved_documents = []
8
+ @request_successful = false
9
+ end
10
+
11
+ def request_successful?
12
+ @request_successful
13
+ end
14
+
15
+ def parse_soap_response(soap_xml)
16
+ doc = REXML::Document.new(StringIO.new(soap_xml))
17
+ status = REXML::XPath.first(doc,
18
+ '/soapenv:Envelope/soapenv:Body/xdsb:RetrieveDocumentSetResponse/rs:RegistryResponse/@status',
19
+ COMMON_NAMESPACES)
20
+ if status.to_s.eql?('urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success')
21
+ @request_successful = true
22
+ REXML::XPath.each(doc,
23
+ '/soapenv:Envelope/soapenv:Body/xdsb:RetrieveDocumentSetResponse/xdsb:DocumentResponse',
24
+ COMMON_NAMESPACES) do |dr|
25
+ doc = {}
26
+ # need to check for include or whether the doc is just embedded in the soap message
27
+ inc = dr.elements['xdsb:Document/xop:Include']
28
+ #Nuke the 'cid:' from the front of the content id since it doesn't show up in the mime message headers
29
+ if inc
30
+ doc[:content_id] = dr.elements['xdsb:Document/xop:Include'].attributes['href'].to_s.sub('cid:', '') if dr.elements['xdsb:Document/xop:Include']
31
+ else
32
+ doc[:content] = Base64.decode64(dr.elements['xdsb:Document'].text)
33
+ end
34
+ doc[:repository_unique_id] = dr.elements['xdsb:RepositoryUniqueId'].text
35
+ doc[:document_unique_id] = dr.elements['xdsb:DocumentUniqueId'].text
36
+ @retrieved_documents << doc
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ module XDS
2
+ class SourcePatientInfo
3
+ include XDS::Helper
4
+ # These fields will need to be entered in HL7v2 format by the user
5
+ attr_accessor :source_patient_identifier, :name, :gender, :date_of_birth, :address
6
+
7
+ def initialize(args={})
8
+ @source_patient_identifier = args[:source_patient_identifier] || ""
9
+ @name = args[:name] || ""
10
+ @gender = args[:gender] || ""
11
+ @date_of_birth = args[:date_of_birth] || ""
12
+ @address = args[:address]
13
+ end
14
+
15
+ def to_soap(builder)
16
+ create_slot(builder,"sourcePatientInfo",value_list)
17
+ end
18
+
19
+ def from_extrinsic_object(eo_node)
20
+ patient_values = get_slot_values(eo_node, 'sourcePatientInfo')
21
+ @source_patient_identifier = match_and_strip(patient_values, 'PID-3')
22
+ @name = match_and_strip(patient_values, 'PID-5')
23
+ @gender = match_and_strip(patient_values, 'PID-8')
24
+ @date_of_birth = match_and_strip(patient_values, 'PID-7')
25
+ @address = match_and_strip(patient_values, 'PID-11')
26
+ end
27
+
28
+ def value_list
29
+ ["PID-3|#{@source_patient_identifier}",
30
+ "PID-5|#{@name}",
31
+ "PID-8|#{@gender}",
32
+ "PID-7|#{@date_of_birth}",
33
+ "PID-11|#{@address}"]
34
+
35
+ end
36
+
37
+ def match_and_strip(slot_values, pid_segment)
38
+ slot_value = slot_values.find {|field_value| field_value.match("^#{pid_segment}\\|.*")}
39
+ if slot_value
40
+ md = slot_value.match("^#{pid_segment}\\|(.+)")
41
+ if md
42
+ return md[1]
43
+ else
44
+ return nil
45
+ end
46
+ else
47
+ return nil
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,26 @@
1
+ module XDS
2
+ class XdsHeader
3
+ def self.new_message_id()
4
+ "urn:uuid:#{UUID.new.generate}"
5
+ end
6
+
7
+ attr_accessor :action
8
+ attr_accessor :endpoint_uri
9
+ @@namespace= {"xmlns" => "http://www.w3.org/2005/08/addressing"}
10
+
11
+ def initialize(endpoint,action,message_id = XdsHeader.new_message_id)
12
+ @action = action
13
+ @endpoint_uri = endpoint
14
+ @message_id = message_id
15
+ end
16
+
17
+ def to_soap(builder)
18
+ builder.soapenv(:Header) do
19
+ builder.wsa(:To, @endpoint_uri)
20
+ builder.wsa(:Action, @action)
21
+ builder.wsa(:MessageID, @message_id)
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,44 @@
1
+ module XDS
2
+ import "org.apache.commons.httpclient.util.EncodingUtil"
3
+ CRLF_BYTES = org.apache.commons.httpclient.util.EncodingUtil.getAsciiBytes("\r\n");
4
+
5
+ class XdsPart < org.apache.commons.httpclient.methods.multipart.StringPart
6
+
7
+ attr_accessor :id
8
+
9
+ def length
10
+ super + XDS::CRLF_BYTES.length + org.apache.commons.httpclient.util.EncodingUtil.get_ascii_bytes(get_id).length
11
+ end
12
+
13
+
14
+ def send_id(out)
15
+ out.write(XDS::CRLF_BYTES)
16
+ out.write(org.apache.commons.httpclient.util.EncodingUtil.get_ascii_bytes(get_id))
17
+ end
18
+
19
+ # /***
20
+ # 298 * Write all the data to the output stream.
21
+ # 299 * If you override this method make sure to override
22
+ # 300 * #length() as well
23
+ # 301 *
24
+ # 302 * @param out The output stream
25
+ # 303 * @throws IOException If an IO problem occurs.
26
+ # 304 */
27
+ def send( out)
28
+ sendStart(out);
29
+ sendDispositionHeader(out)
30
+ sendContentTypeHeader(out)
31
+ send_id(out)
32
+ sendTransferEncodingHeader(out)
33
+
34
+ sendEndOfHeader(out)
35
+ sendData(out)
36
+ sendEnd(out)
37
+ end
38
+
39
+ private
40
+ def get_id
41
+ "Content-ID: <#{@id}>"
42
+ end
43
+ end
44
+ end