1

I am facing the following problem. I am trying to construct a function (macro) on top of the hapi-fhir api

function (macro) on topo of hapi-fhir api
  (defmacro search-patient-resource
  "This macro searches for a specified resource based on the
  Patient Id"
  [res id json?]
  (let [tmp (symbol res)]
    (if json?
         `(. (. (. (. (. (. @restful-client search) (forResource ~(symbol res))) encodedJson) (where (. (. ~(resolve tmp)  PATIENT)
                    (hasId (str ~id))))) (returnBundle Bundle)) execute)
    )))

This macros works when a do something like

(let [id 10465]
 (search-patient-resource "Observation" id true))
=>#object[ca.uhn.fhir.model.dstu2.resource.Bundle 0x520a3cc9 "Bundle[id=Bundle/9ca62ae1-82af-488f-a166-5b014f45886e]"]

but not when I do

 (let [id 10465 res "Observation"]
 (search-patient-resource "Observation" id true))
=> CompilerException java.lang.NullPointerException, compiling:(apycare_emrspp/hapi_fhir_helper.clj:122:1)

Of course I cannot write (symbol ~res) because then the reader evaluates (symbol "Observation") at compile time and I get

 CompilerException java.lang.IllegalArgumentException: No matching method found: forResource for class ca.uhn.fhir.rest.client.GenericCl
 ient$SearchInternal, compiling:(apycare_emrspp/hapi_fhir_helper.clj:122:1)

Also neither

   (resolve (symbol ~res) 

nor

   (resolve ~(symbol re) 

work.

The original java code looks like this

 FhirContext ctx = FhirContext.forDstu2();
 String serverBase = "fhirtest.uhn.ca/baseDstu2";
 IGenericClient client = ctx.newRestfulGenericClient(serverBase); 
 Bundle results = client .search() .forResource(Observation.class) 
.where(Observation.PATIENT.hasId("1234")) 
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) 
.execute(); 

What I do is attempt to Make the call with

 client
 .search() 
 .forResource(another-resource.class) 
 .where(another-resource.PATIENT.hasId(another-id)) 
 .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) 
 .execute();
8
  • I don't understand why you are writing this as a macro. It seems you could write it as a function and avoid the problem. Commented Mar 25, 2017 at 20:56
  • How so? I tried to write it as a function, but how to pass arbirtery method name without the clojure compiler complaining? For instanse lets say I want to pass [Observation] in order to call (. resource Observation). If I pass it as a string then (. resource (symbol "Observation)), will not work unless is expanded from a list through macros. Commented Mar 26, 2017 at 5:24
  • Please clarify the way it is supposed to work. Commented Mar 26, 2017 at 7:35
  • Passing a string and Id as arguments, the string has to be transformed into the java method with the same name and be called at. This works but not when I predefine the string. That is when I pass "Observation" I have the (forResource ~(symbol res)) be transofmed to (forResource Observation) , but if I do (def st "Observation") then I get (forResource st). Commented Mar 26, 2017 at 10:19
  • I tried to create a function to do create the symbol from the string but it doesnt work Commented Mar 26, 2017 at 10:22

1 Answer 1

1

Ok. The problem I was facing was due too I was neglecting to import the appropriate symbols when calling the code from a defferent namespace. when res = "Resource" in the code above then

 ~(symbol "Resource")

Could not be preperty resolved if I had not first add

(import '(ca.uhn.fhir.model.dstu2.resource.Resource))  

In the namespace were the macro was called. This made the code working in most cases. To make it fully functional I had to change

 ~(symbol "Resource")

to (identity ~(symbol "Resource"))

This is the proper translation of java's

Resource.class to clojure code

At the end the macro took the form:

 (defmacro search-patient-resource
 "This macro searches for a specified resource based on the
  Patient Id"
 [res id json?]
 (let [tmp (symbol res)]
   (if json?
     `(. (. (. (. (. (. @restful-client search)
                     (forResource (identity ~(symbol res))))
                  encodedJson)
               (where
                 (.
                  (. ~(resolve tmp)  PATIENT)
                  (hasId (~str ~id)))))
            (returnBundle Bundle))
         execute)
     `(. (. (. (. (. @restful-client search)
                  (forResource (identity ~(symbol res))))
               (where (. (. ~(symbol res)
                            PATIENT)
                         (hasId (str ~id)))))
            (returnBundle Bundle))
         execute))))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.