Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-value query params are being duplicated #5

Open
cmateiasmypulse opened this issue Mar 8, 2022 · 0 comments
Open

Multi-value query params are being duplicated #5

cmateiasmypulse opened this issue Mar 8, 2022 · 0 comments

Comments

@cmateiasmypulse
Copy link

Hi,

I am having an issue with the query parameter values being duplicated when building a liberator resource using with-json-mixin. I'm not entirely sure if that's a bug or not, but hopefully we can get more details on this.
What happens is that when a get request is being made with a multi-value query string ie https://some.url?thing=1&thing=2 I would have expected the parsed params to be something like [1 2] but what I see instead is [1 2 1 2 1 2]. As you would expect, if there are any specs configured the request would not be valid.

To have a better understanding on what is happening I have the following tests:

(deftest with-params-parsed-as-json
  (testing "using with-json-mixing"
    (let [resource
          (->
            (l/build-resource
              (json/with-json-mixin)
              {:allowed-methods
               [:get]

               :handle-ok
               (fn [{:keys [request]}]
                 (let [params (:params request)]
                   {:correct-params
                    (and
                      (= (:thing1 params) [1 2 3])
                      (= (:thing2 params)
                        {:key-1 "value"})
                      (= (:thing3 params) [1 2 1 2 1 2]))}))})
            (keyword-params/wrap-keyword-params)
            (params/wrap-params))
          request (->
                    (ring/request :get "/")
                    (ring/header "Content-Type" json/json-media-type)
                    (ring/query-string
                      "thing1=[1,2,3]&thing2={\"key1\":\"value\"}&thing3=1&thing3=2"))
          response (call-resource resource request)]
      (is (= 200 (:status response)))
      (is (= {:correct-params true}
            (:body response)))))

  (testing "without json encoder/decoder"
    (let [resource
          (->
            (l/build-resource
              (json/with-json-media-type)
              (json/with-body-parsed-as-json)
              (json/with-params-parsed-as-json)
              {:allowed-methods
               [:get]

               :handle-ok
               (fn [{:keys [request]}]
                 (let [params (:params request)]
                   {:correct-params
                    (and
                      (= (:thing1 params) [1 2 3])
                      (= (:thing2 params)
                         {:key-1 "value"})
                      (= (:thing3 params) [1 2]))}))})
            (keyword-params/wrap-keyword-params)
            (params/wrap-params))
          request (->
                    (ring/request :get "/")
                    (ring/header "Content-Type" json/json-media-type)
                    (ring/query-string
                      "thing1=[1,2,3]&thing2={\"key1\":\"value\"}&thing3=1&thing3=2"))
          response (call-resource resource request)]
      (is (= 200 (:status response)))
      (is (= {:correct-params true}
             (:body response))))))

As you can see from above the issue occurs when using with-json-mixin, but when leaving out with-json-encoder and with-json-decoder everything seems to be working as expected. It may seem as the issue is somehow related to those 2 mixings even though they're not doing a lot apart from adding a map to the context. Something strange seems to happen within the merge-actions function as this is the point where those params values are being duplicated

(defn merge-actions
  [left right]
  (fn [context]
    (letfn [(execute-and-update [context f]
              (liberator/update-context context (f context)))]
      (let [left-result (execute-and-update context
                          (liberator-util/make-function left))
            right-result (execute-and-update left-result
                           (liberator-util/make-function right))]
        right-result))))

and more specifically within the liberator update-context which seems to concatenate the vectors within the params unless it's explicitly decorated with the replace metadata as mentioned here. The params are decorated with the replace metadata, but within with-params-parsed-as-json that's the last mixin to be added as part of with-json-mixin and by then the params values have already been duplicated.

As a workaround I've created an internal mixin that takes care of decorating the params with the replace metadata and placed it before with-json-mixin:

(defn- with-replaceable-params []
  {:initialize-context
   (fn [{:keys [request]}]
     {:request {:params (with-meta (:params request) {:replace true})}})})

(defn resource-handler
  [dependencies & {:as overrides}]
  (build-resource
    (with-replaceable-params)
    (with-json-mixin dependencies)    
    overrides))

It would be great to see your thoughts on this, and if you need more details please let me know.

Thanks,
Cris

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant