fhir_client 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/model/tag.rb ADDED
@@ -0,0 +1,57 @@
1
+ module FHIR
2
+ class Tag
3
+ # Each Tag is part of an HTTP header named "Category" with three parts: term, scheme, and label.
4
+ # Each Tag can be in an individual "Category" header, or they can all be concatentated (with comma
5
+ # separation) inside a single "Category" header.
6
+
7
+ # Term is a URI:
8
+ # General tags:
9
+ # Bundle / FHIR Documents: "http://hl7.org/fhir/tag/document"
10
+ # Bundle / FHIR Messages: "http://hl7.org/fhir/tag/message"
11
+ # Profile tags: URL that references a profile resource.
12
+ attr_accessor :term
13
+
14
+ # Scheme is a URI:
15
+ # "http://hl7.org/fhir/tag" A general tag
16
+ # "http://hl7.org/fhir/tag/profile" A profile tag - a claim that the Resource conforms to the profile identified in the term
17
+ # "http://hl7.org/fhir/tag/security" A security label
18
+ attr_accessor :scheme
19
+
20
+ # Label is an OPTIONAL human-readable label for the tag for use when displaying in end-user applications
21
+ attr_accessor :label
22
+
23
+ def to_header
24
+ s = "#{term}; scheme=#{scheme}"
25
+ s += "; label=#{label}" if !label.nil?
26
+ s
27
+ end
28
+
29
+ # Parses a string named "header" and returns a Tag object.
30
+ def self.parse_tag(header)
31
+ h = FHIR::Tag.new
32
+ regex = /\s*;\s*/
33
+ tokens = header.strip.split(regex)
34
+ h.term = tokens.shift
35
+ tokens.each do |token|
36
+ if !token.strip.index('scheme').nil?
37
+ token.strip =~ %r{(?<=scheme)(\s*)=(\s*)([\".:_\-\/\w]+)}
38
+ h.scheme = $3
39
+ elsif !token.strip.index('label').nil?
40
+ token.strip =~ %r{(?<=label)(\s*)=(\s*)([\".:_\-\/\w\s]+)}
41
+ h.label = $3
42
+ end
43
+ end
44
+ h
45
+ end
46
+
47
+ # Parses a string named "header" and returns an Array of Tag objects.
48
+ def self.parse_tags(header)
49
+ tags = []
50
+ regex = /\s*,\s*/
51
+ tokens = header.strip.split(regex)
52
+ tokens.each { |token| tags << FHIR::Tag.parse_tag(token) }
53
+ tags
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,10 @@
1
+ module FHIR
2
+ module Formats
3
+ class PatchFormat
4
+
5
+ PATCH_XML = "application/xml-patch+xml"
6
+ PATCH_JSON = "application/json-patch+json"
7
+
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,133 @@
1
+ module FHIR
2
+ class ResourceAddress
3
+
4
+ DEFAULTS = {
5
+ id: nil,
6
+ resource: nil,
7
+ format: 'application/xml+fhir',
8
+ }
9
+
10
+ DEFAULT_CHARSET = 'UTF-8'
11
+
12
+ def fhir_headers(options, use_format_param=false)
13
+ options = DEFAULTS.merge(options)
14
+
15
+ format = options[:format] || FHIR::Formats::ResourceFormat::RESOURCE_XML
16
+ fhir_headers = {
17
+ 'User-Agent' => 'Ruby FHIR Client',
18
+ 'Content-Type' => format + ';charset=' + DEFAULT_CHARSET,
19
+ 'Accept-Charset' => DEFAULT_CHARSET
20
+ }
21
+ # remove the content-type header if the format is 'xml' or 'json' because
22
+ # even those are valid _format parameter options, they are not valid MimeTypes.
23
+ fhir_headers.delete('Content-Type') if ['xml','json'].include?(format.downcase)
24
+
25
+ if(options[:category])
26
+ # options[:category] should be an Array of FHIR::Tag objects
27
+ tags = {
28
+ 'Category' => options[:category].collect { |h| h.to_header }.join(',')
29
+ }
30
+ fhir_headers.merge!(tags)
31
+ options.delete(:category)
32
+ end
33
+
34
+ if use_format_param
35
+ fhir_headers.delete('Accept')
36
+ options.delete('Accept')
37
+ options.delete(:accept)
38
+ else
39
+ fhir_headers['Accept'] = format
40
+ end
41
+
42
+ fhir_headers.merge!(options) unless options.empty?
43
+ fhir_headers[:operation] = options[:operation][:name] if options[:operation] && options[:operation][:name]
44
+ fhir_headers
45
+ end
46
+
47
+ def resource_url(options, use_format_param=false)
48
+ options = DEFAULTS.merge(options)
49
+
50
+ params = {}
51
+ url = ""
52
+ # handle requests for resources by class or string; useful for testing nonexistent resource types
53
+ url += "/#{ options[:resource].try(:name).try(:demodulize) || options[:resource].split("::").last }" if options[:resource]
54
+ url += "/#{options[:id]}" if options[:id]
55
+ url += "/$validate" if options[:validate]
56
+
57
+ if(options[:operation])
58
+ opr = options[:operation]
59
+ p = opr[:parameters]
60
+ p = p.each{|k,v|p[k]=v[:value]} if p
61
+ params.merge!(p) if p && opr[:method]=='GET'
62
+
63
+ if (opr[:name] == :fetch_patient_record)
64
+ url += "/$everything"
65
+ elsif (opr[:name] == :value_set_expansion)
66
+ url += "/$expand"
67
+ elsif (opr && opr[:name]== :value_set_based_validation)
68
+ url += "/$validate-code"
69
+ elsif (opr && opr[:name]== :code_system_lookup)
70
+ url += "/$lookup"
71
+ elsif (opr && opr[:name]== :concept_map_translate)
72
+ url += "/$translate"
73
+ end
74
+ end
75
+
76
+ if (options[:history])
77
+ history = options[:history]
78
+ url += "/_history/#{history[:id]}"
79
+ params[:_count] = history[:count] if history[:count]
80
+ params[:_since] = history[:since].iso8601 if history[:since]
81
+ end
82
+
83
+ if(options[:search])
84
+ search_options = options[:search]
85
+ url += '/_search' if search_options[:flag]
86
+ url += "/#{search_options[:compartment]}" if search_options[:compartment]
87
+
88
+ if search_options[:parameters]
89
+ search_options[:parameters].each do |key,value|
90
+ params[key.to_sym] = value
91
+ end
92
+ end
93
+ end
94
+
95
+ # options[:params] is simply appended at the end of a url and is used by testscripts
96
+ url += options[:params] if options[:params]
97
+
98
+ if(options[:summary])
99
+ params[:_summary] = options[:summary]
100
+ end
101
+
102
+ if use_format_param && options[:format]
103
+ params[:_format] = options[:format]
104
+ end
105
+
106
+ uri = Addressable::URI.parse(url)
107
+ # params passed in options takes precidence over params calculated in this method
108
+ # for use by testscript primarily
109
+ uri.query_values = params unless options[:params] && options[:params].include?("?")
110
+ uri.normalize.to_str
111
+ end
112
+
113
+ # Get the resource ID out of the URL (e.g. Bundle.entry.response.location)
114
+ def self.pull_out_id(resourceType,url)
115
+ id = nil
116
+ if !resourceType.nil? && !url.nil?
117
+ token = "#{resourceType}/"
118
+ start = url.index(token) + token.length
119
+ t = url[start..-1]
120
+ stop = (t.index("/") || 0)-1
121
+ stop = -1 if stop.nil?
122
+ id = t[0..stop]
123
+ end
124
+ id
125
+ end
126
+
127
+ def self.append_forward_slash_to_path(path)
128
+ path += '/' unless path.last == '/'
129
+ path
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,10 @@
1
+ module FHIR
2
+ module Formats
3
+ class ResourceFormat
4
+
5
+ RESOURCE_XML = "application/xml+fhir"
6
+ RESOURCE_JSON = "application/json+fhir"
7
+
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,170 @@
1
+ module FHIR
2
+ module Sections
3
+ module Crud
4
+
5
+ #
6
+ # Read the current state of a resource.
7
+ #
8
+ def read(klass, id, format=@default_format, summary=nil, options = {})
9
+ options = { resource: klass, id: id, format: format }.merge(options)
10
+ options[:summary] = summary if summary
11
+ reply = get resource_url(options), fhir_headers(options)
12
+ reply.resource = parse_reply(klass, format, reply)
13
+ reply.resource_class = klass
14
+ reply
15
+ end
16
+
17
+ #
18
+ # Read a resource bundle (an XML ATOM feed)
19
+ #
20
+ def read_feed(klass, format=@default_format_bundle)
21
+ options = { resource: klass, format: format }
22
+ reply = get resource_url(options), fhir_headers(options)
23
+ reply.resource = parse_reply(klass, format, reply)
24
+ reply.resource_class = klass
25
+ reply
26
+ end
27
+
28
+ #
29
+ # Read the state of a specific version of the resource
30
+ #
31
+ def vread(klass, id, version_id, format=@default_format)
32
+ options = { resource: klass, id: id, format: format, history: {id: version_id} }
33
+ reply = get resource_url(options), fhir_headers(options)
34
+ reply.resource = parse_reply(klass, format, reply)
35
+ reply.resource_class = klass
36
+ reply
37
+ end
38
+
39
+ def raw_read(options)
40
+ reply = get resource_url(options), fhir_headers(options)
41
+ reply.body
42
+ end
43
+
44
+ def raw_read_url(url)
45
+ reply = get url, fhir_headers({})
46
+ reply.body
47
+ end
48
+
49
+ #
50
+ # Update an existing resource by its id or create it if it is a new resource, not present on the server
51
+ #
52
+ def update(resource, id, format=@default_format)
53
+ base_update(resource, id, nil, format)
54
+ end
55
+
56
+ #
57
+ # Update an existing resource by its id or create it if it is a new resource, not present on the server
58
+ #
59
+ def conditional_update(resource, id, searchParams, format=@default_format)
60
+ options = {
61
+ :search => {
62
+ :flag => false,
63
+ :compartment => nil,
64
+ :parameters => {}
65
+ }
66
+ }
67
+ searchParams.each do |key,value|
68
+ options[:search][:parameters][key] = value
69
+ end
70
+ base_update(resource, id, options, format)
71
+ end
72
+
73
+ #
74
+ # Update an existing resource by its id or create it if it is a new resource, not present on the server
75
+ #
76
+ def base_update(resource, id, options, format)
77
+ options = {} if options.nil?
78
+ options[:resource] = resource.class
79
+ options[:format] = format
80
+ options[:id] = id
81
+ reply = put resource_url(options), resource, fhir_headers(options)
82
+ reply.resource = parse_reply(resource.class, format, reply)
83
+ reply.resource_class = resource.class
84
+ reply
85
+ end
86
+
87
+ #
88
+ # Partial update using a patchset (PATCH)
89
+ #
90
+ def partial_update(klass, id, patchset, options={}, format=@default_format)
91
+ options = { resource: klass, id: id, format: format }.merge options
92
+
93
+ if (format == FHIR::Formats::ResourceFormat::RESOURCE_XML)
94
+ options[:format] = FHIR::Formats::PatchFormat::PATCH_XML
95
+ options[:Accept] = format
96
+ elsif (format == FHIR::Formats::ResourceFormat::RESOURCE_JSON)
97
+ options[:format] = FHIR::Formats::PatchFormat::PATCH_JSON
98
+ options[:Accept] = format
99
+ end
100
+
101
+ reply = patch resource_url(options), patchset, fhir_headers(options)
102
+ reply.resource = parse_reply(klass, format, reply)
103
+ reply.resource_class = klass
104
+ reply
105
+ end
106
+
107
+ #
108
+ # Delete the resource with the given ID.
109
+ #
110
+ def destroy(klass, id=nil, options={})
111
+ options = { resource: klass, id: id, format: nil }.merge options
112
+ reply = delete resource_url(options), fhir_headers(options)
113
+ reply.resource_class = klass
114
+ reply
115
+ end
116
+
117
+ #
118
+ # Create a new resource with a server assigned id. Return the newly created
119
+ # resource with the id the server assigned.
120
+ #
121
+ def create(resource, format=@default_format)
122
+ base_create(resource, nil ,format)
123
+ end
124
+
125
+ #
126
+ # Conditionally create a new resource with a server assigned id.
127
+ #
128
+ def conditional_create(resource, ifNoneExistParameters, format=@default_format)
129
+ query = ''
130
+ ifNoneExistParameters.each do |key,value|
131
+ query += "#{key.to_s}=#{value.to_s}&"
132
+ end
133
+ query = query[0..-2] # strip off the trailing ampersand
134
+ options = {}
135
+ options['If-None-Exist'] = query
136
+ base_create(resource, options, format)
137
+ end
138
+
139
+ #
140
+ # Create a new resource with a server assigned id. Return the newly created
141
+ # resource with the id the server assigned.
142
+ #
143
+ def base_create(resource, options, format)
144
+ options = {} if options.nil?
145
+ options[:resource] = resource.class
146
+ options[:format] = format
147
+ reply = post resource_url(options), resource, fhir_headers(options)
148
+ if [200,201].include? reply.code
149
+ type = reply.response[:headers][:content_type]
150
+ if !type.nil?
151
+ if type.include?('xml') && !reply.body.empty?
152
+ reply.resource = resource.class.from_xml(reply.body)
153
+ elsif type.include?('json') && !reply.body.empty?
154
+ reply.resource = resource.class.from_fhir_json(reply.body)
155
+ else
156
+ reply.resource = resource # just send back the submitted resource
157
+ end
158
+ else
159
+ reply.resource = resource # don't know the content type, so return the resource provided
160
+ end
161
+ else
162
+ reply.resource = resource # just send back the submitted resource
163
+ end
164
+ reply.resource_class = resource.class
165
+ reply
166
+ end
167
+
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,23 @@
1
+ module FHIR
2
+ module Sections
3
+ module Feed
4
+
5
+ FORWARD = :next_link
6
+ BACKWARD = :previous_link
7
+ FIRST = :first_link
8
+ LAST = :last_link
9
+
10
+ def next_page(current, page=FORWARD)
11
+ bundle = current.resource
12
+ link = bundle.method(page).call
13
+ return nil unless link
14
+ reply = get strip_base(link.url), fhir_headers
15
+ reply.resource = parse_reply(current.resource_class, @default_format_bundle, reply)
16
+ reply.resource_class = current.resource_class
17
+ reply
18
+ end
19
+
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,78 @@
1
+ module FHIR
2
+ module Sections
3
+ module History
4
+ #
5
+ # Create a new resource with a server assigned id. Return the newly created
6
+ # resource with the id the server assigned. Associates tags with newly created resource.
7
+ #
8
+ # @param resourceClass
9
+ # @param resource
10
+ # @return
11
+ #
12
+ # public <T extends Resource> AtomEntry<OperationOutcome> create(Class<T> resourceClass, T resource, List<AtomCategory> tags);
13
+
14
+ #
15
+ # Retrieve the update history for a resource with given id since last update time.
16
+ # Last update may be null TODO - ensure this is the case.
17
+ #
18
+ # @param lastUpdate
19
+ # @param resourceClass
20
+ # @param id
21
+ # @return
22
+ #
23
+ # public <T extends Resource> AtomFeed history(Calendar lastUpdate, Class<T> resourceClass, String id);
24
+ # public <T extends Resource> AtomFeed history(DateAndTime lastUpdate, Class<T> resourceClass, String id);
25
+
26
+ def history(options)
27
+ reply = get resource_url(options), fhir_headers(options).except(:history)
28
+ reply.resource = parse_reply(options[:resource], @default_format_bundle, reply)
29
+ reply.resource_class = options[:resource]
30
+ reply
31
+ end
32
+
33
+ #
34
+ # Retrieve the entire update history for a resource with the given id.
35
+ # Last update may be null TODO - ensure this is the case.
36
+ #
37
+ # @param resourceClass
38
+ # @param id
39
+ # @param lastUpdate
40
+ # @return
41
+ #
42
+ def resource_instance_history_as_of(klass, id, lastUpdate)
43
+ history(resource: klass, id: id, history:{since: lastUpdate})
44
+ end
45
+
46
+ def resource_instance_history(klass, id)
47
+ history(resource: klass, id: id, history:{})
48
+ end
49
+
50
+ def resource_history(klass)
51
+ history(resource: klass, history:{})
52
+ end
53
+
54
+ def resource_history_as_of(klass, lastUpdate)
55
+ history(resource: klass, history:{since: lastUpdate})
56
+ end
57
+
58
+ #
59
+ # Retrieve the update history for all resource types since the start of server records.
60
+ #
61
+ def all_history
62
+ history(history:{})
63
+ end
64
+
65
+ #
66
+ # Retrieve the update history for all resource types since a specific last update date/time.
67
+ #
68
+ # Note:
69
+ # @param lastUpdate
70
+ # @return
71
+ #
72
+ def all_history_as_of(lastUpdate)
73
+ history(history:{since: lastUpdate})
74
+ end
75
+
76
+ end
77
+ end
78
+ end