Clojure annotations and integers

I am adding Swagger annotations to JaxRs annotated services.

I have the following:

(^{ GET true Path "/{who}" ApiOperation {:value "Get a hello" :notes "simple clojure GET"} Produces ["text/plain; charset=UTF-8"] ApiResponses {:value [(ApiResponse {:code 200 :message "yay!"})]} } 

If I decompile the created class, the annotations look like this:

 @ApiResponses({@com.wordnik.swagger.annotations.ApiResponse(code=200L, message="yay!")}) @Produces({"text/plain; charset=UTF-8"}) @ApiOperation(value="Get a hello", notes="simple clojure GET") @Path("/{who}") @GET(true) 

notes that in the first code annotations = 200L

At runtime, this value must be int, and I cannot figure out how to do this.

if i try

 ApiResponses {:value [(ApiResponse {:code (int 200) :message "yay!"})]} 

I get a compilation error (using the maven swagger plugin)

 Exception in thread "main" java.lang.ClassCastException: clojure.lang.Var cannot be cast to java.lang.Class, compiling:(pocclj/resourceclj.clj:14) 

I tried

 (def success (int 200)) ... ApiResponses {:value [(ApiResponse {:code success :message "yay!"})]} 

What causes a compilation error:

 Exception in thread "main" java.lang.IllegalArgumentException: Unsupported annotation value: success of class class java.lang.Integer, compiling:(pocclj/resourceclj.clj:14) 

I tried a bunch of other things (deref, etc.) but couldn't find the secret sauce.

I am new to clojure and desperately need help for this.

Thanks in advance

Martin

+4
source share
2 answers

You set the type ': code' correctly. Which can be tested independently:

 user> (def something ^{:ApiResponses {:code (int 200) :message "yay!"}} {:some :data :goes :here}) #'user/something user> (meta something) {:ApiResponses {:code 200, :message "yay!"}} user> (-> (meta something) :ApiResponses :code type) java.lang.Integer 

And without metadata, metadata contains the wrong type:

 user> (def something-wrong ^{:ApiResponses {:code 200 :message "yay!"}} {:some :data :goes :here}) #'user/something-wrong user> (meta something) {:ApiResponses {:code 200, :message "yay!"}} user> (-> (meta something-wrong) :ApiResponses :code type) java.lang.Long 

Due to the exception, it seems that the ApiResponse call ApiResponse failed. If ApiResponse is a macro that expects a number, not an s-expression, then I could see that it is not processing it correctly. If this is a function, you will need to find out why it is crashing.

If I provide a stub implementation for ApiResponse, then it works for me:

 user> (definterface Fooi (Something [])) user.Fooi user> (def ApiResponse identity) #'user/ApiResponse user> (deftype Foo [] Fooi (Something ^{GET true Path "/{who}" ApiOperation {:value "Get a hello" :notes "simple clojure GET"} Produces ["text/plain; charset=UTF-8"] ApiResponses {:value [(ApiResponse {:code (int 200) :message "yay!"})]}} [this] (identity this))) user.Foo 
0
source

I donโ€™t know about ApiResponse or annotations in general, but: it looks like some kind of macro ( deftype? ) Creates annotations for you, and you need it to be 200 as an int. Clojure does not have int literals, so the only way to pass an Integer object directly to a macro is through some other macro that calls it. It is impossible to do it beautifully; as far as I know, you need to either use eval or be very narrow, targeting int literals specifically. Here's a sketch of the solution:

 user> (use 'clojure.walk) user> (defmacro with-int-literals [named-ints & body] (prewalk-replace (into {} (for [[kv] (partition 2 named-ints)] [k (int v)])) `(do ~@body ))) user> (map class (second (macroexpand-1 '(with-int-literals [x 100, y 200] [xy])))) (java.lang.Integer java.lang.Integer) 

So, if you deftype your entire deftype (or any macro generating these annotations) with-int-literals , you can create integers instead. I really don't know if this will work; maybe something in the annotation handler, which for some reason is fundamentally unable to handle ints. But at least that way you can offer its ints and hope for the best.

Edit

Since you actually need internal literals in the metadata, not in the โ€œregularโ€ code, prewalk will not really look at the data that prewalk you. You need to write a version of walk that will take metadata in a reasonable way, and then use it instead of listening here.

0
source

Source: https://habr.com/ru/post/1497491/


All Articles