Coder Social home page Coder Social logo

metosin / compojure-api Goto Github PK

View Code? Open in Web Editor NEW
1.1K 39.0 149.0 3.24 MB

Sweet web apis with Compojure & Swagger

Home Page: http://metosin.github.io/compojure-api/doc/

License: Eclipse Public License 1.0

Clojure 99.82% Shell 0.18%
clojure rest api swagger schema openapi http ring clojure-spec async

compojure-api's Introduction

Compojure-api

Psst! If you're starting a new project, why not try out reitit?

Stuff on top of Compojure for making sweet web apis.

API Docs & Wiki

Latest version

Clojars Project

Latest non-alpha: [metosin/compojure-api "1.1.14"].

See CHANGELOG for details.

For information and help

Clojurians slack (join) has a channel #ring-swagger for talk about any libraries using Ring-swagger. You can also ask questions about Compojure-api and Ring-swagger on other channels at Clojurians Slack or at #clojure on Freenode IRC (mention compojure-api or ring-swagger to highlight us).

Examples

Hello World Api

(require '[compojure.api.sweet :refer :all])
(require '[ring.util.http-response :refer :all])

(def app
  (api
    (GET "/hello" []
      :query-params [name :- String]
      (ok {:message (str "Hello, " name)}))))

Hello World, async

(require '[compojure.api.sweet :refer :all])
(require '[clojure.core.async :as a])

(GET "/hello-async" []
  :query-params [name :- String]
  (a/go
    (a/<! (a/timeout 500))
    (ok {:message (str "Hello, " name)})))

* requires server to be run in async mode

Hello World, async & data-driven

(require '[compojure.api.sweet :refer :all])
(require '[clojure.core.async :as a])
(require '[schema.core :as s])

(context "/hello-async" []
  (resource
    {:get
     {:parameters {:query-params {:name String}}
      :responses {200 {:schema {:message String}}
                  404 {}
                  500 {:schema s/Any}}
      :handler (fn [{{:keys [name]} :query-params}]
                 (a/go
                   (a/<! (a/timeout 500))
                   (ok {:message (str "Hello, " name)})))}}))

* Note that empty body responses can be specified with {} or {:schema s/Any}

Hello World, async, data-driven & clojure.spec

(require '[compojure.api.sweet :refer :all])
(require '[clojure.core.async :as a])
(require '[clojure.spec.alpha :as s])

(s/def ::name string?)
(s/def ::message string?)

(context "/hello-async" []
  (resource
    {:coercion :spec
     :get {:parameters {:query-params (s/keys :req-un [::name])}
           :responses {200 {:schema (s/keys :req-un [::message])}}
           :handler (fn [{{:keys [name]} :query-params}]
                      (a/go
                        (a/<! (a/timeout 500))
                        (ok {:message (str "Hello, " name)})))}}))

Api with Schema & Swagger-docs

(require '[compojure.api.sweet :refer :all])
(require '[schema.core :as s])

(s/defschema Pizza
  {:name s/Str
   (s/optional-key :description) s/Str
   :size (s/enum :L :M :S)
   :origin {:country (s/enum :FI :PO)
            :city s/Str}})

(def app
  (api
    {:swagger
     {:ui "/api-docs"
      :spec "/swagger.json"
      :data {:info {:title "Sample API"
                    :description "Compojure Api example"}
             :tags [{:name "api", :description "some apis"}]
             :consumes ["application/json"]
             :produces ["application/json"]}}}

    (context "/api" []
      :tags ["api"]

      (GET "/plus" []
        :return {:result Long}
        :query-params [x :- Long, y :- Long]
        :summary "adds two numbers together"
        (ok {:result (+ x y)}))

      (POST "/echo" []
        :return Pizza
        :body [pizza Pizza]
        :summary "echoes a Pizza"
        (ok pizza)))))

swagger-api

More samples

To try it yourself, clone this repository and do either:

  1. lein run
  2. lein repl & (go)

Quick start for a new project

Use a Leiningen template, with or without tests:

lein new compojure-api my-api
lein new compojure-api my-api +midje
lein new compojure-api my-api +clojure-test

License

Copyright © 2014-2019 Metosin Oy and contributors.

Distributed under the Eclipse Public License, see LICENSE.

compojure-api's People

Contributors

amalloy avatar antonoellerer avatar deraen avatar eunmin avatar frenchy64 avatar hoxu avatar ikitommi avatar joelittlejohn avatar jsyrjala avatar jtmarmon avatar kalekale avatar lyderichti59 avatar metametadata avatar michaelblume avatar miikka avatar mike706574 avatar mtkp avatar mveytsman avatar nberger avatar phadej avatar rickmoynihan avatar ryfow avatar slipset avatar tayhobbs avatar tchagnon avatar teropa avatar thomaswhitcomb avatar timgilbert avatar tomimas avatar zamaterian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

compojure-api's Issues

not-found returning 400

         (GET* "/jobs/:id" []
               :return Job
               :path-params [id :- Long]
               :responses {440 [String]}
               :summary "get a job by id"
               (if-let [job (get-job id)]
                 (ok job)
                 (not-found {:message "not found"})))

I want to return a 404 if an item is not found however if I provide an id that pushes through to the else in the if-let then I get the following 400:

{:status 400, :headers {"Content-Type" "application/json; charset=utf-8"}, :body "{\"errors\":{\"message\":\"disallowed-key\",\"id\":\"missing-required-key\"}}"}

The missing required key is due to the definition of Job:

(s/defschema Job {:id Long})

I also tried this using case as is used in https://github.com/metosin/compojure-api/blob/cea584b30f76a6defaec47f30701fbea4941420e/test/compojure/api/core_integration_test.clj however that had the same result. I'm not sure what I am doing that is different from the test defined there?

form-params

Is there any plan to support form parameter types?

remove the global swagger state

Swaggered now pushes the routes into a global defonce atom -> bad design and causes problems when multiple root web handlers exist in a project. There should be one swagger route map per root web handler.

Description for path-params and query-params

I haven't been able to add a description to a path param, I tried the following but didn't work:

(GET* "/:id" []
   :path-params [id :- (field String {:description "unique identifier"})]
   ...)

Support for schemas as map values

I'm having some difficulty integrating a schema map using maybe in the map value. Here is the schema definition for the data that my API returns :

(def Boundary
  {:type (enum "MultiPolygon" "Polygon" "MultiPoint" "Point")
   :coordinates [Any]}) 

(def ReturnValue
  {:boundary (maybe Boundary) ;; can be of type Boundary or nil. In either case, 
                                                   ;; the key should always exist
   .....}

Here is the exception I'm running into when starting my server :

java.lang.IllegalArgumentException: error converting to json schema [:boundary (maybe {:type (enum "Point" "Polygon" "MultiPolygon" "MultiPoint"), :coordinates [Any]})]
    at ring.swagger.json_schema$properties$iter__10215__10219$fn__10220$fn__10228.invoke(json_schema.clj:117)
    at ring.swagger.json_schema$properties$iter__10215__10219$fn__10220.invoke(json_schema.clj:113)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.Cons.next(Cons.java:39)
    at clojure.lang.RT.next(RT.java:598)
......

Caused by: java.lang.IllegalArgumentException: don't know how to create json-type of: {:type (enum "Point" "Polygon" "MultiPolygon" "MultiPoint"), :coordinates [Any]}
    at ring.swagger.json_schema$eval10210$fn__10211.invoke(json_schema.clj:95)
    at clojure.lang.MultiFn.invoke(MultiFn.java:227)
    at ring.swagger.json_schema$__GT_json.doInvoke(json_schema.clj:47)
    at clojure.lang.RestFn.invoke(RestFn.java:410)
    at ring.swagger.json_schema$eval10190$fn__10191.invoke(json_schema.clj:86)
    at clojure.lang.MultiFn.invoke(MultiFn.java:227)
    at ring.swagger.json_schema$__GT_json.doInvoke(json_schema.clj:47)
    at clojure.lang.RestFn.invoke(RestFn.java:410)
    at ring.swagger.json_schema$properties$iter__10215__10219$fn__10220$fn__10228.invoke(json_schema.clj:113)
    ... 94 more

I'm using [metosin/compojure-api "0.16.2"]. I'm not quite sure what I'm missing here. Any pointers as to how I can get around this? Do I need to define a custom json-schema?

When model has > 8 fields, swagger-ui shows them in random order

See https://github.com/jsyrjala/compojure-api-examples/blob/ordering/src/compojure/api/examples/domain.clj#L18

(defmodel Pizza {:id    Long
                 :name  String
                 :price Double
                 :hot   Boolean
                 (s/optional-key :description) String
                 :toppings #{Topping}
                 :field7 Boolean
                 :field8 Boolean
                 ;; removing this field9 makes order correct again
                 :field9 Boolean
                 })

Swagger-ui will show fields in order below. If I remove one field from code, the order in swagger-ui will match the code again.

Pizza {
field9 (boolean),
toppings (array[string]),
name (string),
hot (boolean),
price (number),
id (integer),
description (string, optional),
field7 (boolean),
field8 (boolean)
}

Request params JSON array support

Sorry if this has already been asked,

i can't figure out how to deal with query params in this form:
...&myParam=["x","y","z"]&...

is it possible? and if so how ?

meta-data handler for :params

would be nice to have a meta-data handler to read the :params maps from the request. Would need to resolve the parameter types for swagger docs, suggestion:

  1. resolve path-parameters
  2. the rest are :body if the method is POST or PUT
  3. otherwise the rest are :query

... could integrate also to the compojure smart destructurion:

(GET* "/used/:id" [id :- Long, s :- String, simple :- Boolean]
  (ok ...))

0.10.3 doesn't fully work with aot

Actual aot compiled with 0.10.3. This is result from http://localhost:8083/api/api-docs/Section. Individual routes are missing from output.

{"apis":[],"models":{},"produces":    ["application/json"],"resourcePath":"","basePath":"http://localhost:8083","swaggerVersion":"1.2","apiVersion":"v1-dev"}

Expected

{"swaggerVersion":"1.2","produces":["application/json"],"resourcePath":"","models":{},"apis":[{"path":"/api/v1-dev/ping","operations":[{"parameters":[],"responseMessages":[],"nickname":"getApiV1DevPing","notes":"","summary":"Get server version and time.","method":"GET","type":"void"}]}],"apiVersion":"v1-dev","basePath":"http://localhost:8083"}

End result is that when clicking section name in Swagger ui, nothing happens.

Route peeling requires a routes-wrapping with custom routes to work correctly

Creating a custom route-macro in top of compojure-api route-macros (GET*,...) requires an extra routes-wrapper to work correctly. Route-peeling uses now clojure.walk/prewalk, which starts to iterate the contents when the route-peeling expands a macro - instead of iterating the new returned expanded source code. Suggested fix: expand into a list of the source code or use normal recursion to get better control for the peeling.

related code from compojure.api.swagger-test:

  (fact "macros are expanded"
    (defmacro optional-routes [p & body] (when p `(routes ~@body)))
    (extract-routes
      '(context "/api" []
         (optional-routes true
           (GET "/true" [] identity))
         (optional-routes false
           (PUT "/false" [] identity)))) => [{:method :get
                                              :uri "/api/true"}])

Allow unknown schema parameters

Hello metosin!

Is it possible to create schema that would not give error on unknown parameters?
For example:

(s/defschema Total {:total Long})

I want to use this schema and I also want it to accept all other parameters without validation and coercing, like:

{"total": 42, "foo": "bar", "nonono": 1}

0.16.0 includes AOT'ed dependencies

This causes breakage for anyone who happens to be using, say, a different version of http-kit (like 2.1.16), along the lines of:

NoSuchMethodError org.httpkit.server.HttpServer.<init>(Ljava/lang/String;ILorg/httpkit/server/IHandler;III)V
        org.httpkit.server/run-server (server.clj:8)

Of course users of other libraries could see different stuff.

POST* and PUT* fail when body model is an array

The POST* and PUT* macros in compojure.api.core fail with a NullPointerException during compilation when the body model is an array.

How to reproduce:
add to compojure/api/example/domain.clj

(defmodel NewPizzas [NewPizza])

change compojure/api/example/handler.clj from line 35

      (POST* "/pizzas" []
        :return   Pizza
        :body     [pizza NewPizzas {:description "new pizza"}]
        :summary  "Adds a pizza"
        :nickname "addPizza"
        (ok (add! pizza)))

Results in

CompilerException java.lang.NullPointerException, compiling:(compojure/api/example/handler.clj:22:3)
...
Caused by:
NullPointerException
    clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
    compojure.api.core/POST* (core.clj:48)
    clojure.lang.Var.invoke (Var.java:471)
    clojure.lang.Var.invoke (Var.java:471)
    clojure.lang.Var.applyTo (Var.java:532)
    clojure.lang.Compiler.macroexpand1 (Compiler.java:6468)
    clojure.core/macroexpand-1 (core.clj:3633)
    compojure.api.swagger/macroexpand-to-compojure/fn--8233 (swagger.clj:40)
...

When (swaggered) block has > 8 routes, swagger-ui shows them in random order.

See https://github.com/jsyrjala/compojure-api-examples/blob/ordering-routes/src/compojure/api/examples/handler.clj#L13

(swaggered) block has routes in this order

    (POST* "/minus" []
    (GET* "/times/:x/:y" []
    (GET* "/route4/:x/:y" []
    (GET* "/route5/:x/:y" []
    (GET* "/route6/:x/:y" []
    (GET* "/route7/:x/:y" []
    (GET* "/route8/:x/:y" []
    (GET* "/route9/:x/:y" []

Swagger-ui shows this order:

GET /route9/{x}/{y}
GET /route8/{x}/{y}
GET /route7/{x}/{y}
GET /route6/{x}/{y}
GET /route5/{x}/{y}
POST /minus
GET /route4/{x}/{y}
GET /times/{x}/{y}
GET /plus

Nested Schemas with (maybe)

Hi - I'm trying to do this, I believe it should work. But it gives me all sorts of errors depending on which variation I try. Here's a sketch of what I'm trying to do. It is a create method, with different sets of required parameters.

(s/defschema OptionalParam
  (s/either {:option1 String}  {:option2 String}))

(s/defschema CreateParams1 {
  :source OptOne
  :dest      String
  })

(s/defschema CreateParamsN {
  :source OptOne
  :dest      String
  :other-variables Integer
  })

(s/defschema TopLevelCreateAWidget {
   :uuid java.util.UUID,
   :params (s/either
      CreateParams1
      ;;.....
      CreateParamsN)})

I have seen

Can't create empty: schema.core.Either clojure

which made me try to do something without either and used a bunch of maybes, which didn't work either. Because I didn't want to have a bunch of empty keys required.

The latest is this from the :params key.

   "type":"class java.lang.IllegalArgumentException","message":"error converting to json schema

This feels like it should just "work", I should be able to group together the create messages and have an (either) in there to let you pass in any of them... Any suggestions?

If a user's project.clj doesn't have ring-swagger-ui, no UI is available.

We found that removing ring-swagger-ui from a project.clj for a project using compojure-api broke the availability of the Swagger UI functionality.

Probably either ring-swagger-ui needs to be in this project's toplevel project.clj, or the README needs to be updated to warn prospective users of this pitfall. Thanks!

Uberjar fails when using compojure-api with HTTP Kit

I tried using compojure-api with HTTP Kit. Running the server from REPL worked fine, but running lein uberjar resulted in

java.lang.IllegalArgumentException: No matching clause: class compojure.api.swagger.CompojureRoutes
    at compojure.api.swagger$create_paths.invoke(swagger.clj:82)
    at compojure.api.swagger$extract_routes.invoke(swagger.clj:123)
    at compojure.api.swagger$swagger_info.invoke(swagger.clj:131)
    at compojure.api.swagger$swaggered.doInvoke(swagger.clj:172)
        ...

Additionally, trying to load the server in REPL again after the failed uberjar (without running lein clean) results in

java.lang.IllegalStateException: Attempting to call unbound fn: #'clout.core/route-compile
    at clojure.lang.Var$Unbound.throwArity(Var.java:43)
    at clojure.lang.AFn.invoke(AFn.java:39)
    at compojure.api.middleware__init.load(Unknown Source)
    at compojure.api.middleware__init.<clinit>(Unknown Source)
        ...

The problem might have something to do with the fact that HTTP Kit wants the main class to be AOT compiled. I followed the HTTP Kit Leiningen template in creating the server.

I created a minimal project where this happens. You can find it here: https://github.com/arttuka/swagger-httpkit-test

middleware on routes delete api info for swagger

I simply tried to add

(PUT* "/my/route" []
    :return mySchema
    :body [param (describe paramSchema "my param")]
    :middlewares [identity]
    (ok "done"))

in my route declaration, the middlewares appears to work, but the YAML generated for the api-docs occurs as if there were no schema provided. So the swagger ui don't show any more input fields.

+routes+ error after 0.12.0 -> 0.13.1

Hi Tommi,

I've upgraded from 0.12.0 to 0.13.1 (to make use of {s/Keyword s/Any}) and started getting this error:

Unable to resolve symbol: +routes+ in this context, compiling:(my_project/apis/pizzas.clj:141:13)

I have the following structure:
core.clj:

(ns my-project.core
  (:use clojure.pprint)
  (:require [com.duelinmarkers.ring-request-logging
             :refer [wrap-request-logging]]
            [compojure.api.sweet :refer :all]
            [clj-stacktrace.repl]
            [my-project.apis [pizzas :as pizzas]
              [sushis :as sushis]
              ;; some more imports
              ]
            [my-project.util [middleware :as middleware]]))

(defapi app
        (middlewares [middleware/wrap-catch-exceptions]
                     (swagger-ui)
                     (swagger-docs
                       :title "my project API"
                       :apiVersion "0.4.7"
                       :description "my project description"
                       :termsOfServiceUrl "http://www.google.com"
                       :contact "[email protected]"
                       :license "Eclipse 1.0"
                       :licenseUrl "http://www.eclipse.org/legal/epl-v10.html")
                     pizzas/route
                     sushis/route
                     ;; some more routes
                     )
        (compojure.route/not-found "<h1>No.</h1>"))

(def handler
  (-> #'app
      middleware/wrap-print-request
      (middleware/wrap-access-control-allow "*" "GET, POST, PUT, DELETE")
      ;middleware/wrap-print-response
      ;; Config in src/log4j.properties
      com.duelinmarkers.ring-request-logging/wrap-request-logging
      ))

apis/pizzas.clj:

;; ...

(defmodel Pizza
  ;; ...
  )

(defroutes* route
            (swaggered "pizzas"
                       :description "Pizzas API"
                       (context "/pizzas" []
                                (GET* "/" {pms :params}
                                      :summary "Get all pizzas"
                                      :return [Pizza]
                                      (ok (get-all-pizzas pms))))))

I like it because it allows me to isolate api groups from each other.

Now I started investigating and after removing (swaggered) from pizzas.clj made it successfully compile.But pizzas disappeared from the swagger front page.

Then I added (swaggered) to core.clj like this:

(swaggered "pizzas"
  :description "Pizzas API"
  pizzas/route)

Then it gave an error about "unable to resolve Pizza", because (:require [my-project.apis [pizzas :as pizzas]]), not (:require [my-project.apis.pizzas :refer :all]).
I tried to :refer :all, but it gave errors about coinciding var names (like route and DATE_FORMAT, that I have in several files).

In fact, my APIs are more complicated than this, they include many schemas which I don't want to explicitly :refer in core.clj, and there are many vars with same names across different files.

Is there a possibility in 0.13.1 to define this structure without repeating and adding boilerplate code?

Thanks!

middleware not taking effect?

Hello,

First, thanks for this library. It has saved us many hours integrating swagger with our Clojure app.

I am trying to add a middleware to our compojure-api app, that will change all :under_scored keywords in request param keys into :dash-ed ones. e.g. :per_page will be changed into :per-page. It depends on other middlewares like wrap-params and wrap-keyword-params which are by default part of the api-middleware that compojure-api ships.

Here is how I have added the middleware (omitted some routes and renamed to things):

(middlewares [wrap-dashed-params wrap-request-logging]
             (swaggered "Things"
                        :description "foo..."
                        (POST* ;; snip
                         )
                        (GET* "/things" []
                              :return things-api/PaginatedThingList
                              :summary "Returns a paginated list of things"
                              :query [params things-api/GetThingssQueryParams]
                              (ok (things-api/index params)))))

wrap-dashed-params is written like wrap-keyword-params except that it changes keyword keys to be dashed versions. Because I use the wrap-request-logging I see that the params are indeed changed into dashed versions. So I know that it is being run and that the logic inside the middleware works. However, when the request reaches my app (things-api/index in this example) I see the :under_score keywords and not :dash-ed ones. Example:

INFO  com.duelinmarkers.ring-request-logging: Request start: :get /things per_page=3&page=2
DEBUG com.duelinmarkers.ring-request-logging: :params {:page 2, :per-page 3}
INFO  myservice-backend.things.api: {:page 2, :per_page 3}
INFO  com.duelinmarkers.ring-request-logging: Request end: /things 200

In the above log, we see that the logging middleware sees the :dash-ed version of the params but the app sees the :under_scored ones.

What am I missing here? I have tried various ways of specifying the middlewares to compojure-api (like adding api-middleware explicitly, specifying them as a threaded form ((-> wrap-request-logging wrap-dashed-params)), but have not succeeded in getting this to work.

PS: new to Clojure/ring/compjure/compojure-api, so perhaps there is an obvious mistake somewhere?

[Question] Including routes from another namespace into the Swagger docs

I'm fairly new to Clojure, so I could be doing something wrong here, but I was hoping to get some advice on structuring a project.

I'm working on a web API project, and I've split the files up into something like this:

/
  |- src/
    |- test_api/
      |- handler.clj
      |- routes/
        |- product.clj  

The idea is to split the routes that are grouped by context out into their own files; i.e. routes/product.clj contains the routes for the /products context.

I've found that the swagger docs for the defroutes* nested in the context defined in routes/product.clj don't get generated unless I explicitly :refer to them in handler.clj - I'd prefer to only use :require :as so I can refer to them under their namespaces.

I've included examples of the code below. handler.clj has some comments showing which version of the :require statements work, and which version doesn't.

handler.clj

(ns test-api.handler
  (:require [compojure.api.sweet :refer :all]
            [ring.util.http-response :refer :all]
            [schema.core :as s]
            ;; the line below generates the correct swagger docs
            [test-api.routes.product :as productroutes :refer [product-routes get-product-by-id get-products-by-barcode]]
            ;; the line below doesn't generate the docs for the '/products/:id' or '/products/barcode/:barcode' routes, but 
            ;; docs for 'products/test' will be generated
            ;;[test-api.routes.product :as productroutes :refer [product-routes]]
            ))

(s/defschema Result {:result Long})

(defapi app
  (swagger-ui)
  (swagger-docs
    :title "Test-api Api"
    :description "this is Test-api Api.")
  (swaggered "products"
     :description "products api"
     productroutes/product-routes)
  (swaggered "math"
    :description "playing with parameters"
    (GET* "/plus" []
      :return Result
      :query-params [x :- Long, {y :- Long 1}]
      :summary "x+y (y default to 1)"
      (ok {:result (+ x y)}))))

routes/product.clj

(ns test-api.routes.product
  (:require [compojure.api.sweet :refer :all]
            [ring.util.http-response :refer :all]
            [schema.core :as s]
            [test-api.services.product-services :as product-service]
            [test-api.data.mockdata :refer [db]]))


(defroutes* get-product-by-id
  (GET* "/:id" []
    :summary "get a product by id"
    :path-params [id :- Long]
    (ok (product-service/get-product-by-id db id))))

(defroutes* get-products-by-barcode
  (GET* "/barcode/:barcode" []
          :summary "get products with matching barcode"
          :path-params [barcode :- String]
          :query-params [lon :- Double, lat :- Double]
          (ok (product-service/find-products-by-barcode-and-location db barcode lon lat))))


(defroutes* product-routes
  (context "/products" []
    get-product-by-id
    get-products-by-barcode
    (GET* "/test" []
          (ok "test"))
           ))

Is there a way to get this working without including the routes explicitly?
Thanks in advance for any advice you can give me!

Better documentation

README.md is too crowded and messy.

We should probably use Wiki and simplify readme.

Possible wiki pages:

  • Extension points: parameter restructuring, pointer to ring-swagger doc about json-type
  • Serialization, #184

Unable to coerce DateTime in post body

I am using compojure-api version 0.11.4 and attempting to do the following:

(defmodel Token
  {:token-id s/Str
   :created-on DateTime*)

;; Beginning of route def omitted
(swaggered "token"
    (context "/token" []
        (POST* "/" []
            :body [token Token]
            :return Token
            (ok token))))

However, a request with this as the JSON body, fails:

{
    "token-id": "12345",
    "created-on": "2014-02-18T18:25:37.456Z"
}

With this error:

{
  "errors": {
    "expires-on": "(not (date-time? a-java.lang.String))"
  }
}

The same thing happens if I change the :token-id to be a Uuid. Am I missing something? Or is this actually a problem?

[Question] how to mix and match Compojure Routes and static resources in defapi

How do you change the default context of swagger-ui (default is "/") and include a static resource handler under the same?

The following, for example, doesn't work:

(defroutes static
    (route/resources "/public"))

(defapi router
  ;;(static) => wrong number of routes passed in core/routes/fn--1828
  (swagger-ui "swagger") ;; doesn't serve under /swagger, 404-not found
  (swagger-docs :title "Longitude api")
  (swaggered "api" :description "Longitude User who is an Organization (not a Person)"
    (context "/api" []
       (GET* "/org" []  (ok  {}))))

I also tried to include compojure.api.middleware.public-resource-routes under defapi, but no success.

Mounting middlewares example doesn't work

See Mounting middlewares -section in README. https://github.com/metosin/compojure-api

The section seems to indicate that these fragments work identically

(defroutes api-routes
  (middlewares [api-middleware]
     .... )

(defapi app
   ... )

Actually the former fails with this exception:

  java.lang.RuntimeException: Unable to resolve symbol: +routes+ in this context

I think that the former example should be something like this:

(ns ...
 (:require [compojure.api.routes :as routes]))

(defroutes api-routes
  (middlewares [api-middleware]
    (routes/with-routes
       .... ))

Unable to test form params using Swagger UI

server doesn't consume application/x-www-form-urlencoded, try ["application/json","application/x-yaml","application/edn","application/transit+json","application/transit+msgpack"]

The current implementation is not advertising form params support through the swagger docs.

swaggered-macro needs to be in a root of an web app

swaggered -macro needs to be in the root context of an app for the routes to be correct. This would be better:

  • have a separate root macro to do the route collecting
  • defapi would contain it
  • swaggered could be just a marked context, "api starts from here"

add metadata hints for header-params

It would be nice to be able to specify required parameters in the headers similar to how they are specified for query-parameters and body-parameters.

Contrived Example:

(POST* "/" []
             :query-params  [accesstoken :- String]
             :header-params [sha256 :- String
                             path :- String]
             :body          [content String]
             (ok (str path sha256 accesstoken content)))

If this already exists and I just couldn't find it please let me know. Thanks.

Compojure-api roadmap

Ideas how to integrate Swagger 2.0 & clean things up for 1.0 Release.

Minimal changes for 2.0

  1. swagger-docs converts collected data to 2.0 format
  2. swagger-ui default to swagger.json
  3. swaggered pushes name as tags

Breaking

  1. collect routes from root, new route-element for it
  2. collect parameters in the Swagger 2.0 format
  3. remove swaggered-macro
  4. :tags restructuring for setting the correct api-group
  5. context* to set-up common meta-data (tags etc.) for sub-routes

Final

  1. fix defroutes* to handle models in namespaces

... feedback and contributions welcome.

how can you maintain good indentation in Emacs

(defapi app
  (swagger-ui)
  (swagger-docs
    :title "Sample api")
  (swaggered "thingie"
    :description "There be thingies"
    (context "/api" []

      (GET* "/plus" []
        :return       Total
        :query-params [x :- Long {y :- Long 1}]
        :summary      "x+y with query-parameters. y defaults to 1."
        (ok {:total (+ x y)}))

the above code cannot indent correct in my Emacs, when I hit: C-x h and C-M \ .

I use Prelude: http://batsov.com/prelude/.

[Question] Correct way to have routes and schemas i different namespaces

It seems that the swaggered macro expects the route schemas to be available in the namespace where swaggered is called. In my naive first try I wanted to have parts of my API in different namespaces and keep the schema completely contained to that namespace as swaggered itself on the outside doesn't seem to need access to it.

Handler:

(ns api-test.handler
  (:require [compojure.api.sweet :refer :all]
            [api-test.hello-api :as hello]))

(defapi app
  (swagger-ui)
  (swagger-docs
    :title "Api-test")
  (swaggered "api"
    :description "hello world"
    hello/api))

API routes:

(ns api-test.hello-api
  (:require [compojure.api.sweet :refer :all]
            [ring.util.http-response :refer [ok]]
            [schema.core :as s]))

(s/defschema Message {:message String})

(defroutes* api
        (GET* "/hello" []
              :return Message
              :query-params [name :- String]
              :summary "say hello"
              (ok {:message (str "Hello, " name)})))

But in this way the handler code compilation fails on "Unable to resolve symbol: Message in this context".
As I didn't want to do a :refer :all on hello-api I changed the handler to this:

(ns api-test.handler
  (:require [compojure.api.sweet :refer :all]
            [api-test.hello-api :refer [api Message]]))

(defapi app
  (swagger-ui)
  (swagger-docs
    :title "Api-test")
  (swaggered "api"
    :description "hello world"
    api))

But that feels a bit weird as well, at least if I had a lot of multiple schema definitions.
Is there some other way to go around this when I have a large non-trivial API that I want to divide to separate namespaces?

Getting query string parameters

Hi all!
I upgraded from 0.7.0 to 0.7.3 and it broke my compilation a little.
I use the following syntax to get parsed query string parameters:

(defroutes route
  (swaggered "value-sets"
    (context "/value-sets" []
      (GET* "/" {pms :params}
        :return   [ValueSet]
        (ok (get-all-value-sets pms))))))

After an upgrade I start to get an exception:

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: pms in this context, compiling:(fm_server/apis/value_sets.clj:103:13)

The query looks like

GET /values-sets?count=10&page=1&sorting[name]=asc

And then pms should be a map:

{:count "10" :page "1" {:sorting {"name" "asc"}}}

Seems that you've implemented the right way of getting query string params, but what is it?

ex-info-support might leak private data?

ex-info-support middleware might try to send a response containing e.g. the whole request map. In some cases the request might contain e.g. component-system which might contain private data (db passwords) or might be non-encodable (which is kind of good as it's not good to expose the system...)

Might be best if in case of an exception only a general error message is printed? This could be parametrized for development if needed.

Easy to add custom serializators

Currently quite painful to add custom (say xml) format middlewares for parameters, responses & swagger docs.

Suggestion: Protocol or Map-based contract for custom serializators that can be added to compojure.api.middleware/api-middleware as parameters. This could (and should) also be hosted on the ring-middleware-format side.

Something like:

(def xml-format {:name :xml
                 :content-type "application/xml"
                 :input {:predicate xml-request?
                         :parser read-xml
                         :charset read-charset-from-xml-tag
                 :output {:encoder write-xml
                          :charset :default}})

(api-middlware api :formats [:json-kw :transit-json xml-format])

Modifying basePath and resourcePath

I'm having trouble figuring out where I might modify basePath and resourcePath swagger elements from the defapi definition. Our compojure-api project works great in development on localhost; however, when we deploy, the basePath should be something akin to http://www.example.com:3000, instead of http://localhost:3000, so the swagger-ui breaks when trying making a call.

In addition, our paths are proxied, so a call to route /pizza becomes /api/pizza once deployed. If I understand correctly, I need to modify the resourcePath to correct it. Any suggestions would be appreciated.

(defapi app
   (swagger-ui)
  (swagger-docs
    :title "Our REST API"
    :description "Description"
    :termsOfServiceUrl "Terms of Service URL Here"
    :contact "[email protected]"
    :license "License Info Here"
    :licenseUrl "License URL Here")
  (swaggered "Pizzas"
    (context "/pizza" [] pizza/handler) ; to a defroutes* called handler
    (context "/pizza2" [] pizza2/handler)))

Compojure-api does not print stack traces

Hello,

I don't know if there's a rationale for this, but the exception handlers defined in compojure.api.middleware/handle-req-error don't print stack traces, and debugging from the rather laconic response body from requests is harder than it needs to be. I would suggest that a simple (.printStackTrace e) be added to the beginning of handle-req-error in order to help this.

[Question] Accept parameter as well as JSON body on POST

Sorry, I couldn't find anything related in the README and examples: since I am currently in the transfer from homegrown handler style porting to compujore-api I am wondering how can I more elegantly allow to accept incoming POST request data to be either given via request parameters or via JSON body, in my old code base I had assignments around the lines:

(let [user-alias (or (:alias params) (:alias body))

Streaming Response with on-close callback

Hi! Not sure where to put this question / issue...

I've got a situation where I want to have a compojure-api end point to monitor some pub-sub data.

What I'd like to do would be this:

(GET* "/activity" []
  ...
   (let [ listener (create-my-listener (fn [msg] (send! msg))) ]
   (on-close (fn (unsubscribe-listener listener))

Basically, I just keep the socket open, sending data as it comes. I'll never close the socket, but the client may. Is this possible in clojure-api -- could it be? I think we'll be doing a good bit of this in our API.

I saw some comments that Ring would accept an InputStream, but no idea how I could get notified that it closed - to clean things up.

this is terrible

macros shouldn't have side effects, they should expand in to code that has side effects. if a macro does have side effects it is going to break things if someone aot compiles it.

packaging up your own clojure.walk namespace and praying to class path gods that it is chosen over clojure's clojure.walk is terrible.

I normally wouldn't care, but when some poor confused soul stumbles into the clojure irc channel and I see they have yoked themselves to something terrible because they don't know any better it irks me.

please either put a big "this is not intended for others to use" disclaimer on this, or become more familiar with clojure and the jvm ecosystem before attempting to package up code for others to use.

http://clojure-log.n01se.net/#11:51

nz was trying to use this library and having a hard time because if he aot'ed and built an uberjar whatever side effects your macros have would happen at macro expansion time, just prior to compile time, and then when the compiled bytecode was run on another machine the macros were already expanded away, so their side effects never happen

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.