|
1 | 1 | (ns compojure.api.meta |
2 | 2 | (:require [clojure.edn :as edn] |
| 3 | + [clojure.walk :as walk] |
3 | 4 | [compojure.api.common :refer [extract-parameters]] |
4 | 5 | [compojure.api.middleware :as mw] |
5 | 6 | [compojure.api.routes :as routes] |
|
295 | 296 | " (ok {:name \"Kirsi\"))"))) |
296 | 297 |
|
297 | 298 | (defmethod restructure-param :return [_ schema acc] |
298 | | - (let [response (convert-return schema)] |
| 299 | + (let [response (convert-return schema) |
| 300 | + g (gensym 'response)] |
299 | 301 | (-> acc |
300 | | - (update-in [:info :public :responses] (fnil conj []) response) |
301 | | - (update-in [:responses] (fnil conj []) response)))) |
| 302 | + (update-in [:outer-lets] into [g response]) |
| 303 | + (update-in [:info :public :responses] (fnil conj []) g) |
| 304 | + (update-in [:responses] (fnil conj []) g)))) |
302 | 305 |
|
303 | 306 | ;; |
304 | 307 | ;; responses |
|
321 | 324 | " (bad-request \"kosh\"))"))) |
322 | 325 |
|
323 | 326 | (defmethod restructure-param :responses [_ responses acc] |
324 | | - (-> acc |
325 | | - (update-in [:info :public :responses] (fnil conj []) responses) |
326 | | - (update-in [:responses] (fnil conj []) responses))) |
| 327 | + (let [g (gensym 'responses)] |
| 328 | + (-> acc |
| 329 | + (update :outer-lets into [g responses]) |
| 330 | + (update-in [:info :public :responses] (fnil conj []) g) |
| 331 | + (update-in [:responses] (fnil conj []) g)))) |
327 | 332 |
|
328 | 333 | ;; |
329 | 334 | ;; body |
|
338 | 343 | " :body [body User]" |
339 | 344 | " (ok body))"))) |
340 | 345 |
|
341 | | -(defmethod restructure-param :body [_ [value schema] acc] |
342 | | - (-> acc |
343 | | - (update-in [:lets] into [value (src-coerce! schema :body-params :body false)]) |
344 | | - (assoc-in [:info :public :parameters :body] schema))) |
| 346 | +(defmethod restructure-param :body [_ [value schema :as bv] acc] |
| 347 | + (when-not (= "true" (System/getProperty "compojure.api.meta.allow-bad-body")) |
| 348 | + (assert (= 2 (count bv)) |
| 349 | + (str ":body should be [sym schema], provided: " bv |
| 350 | + "\nDisable this check with -Dcompojure.api.meta.allow-bad-body=true"))) |
| 351 | + (let [g (gensym 'body-schema)] |
| 352 | + (-> acc |
| 353 | + (update :outer-lets into [g schema]) |
| 354 | + (update-in [:lets] into [value (src-coerce! g :body-params :body false)]) |
| 355 | + (assoc-in [:info :public :parameters :body] g)))) |
345 | 356 |
|
346 | 357 | ;; |
347 | 358 | ;; query |
|
356 | 367 | " :query [params {:q s/Str, :max s/Int}]" |
357 | 368 | " (ok params))"))) |
358 | 369 |
|
359 | | -(defmethod restructure-param :query [_ [value schema] acc] |
360 | | - (-> acc |
361 | | - (update-in [:lets] into [value (src-coerce! schema :query-params :string)]) |
362 | | - (assoc-in [:info :public :parameters :query] schema))) |
| 370 | +(defmethod restructure-param :query [_ [value schema :as bv] acc] |
| 371 | + (when-not (= "true" (System/getProperty "compojure.api.meta.allow-bad-query")) |
| 372 | + (assert (= 2 (count bv)) |
| 373 | + (str ":query should be [sym schema], provided: " bv |
| 374 | + "\nDisable this check with -Dcompojure.api.meta.allow-bad-query=true"))) |
| 375 | + (let [g (gensym 'query-schema)] |
| 376 | + (-> acc |
| 377 | + (update :outer-lets into [g schema]) |
| 378 | + (update-in [:lets] into [value (src-coerce! g :query-params :string)]) |
| 379 | + (assoc-in [:info :public :parameters :query] g)))) |
363 | 380 |
|
364 | 381 | ;; |
365 | 382 | ;; headers |
|
374 | 391 | " :headers [headers HeaderSchema]" |
375 | 392 | " (ok headers))"))) |
376 | 393 |
|
377 | | -(defmethod restructure-param :headers [_ [value schema] acc] |
378 | | - (-> acc |
379 | | - (update-in [:lets] into [value (src-coerce! schema :headers :string)]) |
380 | | - (assoc-in [:info :public :parameters :header] schema))) |
| 394 | +(defmethod restructure-param :headers [_ [value schema :as bv] acc] |
| 395 | + (when-not (= "true" (System/getProperty "compojure.api.meta.allow-bad-headers")) |
| 396 | + (assert (= 2 (count bv)) |
| 397 | + (str ":headers should be [sym schema], provided: " bv |
| 398 | + "\nDisable this check with -Dcompojure.api.meta.allow-bad-headers=true"))) |
| 399 | + (let [g (gensym 'headers-schema)] |
| 400 | + (-> acc |
| 401 | + (update :outer-lets into [g schema]) |
| 402 | + (update-in [:lets] into [value (src-coerce! g :headers :string)]) |
| 403 | + (assoc-in [:info :public :parameters :header] g)))) |
381 | 404 |
|
382 | 405 | ;; |
383 | 406 | ;; body-params |
|
393 | 416 | " (ok {:total (+ x y)}))"))) |
394 | 417 |
|
395 | 418 | (defmethod restructure-param :body-params [_ body-params acc] |
396 | | - (let [schema (strict (fnk-schema body-params))] |
| 419 | + (let [schema (strict (fnk-schema body-params)) |
| 420 | + g (gensym 'body-params-schema)] |
397 | 421 | (-> acc |
398 | | - (update-in [:letks] into [body-params (src-coerce! schema :body-params :body)]) |
399 | | - (assoc-in [:info :public :parameters :body] schema)))) |
| 422 | + (update :outer-lets into [g schema]) |
| 423 | + (update-in [:letks] into [body-params (src-coerce! g :body-params :body)]) |
| 424 | + (assoc-in [:info :public :parameters :body] g)))) |
400 | 425 |
|
401 | 426 | ;; |
402 | 427 | ;; form-params |
|
413 | 438 | " (ok {:total (+ x y)}))"))) |
414 | 439 |
|
415 | 440 | (defmethod restructure-param :form-params [_ form-params acc] |
416 | | - (let [schema (strict (fnk-schema form-params))] |
| 441 | + (let [schema (strict (fnk-schema form-params)) |
| 442 | + g (gensym 'form-params-schema)] |
417 | 443 | (-> acc |
418 | | - (update-in [:letks] into [form-params (src-coerce! schema :form-params :string)]) |
419 | | - (update-in [:info :public :parameters :formData] st/merge schema) |
| 444 | + (update :outer-lets into [g schema]) |
| 445 | + (update-in [:letks] into [form-params (src-coerce! g :form-params :string)]) |
| 446 | + (update-in [:info :public :parameters :formData] #(if % (list `st/merge % g) g)) |
420 | 447 | (assoc-in [:info :public :consumes] ["application/x-www-form-urlencoded"])))) |
421 | 448 |
|
422 | 449 | ;; |
|
438 | 465 | " (ok (dissoc foo :tempfile)))"))) |
439 | 466 |
|
440 | 467 | (defmethod restructure-param :multipart-params [_ params acc] |
441 | | - (let [schema (strict (fnk-schema params))] |
| 468 | + (let [schema (strict (fnk-schema params)) |
| 469 | + g (gensym 'multipart-params-schema)] |
442 | 470 | (-> acc |
443 | | - (update-in [:letks] into [params (src-coerce! schema :multipart-params :string)]) |
444 | | - (update-in [:info :public :parameters :formData] st/merge schema) |
| 471 | + (update :outer-lets into [g schema]) |
| 472 | + (update-in [:letks] into [params (src-coerce! g :multipart-params :string)]) |
| 473 | + (update-in [:info :public :parameters :formData] #(if % (list `st/merge % g) g)) |
445 | 474 | (assoc-in [:info :public :consumes] ["multipart/form-data"])))) |
446 | 475 |
|
447 | 476 | ;; |
|
458 | 487 | " (ok {:total (+ x y)}))"))) |
459 | 488 |
|
460 | 489 | (defmethod restructure-param :header-params [_ header-params acc] |
461 | | - (let [schema (fnk-schema header-params)] |
| 490 | + (let [schema (fnk-schema header-params) |
| 491 | + g (gensym 'multipart-params-schema)] |
462 | 492 | (-> acc |
463 | | - (update-in [:letks] into [header-params (src-coerce! schema :headers :string)]) |
464 | | - (assoc-in [:info :public :parameters :header] schema)))) |
| 493 | + (update :outer-lets into [g schema]) |
| 494 | + (update-in [:letks] into [header-params (src-coerce! g :headers :string)]) |
| 495 | + (assoc-in [:info :public :parameters :header] g)))) |
465 | 496 |
|
466 | 497 | ;; |
467 | 498 | ;; :query-params |
|
477 | 508 | " (ok {:total (+ x y)}))"))) |
478 | 509 |
|
479 | 510 | (defmethod restructure-param :query-params [_ query-params acc] |
480 | | - (let [schema (fnk-schema query-params)] |
| 511 | + (let [schema (fnk-schema query-params) |
| 512 | + g (gensym 'multipart-params-schema)] |
481 | 513 | (-> acc |
482 | | - (update-in [:letks] into [query-params (src-coerce! schema :query-params :string)]) |
483 | | - (assoc-in [:info :public :parameters :query] schema)))) |
| 514 | + (update :outer-lets into [g schema]) |
| 515 | + (update-in [:letks] into [query-params (src-coerce! g :query-params :string)]) |
| 516 | + (assoc-in [:info :public :parameters :query] g)))) |
484 | 517 |
|
485 | 518 | ;; |
486 | 519 | ;; path-params |
|
496 | 529 | " (ok {:total (+ x y)}))"))) |
497 | 530 |
|
498 | 531 | (defmethod restructure-param :path-params [_ path-params acc] |
499 | | - (let [schema (fnk-schema path-params)] |
| 532 | + (let [schema (fnk-schema path-params) |
| 533 | + g (gensym 'form-params-schema)] |
500 | 534 | (-> acc |
501 | | - (update-in [:letks] into [path-params (src-coerce! schema :route-params :string)]) |
502 | | - (assoc-in [:info :public :parameters :path] schema)))) |
| 535 | + (update :outer-lets into [g schema]) |
| 536 | + (update-in [:letks] into [path-params (src-coerce! g :route-params :string)]) |
| 537 | + (assoc-in [:info :public :parameters :path] g)))) |
503 | 538 |
|
504 | 539 | ;; |
505 | 540 | ;; middleware |
|
568 | 603 |
|
569 | 604 | (defmethod help/help-for [:meta :coercion] [_ _] |
570 | 605 | (help/text |
571 | | - "Route-spesific overrides for coercion. See more on wiki:" |
| 606 | + "Route-specific overrides for coercion. See more on wiki:" |
572 | 607 | "https://github.com/metosin/compojure-api/wiki/Validation-and-coercion\n" |
573 | 608 | (help/code |
574 | 609 | "(POST \"/user\" []" |
|
577 | 612 | " (ok user))"))) |
578 | 613 |
|
579 | 614 | (defmethod restructure-param :coercion [_ coercion acc] |
580 | | - (-> acc |
581 | | - (assoc-in [:info :coercion] coercion) |
582 | | - (update-in [:middleware] conj [`mw/wrap-coercion coercion]))) |
| 615 | + (let [g (gensym 'coercion)] |
| 616 | + (-> acc |
| 617 | + (update :outer-lets into [g coercion]) |
| 618 | + (assoc-in [:info :coercion] g) |
| 619 | + (update-in [:middleware] conj [`mw/wrap-coercion g])))) |
583 | 620 |
|
584 | 621 | ;; |
585 | 622 | ;; Impl |
586 | 623 | ;; |
587 | 624 |
|
588 | 625 | (defmacro dummy-let |
589 | | - "Dummy let-macro used in resolving route-docs. not part of normal invokation chain." |
| 626 | + "Dummy let-macro used in resolving route-docs. not part of normal invocation chain." |
590 | 627 | [bindings & body] |
591 | 628 | (let [bind-form (vec (apply concat (for [n (take-nth 2 bindings)] [n nil])))] |
592 | 629 | `(let ~bind-form ~@body))) |
593 | 630 |
|
594 | 631 | (defmacro dummy-letk |
595 | | - "Dummy letk-macro used in resolving route-docs. not part of normal invokation chain." |
| 632 | + "Dummy letk-macro used in resolving route-docs. not part of normal invocation chain." |
596 | 633 | [bindings & body] |
597 | 634 | (reduce |
598 | 635 | (fn [cur-body-form [bind-form]] |
|
612 | 649 | [path route] |
613 | 650 | `(compojure.api.compojure-compat/make-context |
614 | 651 | ~(#'compojure.core/context-route path) |
615 | | - (constantly ~route))) |
| 652 | + (let [r# ~route] |
| 653 | + (fn [_#] r#)))) |
616 | 654 |
|
617 | 655 | (defn routing [handlers] |
618 | 656 | (if-let [handlers (seq (keep identity (flatten handlers)))] |
|
731 | 769 | (when (var? v) |
732 | 770 | (when (middleware-vars (symbol v)) |
733 | 771 | (let [[_ path route-arg & args] body |
734 | | - [options body] (extract-parameters args true) |
735 | | - [path-string lets arg-with-request] (destructure-compojure-api-request path route-arg) |
736 | | - {:keys [lets |
737 | | - letks |
738 | | - responses |
739 | | - middleware |
740 | | - info |
741 | | - swagger |
742 | | - body]} (reduce |
743 | | - (fn [acc [k v]] |
744 | | - (restructure-param k v (update-in acc [:parameters] dissoc k))) |
745 | | - {:lets lets |
746 | | - :letks [] |
747 | | - :responses nil |
748 | | - :middleware [] |
749 | | - :info {} |
750 | | - :body body} |
751 | | - options)] |
| 772 | + [options body] (extract-parameters args true)] |
752 | 773 | (static-body? &env body)))))))))) |
753 | 774 |
|
754 | 775 | (def route-middleware-vars (into #{} |
|
921 | 942 |
|
922 | 943 | {:keys [lets |
923 | 944 | letks |
| 945 | + outer-lets |
924 | 946 | responses |
925 | 947 | middleware |
926 | 948 | info |
|
930 | 952 | (restructure-param k v (update-in acc [:parameters] dissoc k))) |
931 | 953 | {:lets lets |
932 | 954 | :letks [] |
| 955 | + :outer-lets [] ;; lets around the call to map->Route |
933 | 956 | :responses nil |
934 | 957 | :middleware [] |
935 | 958 | :info {} |
|
942 | 965 | (-> info :public :static))) |
943 | 966 | "Cannot be both a :dynamic and :static context.") |
944 | 967 |
|
| 968 | + ;; I think it's ok if we have :outer-lets |
945 | 969 | bindings? (boolean (or (route-args? route-arg) (seq lets) (seq letks))) |
946 | 970 |
|
947 | 971 | _ (assert (not (and (-> info :public :static) |
|
1064 | 1088 | form `(compojure.core/let-request [~arg-with-request ~'+compojure-api-request+] ~form) |
1065 | 1089 | form `(fn [~'+compojure-api-request+] ~form) |
1066 | 1090 | form `(delay (flatten (~form {})))] |
1067 | | - form)] |
1068 | | - |
1069 | | - `(routes/map->Route |
1070 | | - {:path ~path-string |
1071 | | - :method ~method |
1072 | | - :info (merge-parameters ~info) |
1073 | | - :childs ~childs |
1074 | | - :handler ~form})) |
| 1091 | + form) |
| 1092 | + form `(routes/map->Route |
| 1093 | + {:path ~path-string |
| 1094 | + :method ~method |
| 1095 | + :info (merge-parameters ~info) |
| 1096 | + :childs ~childs |
| 1097 | + :handler ~form}) |
| 1098 | + form (if (seq outer-lets) `(let ~outer-lets ~form) form)] |
| 1099 | + form) |
1075 | 1100 |
|
1076 | 1101 | ;; endpoints |
1077 | 1102 | (let [form `(do ~@body) |
1078 | 1103 | form (if (seq letks) `(p/letk ~letks ~form) form) |
1079 | 1104 | form (if (seq lets) `(let ~lets ~form) form) |
1080 | 1105 | form (compojure.core/compile-route method path arg-with-request (list form)) |
1081 | | - form (if (seq middleware) `(compojure.core/wrap-routes ~form (mw/compose-middleware ~middleware)) form)] |
1082 | | - |
1083 | | - `(routes/map->Route |
1084 | | - {:path ~path-string |
1085 | | - :method ~method |
1086 | | - :info (merge-parameters ~info) |
1087 | | - :handler ~form}))))) |
| 1106 | + form (if (seq middleware) `(compojure.core/wrap-routes ~form (mw/compose-middleware ~middleware)) form) |
| 1107 | + form `(routes/map->Route |
| 1108 | + {:path ~path-string |
| 1109 | + :method ~method |
| 1110 | + :info (merge-parameters ~info) |
| 1111 | + :handler ~form}) |
| 1112 | + form (if (seq outer-lets) `(let ~outer-lets ~form) form)] |
| 1113 | + form)))) |
0 commit comments