Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. This change log follows the conventions
of [keepachangelog.com](http://keepachangelog.com/).

## 7.4.3 - 2026-01-11

### Changed

- Breaking Change: Error handling for request body validation has been updated.
- The error code for schema validation failures is now `"invalid-request-body-payload"`.
- Error messages for invalid request bodies are improved for clarity.
- Error details in responses are now consistently stringified.
- Schema Coercion: String values in JSON request bodies are now automatically coerced to keywords when the schema
expects a keyword, improving compatibility with `EqSchema`.

## 6.4.3 - 2026-01-02

### Added
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject net.clojars.macielti/service-component "6.4.3"
(defproject net.clojars.macielti/service-component "7.4.3"

:description "Service Component is a Pedestal service Integrant component"

Expand Down
16 changes: 12 additions & 4 deletions src/service_component/interceptors.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[io.pedestal.interceptor :as pedestal.interceptor]
[io.pedestal.interceptor.error :as error]
[schema.coerce :as coerce]
[schema.core]
[schema.utils]
[service-component.error :as common-error])
(:import (java.time LocalDate)))
Expand Down Expand Up @@ -35,17 +36,24 @@
:enter (fn [context]
(assoc-in context [:request :components] system-components))}))

(defn ^:private json-matcher
[schema]
(or (when (and (instance? schema.core.EqSchema schema)
(keyword? (:v schema)))
(fn [x] (if (string? x) (keyword x) x)))
(coerce/json-coercion-matcher schema)))

(defn wire-in-body-schema [schema]
(pedestal.interceptor/interceptor
{:name ::schema-body-in-interceptor
:enter (fn [{{:keys [json-params]} :request :as context}]
(let [coercer-fn (coerce/coercer schema coerce/json-coercion-matcher)
(let [coercer-fn (coerce/coercer schema json-matcher)
coercion-result (coercer-fn json-params)]
(when (schema.utils/error? coercion-result)
(common-error/http-friendly-exception 422
"invalid-schema-in"
"The system detected that the received data is invalid"
(-> (schema.utils/error-val coercion-result) h/explain)))
"invalid-request-body-payload"
"The system detected that the received data is invalid."
(-> (schema.utils/error-val coercion-result) h/explain str)))
(assoc-in context [:request :json-params] coercion-result)))}))

(def http-request-in-handle-timing-interceptor
Expand Down
6 changes: 3 additions & 3 deletions test/integration/service_component_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,22 @@
(reset! test-state nil)
(is (match? {:status 422
:headers {"Content-Type" "application/json;charset=UTF-8"}
:body "{\"error\":\"invalid-schema-in\",\"message\":\"The system detected that the received data is invalid\",\"detail\":{\"test\":\"Missing required key\"}}"}
:body "{\"error\":\"invalid-request-body-payload\",\"message\":\"The system detected that the received data is invalid.\",\"detail\":\"{:test \\\"Missing required key\\\"}\"}"}
(test/response-for connector :post "/schema-validation-interceptor-test"
:headers {:content-type "application/json"}
:body (json/encode {}))))

(is (match? {:status 422
:headers {"Content-Type" "application/json;charset=UTF-8"}
:body "{\"error\":\"invalid-schema-in\",\"message\":\"The system detected that the received data is invalid\",\"detail\":{\"hello\":\"Invalid key.\",\"test\":\"Missing required key\"}}"}
:body "{\"error\":\"invalid-request-body-payload\",\"message\":\"The system detected that the received data is invalid.\",\"detail\":\"{:hello \\\"Invalid key.\\\", :test \\\"Missing required key\\\"}\"}"}
(test/response-for connector :post "/schema-validation-interceptor-test"
:headers {:content-type "application/json"}
:body (json/encode {:hello :world}))))

(reset! test-state nil)
(is (match? {:status 422
:headers {"Content-Type" "application/json;charset=UTF-8"}
:body "{\"error\":\"invalid-schema-in\",\"message\":\"The system detected that the received data is invalid\",\"detail\":\"The value must be a map, but was '' instead.\"}"}
:body "{\"error\":\"invalid-request-body-payload\",\"message\":\"The system detected that the received data is invalid.\",\"detail\":\"The value must be a map, but was '' instead.\"}"}
(test/response-for connector :post "/schema-validation-interceptor-test")))

(reset! test-state nil)
Expand Down
14 changes: 14 additions & 0 deletions test/unit/service_component/interceptors_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,17 @@

(is (match? {:request {:query-params {:reference-date (LocalDate/of 1998 12 26)}}}
(chain/execute {:request {:query-params {:reference-date "1998-12-26"}}} [(interceptors/query-params-schema QueryParamsWithDate)]))))

(schema.core/defschema EqKeywordSchema
{:type (schema.core/eq :hello-world)})

(s/deftest wire-in-body-schema-test
(is (match? {:request {:json-params {:type :hello-world}}}
(chain/execute {:request {:json-params {:type "hello-world"}}} [(interceptors/wire-in-body-schema EqKeywordSchema)])))

(let [ex (is (thrown? ExceptionInfo (chain/execute {:request {:json-params {:type "test"}}} [(interceptors/wire-in-body-schema EqKeywordSchema)])))]
(is (match? {:status 422
:error "invalid-request-body-payload"
:message "The system detected that the received data is invalid."
:detail "{:type [not [= :hello-world :test]]}"}
(ex-data ex)))))