fhir_client 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,118 @@
1
+ module FHIR
2
+ module Sections
3
+ module Operations
4
+
5
+ # DSTU 2 Operations: http://hl7.org/implement/standards/FHIR-Develop/operations.html
6
+
7
+ # Concept Translation [base]/ConceptMap/$translate | [base]/ConceptMap/[id]/$translate
8
+
9
+ # Closure Table Maintenance [base]/$closure
10
+
11
+ # Fetch Patient Record [base]/Patient/$everything | [base]/Patient/[id]/$everything
12
+ # http://hl7.org/implement/standards/FHIR-Develop/patient-operations.html#everything
13
+ # Fetches resources for a given patient record, scoped by a start and end time, and returns a Bundle of results
14
+ def fetch_patient_record(id=nil, startTime=nil, endTime=nil, method='GET', format=@default_format)
15
+ fetch_record(id,startTime,endTime,method,FHIR::Patient,format)
16
+ end
17
+
18
+ def fetch_encounter_record(id=nil, method='GET', format=@default_format)
19
+ fetch_record(id,nil,nil,method,FHIR::Encounter,format)
20
+ end
21
+
22
+ def fetch_record(id=nil, startTime=nil, endTime=nil, method='GET', klass=FHIR::Patient, format=@default_format)
23
+ options = { resource: klass, format: format, operation: { name: :fetch_patient_record, method: method } }
24
+ options.deep_merge!({id: id}) if !id.nil?
25
+ options[:operation][:parameters] = {} if options[:operation][:parameters].nil?
26
+ options[:operation][:parameters].merge!({start: { type: 'Date', value: startTime}}) if !startTime.nil?
27
+ options[:operation][:parameters].merge!({end: { type: 'Date', value:endTime}}) if !endTime.nil?
28
+
29
+ if options[:operation][:method]=='GET'
30
+ reply = get resource_url(options), fhir_headers(options)
31
+ else
32
+ # create Parameters body
33
+ body = nil
34
+ if(options[:operation] && options[:operation][:parameters])
35
+ p = FHIR::Parameters.new
36
+ options[:operation][:parameters].each do |key,value|
37
+ parameter = FHIR::Parameters::Parameter.new.from_hash(name: key.to_s)
38
+ parameter.method("value#{value[:type]}=").call(value[:value])
39
+ p.parameter << parameter
40
+ end
41
+ body = p.to_xml
42
+ end
43
+ reply = post resource_url(options), p, fhir_headers(options)
44
+ end
45
+
46
+ reply.resource = parse_reply(FHIR::Bundle, format, reply)
47
+ reply.resource_class = options[:resource]
48
+ reply
49
+ end
50
+
51
+ # Build Questionnaire [base]/Profile/$questionnaire | [base]/Profile/[id]/$questionnaire
52
+
53
+ # Populate Questionnaire [base]/Questionnaire/$populate | [base]/Questionnaire/[id]/$populate
54
+
55
+ # Value Set Expansion [base]/ValueSet/$expand | [base]/ValueSet/[id]/$expand
56
+ # http://hl7.org/implement/standards/FHIR-Develop/valueset-operations.html#expand
57
+ # The definition of a value set is used to create a simple collection of codes suitable for use for data entry or validation.
58
+ def value_set_expansion(params={}, format=@default_format)
59
+ options = { resource: FHIR::ValueSet, operation: { name: :value_set_expansion } }
60
+ options.deep_merge!(params)
61
+ terminology_operation(options, format)
62
+ end
63
+
64
+ # Value Set based Validation [base]/ValueSet/$validate | [base]/ValueSet/[id]/$validate
65
+ # http://hl7.org/implement/standards/FHIR-Develop/valueset-operations.html#validate
66
+ # Validate that a coded value is in the set of codes allowed by a value set.
67
+ def value_set_code_validation(params={}, format=@default_format)
68
+ options = { resource: FHIR::ValueSet, operation: { name: :value_set_based_validation } }
69
+ options.deep_merge!(params)
70
+ terminology_operation(options, format)
71
+ end
72
+
73
+ # Concept Look Up [base]/CodeSystem/$lookup
74
+ def code_system_lookup(params={}, format=@default_format)
75
+ options = { resource: FHIR::CodeSystem, operation: { name: :code_system_lookup } }
76
+ options.deep_merge!(params)
77
+ terminology_operation(options, format)
78
+ end
79
+
80
+ #
81
+ def concept_map_translate(params={}, format=@default_format)
82
+ options = { resource: FHIR::ConceptMap, operation: { name: :concept_map_translate } }
83
+ options.deep_merge!(params)
84
+ terminology_operation(options, format)
85
+ end
86
+
87
+ def terminology_operation(params={}, format=@default_format)
88
+ options = { format: format }
89
+ # params = [id, code, system, version, display, coding, codeableConcept, date, abstract]
90
+ options.deep_merge!(params)
91
+
92
+ if options[:operation][:method]=='GET'
93
+ reply = get resource_url(options), fhir_headers(options)
94
+ else
95
+ # create Parameters body
96
+ body = nil
97
+ if(options[:operation] && options[:operation][:parameters])
98
+ p = FHIR::Parameters.new
99
+ options[:operation][:parameters].each do |key,value|
100
+ parameter = FHIR::Parameters::Parameter.new.from_hash(name: key.to_s)
101
+ parameter.method("value#{value[:type]}=").call(value[:value])
102
+ p.parameter << parameter
103
+ end
104
+ body = p.to_xml
105
+ end
106
+ reply = post resource_url(options), p, fhir_headers(options)
107
+ end
108
+
109
+ reply.resource = parse_reply(options[:resource], format, reply)
110
+ reply.resource_class = options[:resource]
111
+ reply
112
+ end
113
+
114
+ # Batch Mode Validation [base]/ValueSet/$batch
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,53 @@
1
+ module FHIR
2
+ module Sections
3
+ module Search
4
+
5
+ #
6
+ # Search a set of resources of a given type.
7
+ #
8
+ # @param klass The type of resource to be searched.
9
+ # @param options A hash of options used to construct the search query.
10
+ # @return FHIR::ClientReply
11
+ #
12
+ def search(klass, options={}, format=@default_format_bundle)
13
+ options.merge!({ resource: klass, format: format })
14
+
15
+ if options[:search] && options[:search][:flag]
16
+ reply = post resource_url(options), nil, fhir_headers(options)
17
+ else
18
+ reply = get resource_url(options), fhir_headers(options)
19
+ end
20
+ # reply = get resource_url(options), fhir_headers(options)
21
+ reply.resource = parse_reply(klass, format, reply)
22
+ reply.resource_class = klass
23
+ reply
24
+ end
25
+
26
+ def search_existing(klass, id, options={}, format=@default_format_bundle)
27
+ options.merge!({ resource: klass, id: id, format: format })
28
+ #if options[:search][:flag]
29
+ if options[:search] && options[:search][:flag]
30
+ reply = post resource_url(options), nil, fhir_headers(options)
31
+ else
32
+ reply = get resource_url(options), fhir_headers(options)
33
+ end
34
+ reply.resource = parse_reply(klass, format, reply)
35
+ reply.resource_class = klass
36
+ reply
37
+ end
38
+
39
+ def search_all(options={}, format=@default_format_bundle)
40
+ options.merge!({ format: format })
41
+ if options[:search] && options[:search][:flag]
42
+ reply = post resource_url(options), nil, fhir_headers(options)
43
+ else
44
+ reply = get resource_url(options), fhir_headers(options)
45
+ end
46
+ reply.resource = parse_reply(nil, format, reply)
47
+ reply.resource_class = nil
48
+ reply
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,64 @@
1
+ module FHIR
2
+ module Sections
3
+ module Tags
4
+ #
5
+ # Get a list of all tags on server
6
+ #
7
+ # GET [base]/_tags
8
+ #
9
+ # public List<AtomCategory> getAllTags();
10
+
11
+ #
12
+ # Get a list of all tags used for the nominated resource type
13
+ #
14
+ # GET [base]/[type]/_tags
15
+ #
16
+ # public <T extends Resource> List<AtomCategory> getAllTagsForResourceType(Class<T> resourceClass);
17
+
18
+ #
19
+ # Get a list of all tags affixed to the nominated resource. This duplicates the HTTP header entries
20
+ #
21
+ # GET [base]/[type]/[id]/_tags
22
+ #
23
+ # public <T extends Resource> List<AtomCategory> getTagsForResource(Class<T> resource, String id);
24
+
25
+ #
26
+ # Get a list of all tags affixed to the nominated version of the resource. This duplicates the HTTP header entries
27
+ #
28
+ # GET [base]/[type]/[id]/_history/[vid]/_tags
29
+ #
30
+ # public <T extends Resource> List<AtomCategory> getTagsForResourceVersion(Class<T> resource, String id, String versionId);
31
+
32
+ #
33
+ # Remove all tags in the provided list from the list of tags for the nominated resource
34
+ #
35
+ # DELETE [base]/[type]/[id]/_tags
36
+ #
37
+ # //public <T extends Resource> boolean deleteTagsForResource(Class<T> resourceClass, String id);
38
+
39
+ #
40
+ # Remove tags in the provided list from the list of tags for the nominated version of the resource
41
+ #
42
+ # DELETE [base]/[type]/[id]/_history/[vid]/_tags
43
+ #
44
+ # public <T extends Resource> List<AtomCategory> deleteTags(List<AtomCategory> tags, Class<T> resourceClass, String id, String version);
45
+
46
+ #
47
+ # Affix tags in the list to the nominated resource
48
+ #
49
+ # POST [base]/[type]/[id]/_tags
50
+ # @return
51
+ #
52
+ # public <T extends Resource> List<AtomCategory> createTags(List<AtomCategory> tags, Class<T> resourceClass, String id);
53
+
54
+ #
55
+ # Affix tags in the list to the nominated version of the resource
56
+ #
57
+ # POST [base]/[type]/[id]/_history/[vid]/_tags
58
+ #
59
+ # @return
60
+ #
61
+ # public <T extends Resource> List<AtomCategory> createTags(List<AtomCategory> tags, Class<T> resourceClass, String id, String version);
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,83 @@
1
+ module FHIR
2
+ module Sections
3
+ module Transactions
4
+
5
+ attr_accessor :transaction_bundle
6
+
7
+ def begin_transaction
8
+ @transaction_bundle = FHIR::Bundle.new
9
+ @transaction_bundle.type = 'transaction'
10
+ @transaction_bundle.entry ||= []
11
+ @transaction_bundle
12
+ end
13
+
14
+ def begin_batch
15
+ @transaction_bundle = FHIR::Bundle.new
16
+ @transaction_bundle.type = 'batch'
17
+ @transaction_bundle.entry ||= []
18
+ @transaction_bundle
19
+ end
20
+
21
+ # syntactic sugar for add_batch_request
22
+ # @param method one of ['GET','POST','PUT','DELETE']
23
+ # @param url relative path, such as 'Patient/123'. Do not include the [base]
24
+ # @param resource The resource if a POST or PUT
25
+ def add_transaction_request(method, url, resource=nil, if_none_exist=nil)
26
+ add_batch_request(method, url, resource, if_none_exist)
27
+ end
28
+
29
+ def add_batch_request(method, url, resource=nil, if_none_exist=nil)
30
+ request = FHIR::Bundle::Entry::Request.new
31
+ if FHIR::Bundle::Entry::Request::METADATA['method']['valid_codes'].values.first.include?(method.upcase)
32
+ request.local_method = method.upcase
33
+ else
34
+ request.local_method = 'POST'
35
+ end
36
+ request.ifNoneExist = if_none_exist if !if_none_exist.nil?
37
+ if url.nil? && !resource.nil?
38
+ options = Hash.new
39
+ options[:resource] = resource.class
40
+ options[:id] = resource.id if request.local_method != 'POST'
41
+ request.url = resource_url(options)
42
+ request.url = request.url[1..-1] if request.url.starts_with?('/')
43
+ else
44
+ request.url = url
45
+ end
46
+
47
+ entry = FHIR::Bundle::Entry.new
48
+ entry.resource = resource
49
+ entry.request = request
50
+
51
+ @transaction_bundle.entry << entry
52
+ entry
53
+ end
54
+
55
+ # syntactic sugar for end_batch
56
+ def end_transaction(format=@default_format)
57
+ end_batch(format)
58
+ end
59
+
60
+ # submit the batch/transaction to the server
61
+ # @param format
62
+ # @return FHIR::ClientReply
63
+ #
64
+ def end_batch(format=@default_format)
65
+ options = { format: format, 'Prefer' => 'return=representation' }
66
+ reply = post resource_url(options), @transaction_bundle, fhir_headers(options)
67
+ begin
68
+ if(format.downcase.include?('xml'))
69
+ reply.resource = FHIR::Xml.from_xml(reply.body)
70
+ else
71
+ reply.resource = FHIR::Json.from_json(reply.body)
72
+ end
73
+ rescue Exception => e
74
+ reply.resource = nil
75
+ end
76
+ reply.resource_class = reply.resource.class
77
+ reply
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,49 @@
1
+ module FHIR
2
+ module Sections
3
+ module Validate
4
+ #
5
+ # Validate resource payload.
6
+ #
7
+ # @param resourceClass
8
+ # @param resource
9
+ # @param id
10
+ # @return
11
+ #
12
+ # public <T extends Resource> AtomEntry<OperationOutcome> validate(Class<T> resourceClass, T resource, String id);
13
+ def validate(resource, options={}, format=@default_format)
14
+ options.merge!({ resource: resource.class, validate: true, format: format })
15
+ params = FHIR::Parameters.new
16
+ add_resource_parameter(params,'resource',resource)
17
+ add_parameter(params,'profile','Uri',options[:profile_uri]) if !options[:profile_uri].nil?
18
+ post resource_url(options), params, fhir_headers(options)
19
+ end
20
+
21
+ def validate_existing(resource, id, options={}, format=@default_format)
22
+ options.merge!({ resource: resource.class, id: id, validate: true, format: format })
23
+ params = FHIR::Parameters.new
24
+ add_resource_parameter(params,'resource',resource)
25
+ add_parameter(params,'profile','Uri',options[:profile_uri]) if !options[:profile_uri].nil?
26
+ post resource_url(options), params, fhir_headers(options)
27
+ end
28
+
29
+
30
+ private
31
+ def add_parameter(params,name,type,value)
32
+ params.parameter ||= []
33
+ parameter = FHIR::Parameters::Parameter.new.from_hash(name: name)
34
+ parameter.method("value#{type}=").call(value)
35
+ params.parameter << parameter
36
+ end
37
+
38
+ def add_resource_parameter(params, name,resource)
39
+ params.parameter ||= []
40
+ parameter = FHIR::Parameters::Parameter.new.from_hash(name: name)
41
+ parameter.resource = resource
42
+ params.parameter << parameter
43
+ end
44
+
45
+
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,73 @@
1
+ namespace :fhir do
2
+
3
+ desc 'console'
4
+ task :console, [] do |t, args|
5
+ binding.pry
6
+ end
7
+
8
+ #
9
+ # Prerequisites & Assumptions:
10
+ #
11
+ # 1. Running SMART-on-FHIR (DSTU2 branch) server [http://example/fhir]
12
+ # 2. Running OpenID Connect server [http://example:8080/openid-connect-server-webapp]
13
+ # 3. Configured client under OpenID Connect server:
14
+ # a. Create a system scope if necessary. SMART-on-FHIR requires 'fhir_complete' scope.
15
+ # b. Add the scope created in (a) to client.
16
+ # c. Add the FHIR server URL to the list of allowed redirect URIs (required?)
17
+ # d. Ensure client has 'client credentials' grant type
18
+ # d. Whitelist the client
19
+ # 4. 'client_id' and 'client_secret' variables are the name and secret of the client created in (3)
20
+ # 5. :authorize_url is the authorization endpoint of the OpenID connect server
21
+ # 6. :token_url is the token endpoint of the OpenID connect server
22
+ #
23
+ desc 'OAuth2 Example'
24
+ task :oauth2, [:url,:client_id,:client_secret] do |t, args|
25
+ client = FHIR::Client.new(args.url)
26
+ client_id = args.client_id
27
+ client_secret = args.client_secret
28
+ options = client.get_oauth2_metadata_from_conformance
29
+ if options.empty?
30
+ puts 'This server does not support the expected OAuth2 extensions.'
31
+ else
32
+ client.set_oauth2_auth(client_id,client_secret,options[:authorize_url],options[:token_url])
33
+ reply = client.read_feed(FHIR::Patient)
34
+ puts reply.body
35
+ end
36
+ end
37
+
38
+ desc 'count all resources for a given server'
39
+ task :count, [:url] do |t, args|
40
+ client = FHIR::Client.new(args.url)
41
+ counts = {}
42
+ fhir_resources.map do | klass |
43
+ reply = client.read_feed(klass)
44
+ counts["#{klass.name.demodulize}"] = reply.resource.total unless reply.resource.nil?
45
+ end
46
+ printf " %-30s %5s\n", 'Resource', 'Count'
47
+ printf " %-30s %5s\n", '--------', '-----'
48
+ counts.each do |key,value|
49
+ # puts "#{key} #{value}"
50
+ printf " %-30s %5s\n", key, value
51
+ end
52
+ end
53
+
54
+ desc 'delete all resources for a given server'
55
+ task :clean, [:url] do |t, args|
56
+ client = FHIR::Client.new(args.url)
57
+ fhir_resources.map do | klass |
58
+ reply = client.read_feed(klass)
59
+ while !reply.nil? && !reply.resource.nil? && reply.resource.total > 0
60
+ reply.resource.entry.each do |entry|
61
+ client.destroy(klass,entry.resource.id) unless entry.resource.nil?
62
+ end
63
+ reply = client.read_feed(klass)
64
+ end
65
+ end
66
+ Rake::Task['fhir:count'].invoke(args.url)
67
+ end
68
+
69
+ def fhir_resources
70
+ Mongoid.models.select {|c| c.name.include?('FHIR') && !c.included_modules.find_index(FHIR::Resource).nil?}
71
+ end
72
+
73
+ end