Repository: oliyh/martian (clojure)

Issue:
martian-re-frame martian instance has empty `:handlers`
Using martian-re-frame 0.1.13 and friends. The [code sample in the martian-re-frame documentation](https://cljdoc.org/d/martian-re-frame/martian-re-frame/0.1.13/doc/re-frame-integration) fails with `Uncaught Error: No protocol method ISet.-disjoin defined for type cljs.core/List: ([:my-operation-id {... params ...} :http-success :http-failure])`. I noted issue #57 and now have martian initialization completing before performing a re-frame dispatch, but that still results `::http-failure` being called with `:unknown-route` followed by the aforementioned error.

Inspecting the martian value in re-frame's `app-db` reveals that the `:handlers` key holds a blank seq. Wondering if this could be source of trouble, I tried feeding`martian.re-frame/init` various swagger json documents, such as ones from the official examples repo, our own that we have on hand here, and `martian/core/test-resources/swagger.json` and `martian/core/test-resources/swagger.json`. Every document resulted in an empty `:handlers`. The `:base-url` changes appropriately, and the `:interceptors` seem to contain everything that it should, but `:handlers` is conspicuously empty. This is the case in Firefox and Google Chrome.

At the Clojure-based lein repl, these JSON files uniformly produce a perfectly usable martian, just as advertised, and `:handlers` is flush with the correct entries. Also, martian's own re-frame test suite `cd re-frame && lein test` passes 100%. I ran the test suite with the dependency versions in `project.clj` as tagged in martian `v0.1.13` and also separately with the versions we are using here.

Pull request description:
Keywordizing broke the parameters for OpenAPI parsing. The test didn't have any parameters, so it wasn't spotted. Closes #93

Patch:
diff --git a/core/src/martian/openapi.cljc b/core/src/martian/openapi.cljc
index 1505564d..40db1d9d 100644
--- a/core/src/martian/openapi.cljc
+++ b/core/src/martian/openapi.cljc
@@ -115,7 +115,7 @@
           ;; which aren't the associated OPTIONS call.
           :when (and (:operationId definition)
                      (not= :options method))
-          :let [parameters (group-by :in (:parameters definition))
+          :let [parameters (group-by (comp keyword :in) (:parameters definition))
                 body       (process-body (:requestBody definition) components (:encodes content-types))
                 responses  (process-responses (:responses definition) components (:decodes content-types))]]
       {:path-parts         (vec (tokenise-path url))


Test patch:
diff --git a/core/test-resources/openapi2.json b/core/test-resources/openapi2.json
new file mode 100644
index 00000000..7d7f183f
--- /dev/null
+++ b/core/test-resources/openapi2.json
@@ -0,0 +1,55 @@
+{
+  "openapi": "3.0.1",
+  "info": {
+    "title": "My API",
+    "version": "1"
+  },
+  "paths": {
+    "/project/{projectKey}": {
+      "get": {
+        "summary": "Get specific values from a configuration for a specific project",
+        "operationId": "getProjectConfiguration",
+        "parameters": [
+          {
+            "name": "projectId",
+            "in": "path",
+            "description": "Project ID",
+            "required": true,
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          },
+          {
+            "name": "key",
+            "in": "query",
+            "description": "Obtains values corresponding to these keys from a project's configuration",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "Configuration for the specified project",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "string",
+                  "format": "string"
+                }
+              }
+            }
+          },
+          "403": {
+            "description": "Refusing access to requested resource, perhaps due to insufficient privilege"
+          },
+          "404": {
+            "description": "Requested resource was not found"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/core/test/martian/openapi_test.cljc b/core/test/martian/openapi_test.cljc
index e0a77b0b..f6b738a1 100644
--- a/core/test/martian/openapi_test.cljc
+++ b/core/test/martian/openapi_test.cljc
@@ -60,3 +60,28 @@
                (->> (filter #(= (:route-name %) :update-pet)))
                first
                (select-keys [:consumes :produces]))))))
+
+(deftest openapi-parameters-test
+  (testing "parses parameters"
+    (is (= {:description nil,
+            :method :get,
+            :produces ["application/json"],
+            :path-schema {:projectId s/Str},
+            :query-schema {(s/optional-key :key) s/Str},
+            :form-schema {},
+            :path-parts ["/project/" :projectKey],
+            :headers-schema {},
+            :consumes [nil],
+            :summary "Get specific values from a configuration for a specific project",
+            :body-schema nil,
+            :route-name :get-project-configuration,
+            :response-schemas
+            [{:status (s/eq 200), :body s/Str}
+             {:status (s/eq 403), :body nil}
+             {:status (s/eq 404), :body nil}]}
+           (-> (parse-string (slurp (io/resource "openapi2.json")))
+               (openapi->handlers {:encodes ["application/json" "application/octet-stream"]
+                                   :decodes ["application/json" "application/octet-stream"]})
+               (->> (filter #(= (:route-name %) :get-project-configuration)))
+               first
+               (dissoc :openapi-definition))))))