From 5a817bbe56f7e9507b80b56b35d16a5ec00a64d4 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Mon, 21 Oct 2024 16:56:13 +0300 Subject: [PATCH 01/11] Add /api/sports-place-types and by type-code --- webapp/src/clj/lipas/backend/handler.clj | 129 +++++++++++++++++- webapp/src/clj/lipas/backend/legacy/api.clj | 17 +++ webapp/src/cljc/lipas/schema/core_legacy.cljc | 16 +++ 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 webapp/src/clj/lipas/backend/legacy/api.clj create mode 100644 webapp/src/cljc/lipas/schema/core_legacy.cljc diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index 2ab8f59df..0b84b5c86 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -21,6 +21,28 @@ [ring.middleware.params :as params] [ring.util.io :as ring-io] [taoensso.timbre :as log])) + (:require [clojure.java.io :as io] + [clojure.spec.alpha :as s] + [lipas.backend.core :as core] + [lipas.backend.jwt :as jwt] + [lipas.backend.legacy.api :as legacy.api] + [lipas.backend.middleware :as mw] + [lipas.backend.routes.v1-legacy :as v1-legacy] + [lipas.roles :as roles] + [lipas.schema.core] + [lipas.utils :as utils] + [muuntaja.core :as m] + [reitit.coercion.spec] + [reitit.ring :as ring] + [reitit.ring.coercion :as coercion] + [reitit.ring.middleware.exception :as exception] + [reitit.ring.middleware.multipart :as multipart] + [reitit.ring.middleware.muuntaja :as muuntaja] + [reitit.swagger :as swagger] + [reitit.swagger-ui :as swagger-ui] + [ring.middleware.params :as params] + [ring.util.io :as ring-io] + [taoensso.timbre :as log])) (defn exception-handler ([status type] @@ -70,7 +92,6 @@ [{:keys [db emailer search mailchimp aws ptv] :as ctx}] (ring/ring-handler (ring/router - [["/favicon.ico" {:get {:no-doc true @@ -720,6 +741,111 @@ (ptv-handler/routes ctx)] (v2/routes ctx)] + ;; PTV + ["/actions/get-ptv-integration-candidates" + {:post + {:no-doc false + :require-role :ptv/manage + :parameters {:body map?} + :handler + (fn [{:keys [body-params]}] + {:status 200 + :body (core/get-ptv-integration-candidates search body-params)})}}] + + ["/actions/generate-ptv-descriptions" + {:post + {:no-doc false + :require-role :ptv/manage + :parameters {:body map?} + :handler + (fn [{:keys [body-params]}] + {:status 200 + :body (core/generate-ptv-descriptions search body-params)})}}] + + ["/actions/generate-ptv-service-descriptions" + {:post + {:no-doc false + :require-role :ptv/manage + :parameters {:body map?} + :handler + (fn [{:keys [body-params]}] + {:status 200 + :body (core/generate-ptv-service-descriptions search body-params)})}}] + + ["/actions/save-ptv-service" + {:post + {:no-doc false + :require-role :ptv/manage + :parameters {:body map?} + :handler + (fn [{:keys [body-params]}] + {:status 200 + :body (core/upsert-ptv-service! body-params)})}}] + + ["/actions/fetch-ptv-services" + {:post + {:no-doc false + :require-role :ptv/manage + :parameters {:body map?} + :handler + (fn [{:keys [body-params]}] + {:status 200 + :body (core/fetch-ptv-services body-params)})}}] + + ["/actions/save-ptv-service-location" + {:post + {:no-doc false + :require-role :ptv/manage + :parameters {:body map?} + :handler + (fn [{:keys [body-params identity]}] + {:status 200 + :body (core/upsert-ptv-service-location! db search identity body-params)})}}] + + ["/actions/save-ptv-meta" + {:post + {:no-doc false + :require-role :ptv/manage + :parameters {:body map?} + :handler + (fn [{:keys [body-params identity]}] + {:status 200 + :body (core/save-ptv-integration-definitions db search identity body-params)})}}]] + + ;; legacy routes + ["/v1/api" + ["/swagger.json" + {:get + {:no-doc true + :swagger {:id ::legacy + :info {:title "Lipas-API (legacy) v1"} + :securityDefinitions + {:token-auth + {:type "apiKey" + :in "header" + :name "Authorization"}}} + :handler (swagger/create-swagger-handler)}}] + ["/sports-place-types" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.api/lang])} + :get + {:handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :en)] + {:status 200 + :body (legacy.api/sports-place-types locale)}))}}] + ["sports-place-types/:type-code" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.api/lang]) + :path {:type-code int?}} + :get + {:handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :en) + type-code (-> req :parameters :path :type-code)] + (println locale type-code) + {:status 200 + :body (legacy.api/sports-place-by-type-code locale type-code)}))}}]]] {:data {:coercion reitit.coercion.spec/coercion @@ -745,4 +871,5 @@ mw/privilege-middleware]}}) (ring/routes (swagger-ui/create-swagger-ui-handler {:path "/api/swagger-ui" :url "/api/swagger.json"}) + (swagger-ui/create-swagger-ui-handler {:path "/v1/api/swagger-ui" :url "/v1/api/swagger.json"}) (ring/create-default-handler)))) diff --git a/webapp/src/clj/lipas/backend/legacy/api.clj b/webapp/src/clj/lipas/backend/legacy/api.clj new file mode 100644 index 000000000..38ad07419 --- /dev/null +++ b/webapp/src/clj/lipas/backend/legacy/api.clj @@ -0,0 +1,17 @@ +(ns lipas.backend.legacy.api + (:require [lipas.data.types-old :as types-old] + [lipas.utils :as utils])) + +(defn- ->legacy-api [m lang] + (-> m + utils/->camel-case-keywords + (select-keys [:typeCode :name :description :geometryType :subCategory]) + (update :name lang) + (update :description lang))) + +(defn sports-place-types [lang] + (->> (vals types-old/all) + (map #(->legacy-api % lang)))) + +(defn sports-place-by-type-code [lang type-code] + (->legacy-api (types-old/all type-code) lang)) diff --git a/webapp/src/cljc/lipas/schema/core_legacy.cljc b/webapp/src/cljc/lipas/schema/core_legacy.cljc new file mode 100644 index 000000000..bbc0646de --- /dev/null +++ b/webapp/src/cljc/lipas/schema/core_legacy.cljc @@ -0,0 +1,16 @@ +(ns lipas.schema.core-legacy + (:require [clojure.spec.alpha :as s] + [lipas.schema.core])) + +(s/def :lipas-legacy.api.parameter/lang :lipas.api/lang) + +(s/def :lipas-legacy.api.response/sports-place-types + (s/keys :req-un [:lipas-legacy.sports-site/typeCode + :lipas.sports-site/name + :lipas.sports-site/description + :lipas-legacy.sports-site/geometryType + :lipas-legacy.sports-site/subCategory])) + +(s/def :lipas-legacy.sports-site/geometryType #{"Point" "LineString"}) +(s/def :lipas-legacy.sports-site/typeCode (s/int-in 1 5000)) +(s/def :lipas-legacy.sports-site/subCategory (s/int-in 1 5000)) From 8ae18742eeeef9ff6061950fa38f50330bdca97f Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Tue, 22 Oct 2024 15:39:04 +0300 Subject: [PATCH 02/11] Remove redudant import --- webapp/src/clj/lipas/backend/handler.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index 0b84b5c86..ea2667b7d 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -27,7 +27,6 @@ [lipas.backend.jwt :as jwt] [lipas.backend.legacy.api :as legacy.api] [lipas.backend.middleware :as mw] - [lipas.backend.routes.v1-legacy :as v1-legacy] [lipas.roles :as roles] [lipas.schema.core] [lipas.utils :as utils] From 7585b5f189ea12c671a0a8a7d242b4aba756d46d Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Fri, 1 Nov 2024 15:11:36 +0200 Subject: [PATCH 03/11] Remove auth --- webapp/src/clj/lipas/backend/handler.clj | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index ea2667b7d..50945d371 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -62,7 +62,6 @@ :user-not-found (exception-handler 404 :user-not-found) :email-not-found (exception-handler 404 :email-not-found) :reminder-not-found (exception-handler 404 :reminder-not-found) - :qbits.spandex/response-exception (exception-handler 500 :internal-server-error :print-stack) ;; Return 500 and print stack trace for exceptions that are not @@ -817,12 +816,7 @@ {:get {:no-doc true :swagger {:id ::legacy - :info {:title "Lipas-API (legacy) v1"} - :securityDefinitions - {:token-auth - {:type "apiKey" - :in "header" - :name "Authorization"}}} + :info {:title "Lipas-API (legacy) v1"}} :handler (swagger/create-swagger-handler)}}] ["/sports-place-types" {:swagger {:id ::legacy} @@ -842,7 +836,6 @@ (fn [req] (let [locale (or (-> req :parameters :query :lang keyword) :en) type-code (-> req :parameters :path :type-code)] - (println locale type-code) {:status 200 :body (legacy.api/sports-place-by-type-code locale type-code)}))}}]]] From c5315d443b29bd9174e700a6a8debc2c709b1e98 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Fri, 1 Nov 2024 15:12:58 +0200 Subject: [PATCH 04/11] Add properties to sports-place-type --- webapp/src/clj/lipas/backend/legacy/api.clj | 41 +++++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/webapp/src/clj/lipas/backend/legacy/api.clj b/webapp/src/clj/lipas/backend/legacy/api.clj index 38ad07419..979887289 100644 --- a/webapp/src/clj/lipas/backend/legacy/api.clj +++ b/webapp/src/clj/lipas/backend/legacy/api.clj @@ -1,17 +1,44 @@ (ns lipas.backend.legacy.api - (:require [lipas.data.types-old :as types-old] - [lipas.utils :as utils])) + (:require + [lipas.data.prop-types :as prop-types] + [lipas.data.types :as types] + [lipas.utils :as utils])) + +(defn fill-properties [m] + (reduce (fn [acc k] (assoc-in acc [:props k] (prop-types/all k))) + m + (-> m :props keys))) + + +(defn localize-props [m locale prop-keys] + (reduce (fn [acc k] + (-> acc + (update-in [:props k :description] locale) + (update-in [:props k :name] locale))) + m + prop-keys)) + +(defn- localize [m lang] + (let [prop-keys (-> m :props keys)] + (-> m + (update :name lang) + (update :description lang) + (localize-props lang prop-keys)))) (defn- ->legacy-api [m lang] (-> m - utils/->camel-case-keywords - (select-keys [:typeCode :name :description :geometryType :subCategory]) + (fill-properties) + (localize lang) (update :name lang) - (update :description lang))) + (update :description lang) + utils/->camel-case-keywords)) + + +(prop-types/all :track-length-m) (defn sports-place-types [lang] - (->> (vals types-old/all) + (->> (vals types/all) (map #(->legacy-api % lang)))) (defn sports-place-by-type-code [lang type-code] - (->legacy-api (types-old/all type-code) lang)) + (->legacy-api (types/all type-code) lang)) From 2959b4ec596f04f5806592ee6dbb08e4c85f9fc3 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Mon, 4 Nov 2024 17:27:35 +0200 Subject: [PATCH 05/11] add stuff to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c89189f86..e5a60e1b3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ webapp/.lsp/.cache webapp/out webapp/.shadow-cljs webapp/.lein-repl-history -webapp/.user.edn +.clj-kondo +.lsp \ No newline at end of file From eafc7949cbb0e3e85a39f2f658fcdc120f60cd1b Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Mon, 4 Nov 2024 17:29:07 +0200 Subject: [PATCH 06/11] Add properties to sports-place-type by type code --- webapp/src/clj/lipas/backend/legacy/api.clj | 38 ++++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/webapp/src/clj/lipas/backend/legacy/api.clj b/webapp/src/clj/lipas/backend/legacy/api.clj index 979887289..b71ce20e0 100644 --- a/webapp/src/clj/lipas/backend/legacy/api.clj +++ b/webapp/src/clj/lipas/backend/legacy/api.clj @@ -4,41 +4,45 @@ [lipas.data.types :as types] [lipas.utils :as utils])) +(def keys-vec [:type-code :name :description :geometry-type :sub-category]) + (defn fill-properties [m] (reduce (fn [acc k] (assoc-in acc [:props k] (prop-types/all k))) m (-> m :props keys))) -(defn localize-props [m locale prop-keys] +(defn localize-properties [m locale] (reduce (fn [acc k] (-> acc (update-in [:props k :description] locale) - (update-in [:props k :name] locale))) - m - prop-keys)) + (update-in [:props k :name] locale))) m (-> m :props keys))) (defn- localize [m lang] - (let [prop-keys (-> m :props keys)] - (-> m - (update :name lang) - (update :description lang) - (localize-props lang prop-keys)))) + (-> m + (update :name lang) + (update :description lang))) -(defn- ->legacy-api [m lang] + +(defn- ->legacy-api-with-properties [m lang] (-> m (fill-properties) + (select-keys (conj keys-vec :props)) + (update :props #(-> % (dissoc :schoolUse? :freeUse?))) + (localize-properties lang) (localize lang) - (update :name lang) - (update :description lang) utils/->camel-case-keywords)) +(defn- ->legacy-api [m lang] + (-> m + (select-keys keys-vec) + (localize lang) + utils/->camel-case-keywords)) - -(prop-types/all :track-length-m) (defn sports-place-types [lang] - (->> (vals types/all) - (map #(->legacy-api % lang)))) + (->> (vals types/all) + (map #(->legacy-api % lang)))) (defn sports-place-by-type-code [lang type-code] - (->legacy-api (types/all type-code) lang)) + (-> (types/all type-code) + (->legacy-api-with-properties lang))) From 64d6c50addfc329a913ebf5bf9ca04f6ef20a6b0 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Mon, 18 Nov 2024 20:14:42 +0200 Subject: [PATCH 07/11] Implement categories --- webapp/src/clj/lipas/backend/handler.clj | 13 ++++++++-- webapp/src/clj/lipas/backend/legacy/api.clj | 28 ++++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index 50945d371..46ba950f8 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -827,7 +827,7 @@ (let [locale (or (-> req :parameters :query :lang keyword) :en)] {:status 200 :body (legacy.api/sports-place-types locale)}))}}] - ["sports-place-types/:type-code" + ["/sports-place-types/:type-code" {:swagger {:id ::legacy} :parameters {:query (s/keys :opt-un [:lipas.api/lang]) :path {:type-code int?}} @@ -837,7 +837,16 @@ (let [locale (or (-> req :parameters :query :lang keyword) :en) type-code (-> req :parameters :path :type-code)] {:status 200 - :body (legacy.api/sports-place-by-type-code locale type-code)}))}}]]] + :body (legacy.api/sports-place-by-type-code locale type-code)}))}}] + ["/categories" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.api/lang])} + :get + {:handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :en)] + {:status 200 + :body (legacy.api/categories locale)}))}}]]] {:data {:coercion reitit.coercion.spec/coercion diff --git a/webapp/src/clj/lipas/backend/legacy/api.clj b/webapp/src/clj/lipas/backend/legacy/api.clj index b71ce20e0..06a90791d 100644 --- a/webapp/src/clj/lipas/backend/legacy/api.clj +++ b/webapp/src/clj/lipas/backend/legacy/api.clj @@ -39,10 +39,30 @@ (localize lang) utils/->camel-case-keywords)) -(defn sports-place-types [lang] +(defn sports-place-types [locale] (->> (vals types/all) - (map #(->legacy-api % lang)))) + (map #(->legacy-api % locale)))) -(defn sports-place-by-type-code [lang type-code] +(defn sports-place-by-type-code [locale type-code] (-> (types/all type-code) - (->legacy-api-with-properties lang))) + (->legacy-api-with-properties locale))) + +(defn collect-sport-place-types [sub-category-type-code] + (->> (vals types/all) + (filter #(= (% :sub-category) sub-category-type-code)) + (map :type-code))) + +(defn- collect-subcategories [type-code locale] + (->> + (vals types/sub-categories) + (filter #(= (% :main-category) (str type-code))) + (map (fn [x] + {:typeCode (x :type-code) + :name (-> x :name locale) + :sportsPlaceTypes (collect-sport-place-types (x :type-code))})))) + +(defn categories [locale] + (mapv (fn [cat] {:name (-> cat :name locale) + :typeCode (cat :type-code) + :subCategories (collect-subcategories (cat :type-code) locale)}) + (vals types/main-categories))) From 04f944ab3800bf329e28274542517b514fc694b5 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Mon, 25 Nov 2024 19:46:13 +0200 Subject: [PATCH 08/11] Add schema for legacy sports places --- webapp/src/clj/lipas/backend/handler.clj | 58 +++++++++++++++-- webapp/src/clj/lipas/backend/legacy/api.clj | 5 ++ webapp/src/cljc/lipas/schema/core.cljc | 72 +++++++++++++++++++++ 3 files changed, 129 insertions(+), 6 deletions(-) diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index 46ba950f8..00b42eeca 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -817,14 +817,58 @@ {:no-doc true :swagger {:id ::legacy :info {:title "Lipas-API (legacy) v1"}} + :tags [{:name "sport-places" + :description "Sport places"} + {:name "sport-place-types" + :description "Sport place types"}] :handler (swagger/create-swagger-handler)}}] + ["/sports-places" + {:swagger {:id ::legacy} + :parameters {:query :lipas.legacy.api/search-params} + :get + {:tags ["sport-places"] + :handler + (fn [{:keys [parameters]}] + (let [spec :lipas.legacy.api/search-params + search-params (-> parameters :query) + valid? (s/valid? spec search-params)] + (if valid? + {:status 200 + :body (legacy.api/get-sports-places search-params)} + {:status 400 + :body (s/explain-data spec search-params)})))}}] + ["/sports-places/:sports-place-id" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.api/lang]) + :path {:sports-place-id int?}} + :get + {:tags ["sport-places"] + :handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi) + sports-place-id (-> req :parameters :path :sports-place-id)] + {:status 200 + :body (str "hello world" sports-place-id)}))}}] + ["/deleted-sports-places" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.legacy.api/since])} + :get + {:tags ["sport-places"] + :handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi) + since (or (-> req :parameters :query :since) "1984-01-01T00:00:00")] + {:status 200 + :body (str "hello world" since locale)}))}}] + ["/sports-place-types" {:swagger {:id ::legacy} :parameters {:query (s/keys :opt-un [:lipas.api/lang])} :get - {:handler + {:tags ["sport-place-types"] + :handler (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :en)] + (let [locale (or (-> req :parameters :query :lang keyword) :fi)] {:status 200 :body (legacy.api/sports-place-types locale)}))}}] ["/sports-place-types/:type-code" @@ -832,19 +876,21 @@ :parameters {:query (s/keys :opt-un [:lipas.api/lang]) :path {:type-code int?}} :get - {:handler + {:tags ["sport-place-types"] + :handler (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :en) + (let [locale (or (-> req :parameters :query :lang keyword) :fi) type-code (-> req :parameters :path :type-code)] {:status 200 :body (legacy.api/sports-place-by-type-code locale type-code)}))}}] ["/categories" - {:swagger {:id ::legacy} + {:tags ["sport-place-types"] + :swagger {:id ::legacy} :parameters {:query (s/keys :opt-un [:lipas.api/lang])} :get {:handler (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :en)] + (let [locale (or (-> req :parameters :query :lang keyword) :fi)] {:status 200 :body (legacy.api/categories locale)}))}}]]] diff --git a/webapp/src/clj/lipas/backend/legacy/api.clj b/webapp/src/clj/lipas/backend/legacy/api.clj index 06a90791d..03e8c2add 100644 --- a/webapp/src/clj/lipas/backend/legacy/api.clj +++ b/webapp/src/clj/lipas/backend/legacy/api.clj @@ -66,3 +66,8 @@ :typeCode (cat :type-code) :subCategories (collect-subcategories (cat :type-code) locale)}) (vals types/main-categories))) + +(defn get-sports-places [params] + ;; TODO collect params and query ES + params) + diff --git a/webapp/src/cljc/lipas/schema/core.cljc b/webapp/src/cljc/lipas/schema/core.cljc index d53685879..782e769ff 100644 --- a/webapp/src/cljc/lipas/schema/core.cljc +++ b/webapp/src/cljc/lipas/schema/core.cljc @@ -2066,6 +2066,78 @@ :lipas.sports-site/type (s/keys :req-un [:lipas.football.type/type-code]))) +;;; Legacy API ;;; + +(s/def :lipas.legacy.api/closeToLon (int-in -180 180)#_:lipas.location.coordinates/lon) +(s/def :lipas.legacy.api/closeToLat (int-in -180 180)#_:lipas.location.coordinates/lat) +(s/def :lipas.legacy.api/modifiedAfter :lipas/timestamp) +(def legacy-fields #{"properties" + "schoolUse" + "email" + "type.name" + "reservationsLink" + "location.sportsPlaces" + "renovationYears" + "admin" + "location.coordinates.tm35fin" + "www" + "location.geometries" + "name" + "type.typeCode" + "location.locationId" + "constructionYear" + "freeUse" + "location.city.name" + "lastModified" + "marketingName" + "location.postalCode" + "location.postalOffice" + "location.city.cityCode" + "phoneNumber" + "location.neighborhood" + "owner" + "location.coordinates.wgs84" + "location.address"}) +(s/def :lipas.legacy.api/field* legacy-fields) +(s/def :lipas.legacy.api/field + (st/spec + {:spec :lipas.legacy.api/field* + :swagger/type "string" + :swagger/enum legacy-fields})) +(s/def :lipas.legacy.api/fields + (s/coll-of :lipas.legacy.api/field + :min-count 0 + :distinct true + :into [])) +(s/def :lipas.legacy.api/retkikartta boolean?) +(s/def :lipas.legacy.api/closeToMatch #{"start-point" "any-point"}) +(s/def :lipas.legacy.api/page (int-in 0 999999)) +(s/def :lipas.legacy.api/closeToDistanceKm (number-in {:min 0 :max 99999})) +(s/def :lipas.legacy.api/harrastuspassi boolean?) +(s/def :lipas.legacy.api/pageSize (int-in 0 1000)) +(s/def :lipas.legacy.api/typeCodes (s/coll-of :lipas.sports-site.type/type-code + :distinct true + :min-count 0 + :into [])) +(s/def :lipas.legacy.api/cityCodes [:lipas.location.city/city-code]) +(s/def :lipas.legacy.api/searchString (str-in 3 100)) +(s/def :lipas.legacy.api/since :lipas/timestamp) +(s/def :lipas.legacy.api/search-params + (s/keys :opt-un [:lipas.legacy.api/closeToLon + :lipas.legacy.api/closeToLat + :lipas.api/lang + :lipas.legacy.api/modifiedAfter + :lipas.legacy.api/fields + :lipas.legacy.api/retkikartta + :lipas.legacy.api/closeToMatch + :lipas.legacy.api/page + :lipas.legacy.api/closeToDistanceKm + :lipas.legacy.api/harrastuspassi + :lipas.legacy.api/pageSize + :lipas.legacy.api/typeCodes + :lipas.legacy.api/cityCodes + :lipas.legacy.api/searchString])) + ;;; HTTP-API ;;; (s/def :lipas.api/revs #{"latest" "yearly"}) From 2a160d2468153a72518235a15d9328105bdb2139 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Mon, 2 Dec 2024 17:22:34 +0200 Subject: [PATCH 09/11] Working on legacy-api/get-sports-places, schema almost done! --- webapp/src/clj/lipas/backend/handler.clj | 10 +-- webapp/src/clj/lipas/backend/legacy/api.clj | 18 ++++- webapp/src/cljc/lipas/schema/core.cljc | 82 ++++++++++++++------- 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index 00b42eeca..80258ea25 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -25,7 +25,7 @@ [clojure.spec.alpha :as s] [lipas.backend.core :as core] [lipas.backend.jwt :as jwt] - [lipas.backend.legacy.api :as legacy.api] + [lipas.backend.legacy.api :as legacy-api] [lipas.backend.middleware :as mw] [lipas.roles :as roles] [lipas.schema.core] @@ -834,7 +834,7 @@ valid? (s/valid? spec search-params)] (if valid? {:status 200 - :body (legacy.api/get-sports-places search-params)} + :body (legacy-api/get-sports-places search search-params)} {:status 400 :body (s/explain-data spec search-params)})))}}] ["/sports-places/:sports-place-id" @@ -870,7 +870,7 @@ (fn [req] (let [locale (or (-> req :parameters :query :lang keyword) :fi)] {:status 200 - :body (legacy.api/sports-place-types locale)}))}}] + :body (legacy-api/sports-place-types locale)}))}}] ["/sports-place-types/:type-code" {:swagger {:id ::legacy} :parameters {:query (s/keys :opt-un [:lipas.api/lang]) @@ -882,7 +882,7 @@ (let [locale (or (-> req :parameters :query :lang keyword) :fi) type-code (-> req :parameters :path :type-code)] {:status 200 - :body (legacy.api/sports-place-by-type-code locale type-code)}))}}] + :body (legacy-api/sports-place-by-type-code locale type-code)}))}}] ["/categories" {:tags ["sport-place-types"] :swagger {:id ::legacy} @@ -892,7 +892,7 @@ (fn [req] (let [locale (or (-> req :parameters :query :lang keyword) :fi)] {:status 200 - :body (legacy.api/categories locale)}))}}]]] + :body (legacy-api/categories locale)}))}}]]] {:data {:coercion reitit.coercion.spec/coercion diff --git a/webapp/src/clj/lipas/backend/legacy/api.clj b/webapp/src/clj/lipas/backend/legacy/api.clj index 03e8c2add..f8013ed91 100644 --- a/webapp/src/clj/lipas/backend/legacy/api.clj +++ b/webapp/src/clj/lipas/backend/legacy/api.clj @@ -1,7 +1,9 @@ (ns lipas.backend.legacy.api (:require + [lipas.backend.core :as core] [lipas.data.prop-types :as prop-types] [lipas.data.types :as types] + [lipas.schema.core :as schema] [lipas.utils :as utils])) (def keys-vec [:type-code :name :description :geometry-type :sub-category]) @@ -67,7 +69,17 @@ :subCategories (collect-subcategories (cat :type-code) locale)}) (vals types/main-categories))) -(defn get-sports-places [params] - ;; TODO collect params and query ES - params) +(def query-template {:query + {:simple_query_string + {:query "*" + :analyze_wildcard true, + :fields (vec schema/legacy-fields), + :default_operator "and"}}}) + +(defn ->es-query [params] + ;; TODO: use params + query-template) + +(defn get-sports-places [search params] + (core/search search (->es-query params))) diff --git a/webapp/src/cljc/lipas/schema/core.cljc b/webapp/src/cljc/lipas/schema/core.cljc index 782e769ff..3032179ab 100644 --- a/webapp/src/cljc/lipas/schema/core.cljc +++ b/webapp/src/cljc/lipas/schema/core.cljc @@ -80,6 +80,8 @@ (def timestamp-regex #"\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)") +(re-find timestamp-regex "2020-01-01T00:00:00.000Z") + (def date-regex #"\d{4}-[01]\d-[0-3]\d") @@ -2067,10 +2069,34 @@ (s/keys :req-un [:lipas.football.type/type-code]))) ;;; Legacy API ;;; - -(s/def :lipas.legacy.api/closeToLon (int-in -180 180)#_:lipas.location.coordinates/lon) -(s/def :lipas.legacy.api/closeToLat (int-in -180 180)#_:lipas.location.coordinates/lat) +(s/def :lipas.legacy.api/closeToLon (int-in -180 180) #_:lipas.location.coordinates/lon) +(s/def :lipas.legacy.api/closeToLat (int-in -180 180) #_:lipas.location.coordinates/lat) (s/def :lipas.legacy.api/modifiedAfter :lipas/timestamp) +(s/def :lipas.legacy.api/retkikartta boolean?) +(s/def :lipas.legacy.api/closeToMatch #{"start-point" "any-point"}) +(s/def :lipas.legacy.api/page (int-in 0 999999)) +(s/def :lipas.legacy.api/closeToDistanceKm (number-in {:min 0 :max 99999})) +(s/def :lipas.legacy.api/harrastuspassi boolean?) +(s/def :lipas.legacy.api/pageSize (int-in 0 1000)) +(s/def :lipas.legacy.api/searchString (str-in 3 100)) +(s/def :lipas.legacy.api/since :lipas/timestamp) + + +;; TODO: cityCode, fields and typCode doesn't work from swagger. +;; Swagger is sending those as comma separated values. +(s/def :lipas.legacy.api/cityCode + (st/spec + {:spec city-codes + :type :long + :swagger/type "number" + :swagger/enum city-codes})) +(s/def :lipas.legacy.api/cityCodes + (s/coll-of :lipas.legacy.api/cityCode + :distinct true + :min-count 1 + :max-count (count city-codes) + :into [])) + (def legacy-fields #{"properties" "schoolUse" "email" @@ -2098,32 +2124,39 @@ "owner" "location.coordinates.wgs84" "location.address"}) -(s/def :lipas.legacy.api/field* legacy-fields) -(s/def :lipas.legacy.api/field + +(s/def :lipas.legacy.api/field + (st/spec {:spec legacy-fields + :swagger/type "enum" + :swagger/enum legacy-fields})) + + (s/def :lipas.legacy.api/fields + (s/coll-of :lipas.legacy.api/field + :distinct true + :min-count 1 + :max-count (count legacy-fields) + :into [])) + +(s/def :lipas.legacy.api/typeCode* + (into #{} type-codes)) + +(s/def :lipas.legacy.api/typeCode (st/spec - {:spec :lipas.legacy.api/field* - :swagger/type "string" - :swagger/enum legacy-fields})) -(s/def :lipas.legacy.api/fields - (s/coll-of :lipas.legacy.api/field + {:spec :lipas.legacy.api/typeCode* + :type :long + :swagger/type "number" + :swagger/enum type-codes})) + +(s/def :lipas.legacy.api/typeCodes + (s/coll-of :lipas.legacy.api/typeCode :min-count 0 :distinct true :into [])) -(s/def :lipas.legacy.api/retkikartta boolean?) -(s/def :lipas.legacy.api/closeToMatch #{"start-point" "any-point"}) -(s/def :lipas.legacy.api/page (int-in 0 999999)) -(s/def :lipas.legacy.api/closeToDistanceKm (number-in {:min 0 :max 99999})) -(s/def :lipas.legacy.api/harrastuspassi boolean?) -(s/def :lipas.legacy.api/pageSize (int-in 0 1000)) -(s/def :lipas.legacy.api/typeCodes (s/coll-of :lipas.sports-site.type/type-code - :distinct true - :min-count 0 - :into [])) -(s/def :lipas.legacy.api/cityCodes [:lipas.location.city/city-code]) -(s/def :lipas.legacy.api/searchString (str-in 3 100)) -(s/def :lipas.legacy.api/since :lipas/timestamp) + + (s/def :lipas.legacy.api/search-params - (s/keys :opt-un [:lipas.legacy.api/closeToLon + (s/keys :opt-un [:lipas.legacy.api/typeCodes + :lipas.legacy.api/closeToLon :lipas.legacy.api/closeToLat :lipas.api/lang :lipas.legacy.api/modifiedAfter @@ -2134,7 +2167,6 @@ :lipas.legacy.api/closeToDistanceKm :lipas.legacy.api/harrastuspassi :lipas.legacy.api/pageSize - :lipas.legacy.api/typeCodes :lipas.legacy.api/cityCodes :lipas.legacy.api/searchString])) From 8124064602a9afc28e53655247f556fa2b444a94 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Mon, 20 Jan 2025 10:10:15 +0200 Subject: [PATCH 10/11] collection format wip --- webapp/src/clj/lipas/backend/handler.clj | 3 ++- webapp/src/cljc/lipas/schema/core.cljc | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index 80258ea25..ab10e9a94 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -824,7 +824,8 @@ :handler (swagger/create-swagger-handler)}}] ["/sports-places" {:swagger {:id ::legacy} - :parameters {:query :lipas.legacy.api/search-params} + :parameters {:query :lipas.legacy.api/search-params + :collection-format "multi"} :get {:tags ["sport-places"] :handler diff --git a/webapp/src/cljc/lipas/schema/core.cljc b/webapp/src/cljc/lipas/schema/core.cljc index 3032179ab..8f12e43ce 100644 --- a/webapp/src/cljc/lipas/schema/core.cljc +++ b/webapp/src/cljc/lipas/schema/core.cljc @@ -2148,10 +2148,11 @@ :swagger/enum type-codes})) (s/def :lipas.legacy.api/typeCodes - (s/coll-of :lipas.legacy.api/typeCode - :min-count 0 - :distinct true - :into [])) + (st/spec {:spec (s/coll-of :lipas.legacy.api/typeCode + :min-count 0 + :distinct true + :into []) + :collectionFormat "multi"})) (s/def :lipas.legacy.api/search-params From 1b631f5520b90d89ee2e5c93ae7dc5c1f25ebe47 Mon Sep 17 00:00:00 2001 From: Tuomas-Matti Soikkeli Date: Tue, 4 Feb 2025 16:24:55 +0200 Subject: [PATCH 11/11] Move to malli, part1 --- webapp/src/clj/lipas/backend/api/v1.clj | 136 +++++++++++++ webapp/src/clj/lipas/backend/api/v2.clj | 12 +- webapp/src/clj/lipas/backend/handler.clj | 181 +----------------- webapp/src/clj/lipas/backend/legacy/api.clj | 23 ++- webapp/src/cljc/lipas/schema/core.cljc | 58 +----- webapp/src/cljc/lipas/schema/core_legacy.cljc | 80 ++++++-- 6 files changed, 228 insertions(+), 262 deletions(-) create mode 100644 webapp/src/clj/lipas/backend/api/v1.clj diff --git a/webapp/src/clj/lipas/backend/api/v1.clj b/webapp/src/clj/lipas/backend/api/v1.clj new file mode 100644 index 000000000..a5215b874 --- /dev/null +++ b/webapp/src/clj/lipas/backend/api/v1.clj @@ -0,0 +1,136 @@ +(ns lipas.backend.api.v1 + (:require [lipas.backend.legacy.api :as legacy-api] + [lipas.schema.sports-sites.types :as types-schema] + [lipas.schema.core-legacy :as legacy-schema] + [malli.core :as m] + [reitit.coercion.malli :as malli] + [reitit.openapi :as openapi] + [reitit.swagger-ui :as swagger-ui])) + +(defn routes [{:keys [search]}] + (let [ui-handler (swagger-ui/create-swagger-ui-handler + {:url "/v1/openapi.json"})] + ["/v1" + {:openapi + {:id :api-v1 + + :info {:title "LIPAS API V1" + :summary "API for Finnish sports and recreational facility database LIPAS" + :description "The LIPAS system provides comprehensive data about sports and recreational facilities in Finland. The API is organized into two main sections: + +**Sports Places** +The core entities of LIPAS. Each sports facility is classified using a hierarchical type system, where specific facility types belong to subcategories within seven main categories. Each facility type has its own specific set of properties and a defined geometry type (Point, LineString, or Polygon) that describes its spatial representation. + +**Sports Site Categories** +Access to the hierarchical type classification system used for categorizing sports facilities." + + :contact + {:name "Support, feature requests and bug reports" + :url "https://github.com/lipas-liikuntapaikat/lipas/issues" + :email "lipasinfo@jyu.fi"} + + :license + {:name "Creative Commons Attribution 4.0 International" + :identifier "CC-BY-SA-4.0" + :url "https://creativecommons.org/licenses/by-sa/4.0/"}}} + ;; The regular handle is still using swagger-spec, so :openapi :id doesn't hide + ;; these routes from that. + :swagger {:id :hide-from-default} + :coercion malli/coercion} + ["/sports-places" + {:parameters {:query legacy-schema/search-params} + :get + {:tags ["sport-places"] + :handler + (fn [{:keys [parameters]}] + (let [search-params (-> parameters :query)] + {:status 400 + :body (m/explain legacy-schema/search-params search-params)})) + :responses {200 {:body :any}}}}] + ["/categories" + {:tags ["sport-place-types"] + :parameters {:query [:map [:lang {:optional true} #'legacy-schema/lang]]} + :get + {:handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi)] + {:status 200 + :body (legacy-api/categories locale)})) + :responses {200 {:body :any}}}}] + ["/sports-place-types" { + :parameters {:query [:map [:lang {:optional true} #'legacy-schema/lang]]} + :get + {:tags ["sport-place-types"] + :handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi)] + {:status 200 + :body (legacy-api/sports-place-types locale)})) + :responses {200 {:body :any}}}}] + ["/sports-place-types/:type-code" + {:swagger {:id ::legacy} + :parameters {:query [:map [:lang {:optional true} #'legacy-schema/lang]] + :path [:map [:type-code #'types-schema/active-type-code]]} + :get + {:tags ["sport-place-types"] + :handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi) + type-code (-> req :parameters :path :type-code)] + {:status 200 + :body (legacy-api/sports-place-by-type-code locale type-code)})) + :responses {200 {:body :any}}}}] + (comment + ;; mallissa provider, sillä voisi tehdä mallin + ["/sports-places/:sports-place-id" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.api/lang]) + :path {:sports-place-id int?}} + :get + {:tags ["sport-places"] + :handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi) + sports-place-id (-> req :parameters :path :sports-place-id)] + {:status 200 + :body (str "hello world" sports-place-id)}))}}] + ["/deleted-sports-places" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.legacy.api/since])} + :get + {:tags ["sport-places"] + :handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi) + since (or (-> req :parameters :query :since) "1984-01-01T00:00:00")] + {:status 200 + :body (str "hello world" since locale)}))}}] + + ["/sports-place-types" + {:swagger {:id ::legacy} + :parameters {:query (s/keys :opt-un [:lipas.api/lang])} + :get + {:tags ["sport-place-types"] + :handler + (fn [req] + (let [locale (or (-> req :parameters :query :lang keyword) :fi)] + {:status 200 + :body (legacy-api/sports-place-types locale)}))}}] + + + ) + ["/openapi.json" + {:get + {:no-doc true + :swagger {:info {:title "Lipas-API (legacy) v1"}} + :tags [{:name "sport-places" + :description "Sport places"} + {:name "sport-place-types" + :description "Sport place types"}] + :handler (openapi/create-openapi-handler)}}] + ["/swagger-ui" + {:get {:no-doc true + :handler ui-handler}}] + ["/swagger-ui/*" + {:get {:no-doc true + :handler ui-handler}}]])) \ No newline at end of file diff --git a/webapp/src/clj/lipas/backend/api/v2.clj b/webapp/src/clj/lipas/backend/api/v2.clj index 65783a2e5..9c7e288ae 100644 --- a/webapp/src/clj/lipas/backend/api/v2.clj +++ b/webapp/src/clj/lipas/backend/api/v2.clj @@ -129,13 +129,11 @@ Additional non-facility entities in LIPAS, that complement the sports facility d ;; These get merged in a wild way #_#_:tags [{:name "Sports Sites" - :description "The core entities of LIPAS."} - {:name "Sports Site Categories" - :description "Hierarchical categorization of sports facilities"} - {:name "Locations of Interest" - :description "Additional non-facility entities in LIPAS"}] - - } + :description "The core entities of LIPAS."} + {:name "Sports Site Categories" + :description "Hierarchical categorization of sports facilities"} + {:name "Locations of Interest" + :description "Additional non-facility entities in LIPAS"}]} ;; The regular handle is still using swagger-spec, so :openapi :id doesn't hide ;; these routes from that. :swagger {:id :hide-from-default} diff --git a/webapp/src/clj/lipas/backend/handler.clj b/webapp/src/clj/lipas/backend/handler.clj index ab10e9a94..7533ede53 100644 --- a/webapp/src/clj/lipas/backend/handler.clj +++ b/webapp/src/clj/lipas/backend/handler.clj @@ -2,6 +2,7 @@ (:require [clojure.java.io :as io] [clojure.spec.alpha :as s] [lipas.backend.api.v2 :as v2] + [lipas.backend.api.v1 :as v1] [lipas.backend.core :as core] [lipas.backend.jwt :as jwt] [lipas.backend.middleware :as mw] @@ -21,27 +22,6 @@ [ring.middleware.params :as params] [ring.util.io :as ring-io] [taoensso.timbre :as log])) - (:require [clojure.java.io :as io] - [clojure.spec.alpha :as s] - [lipas.backend.core :as core] - [lipas.backend.jwt :as jwt] - [lipas.backend.legacy.api :as legacy-api] - [lipas.backend.middleware :as mw] - [lipas.roles :as roles] - [lipas.schema.core] - [lipas.utils :as utils] - [muuntaja.core :as m] - [reitit.coercion.spec] - [reitit.ring :as ring] - [reitit.ring.coercion :as coercion] - [reitit.ring.middleware.exception :as exception] - [reitit.ring.middleware.multipart :as multipart] - [reitit.ring.middleware.muuntaja :as muuntaja] - [reitit.swagger :as swagger] - [reitit.swagger-ui :as swagger-ui] - [ring.middleware.params :as params] - [ring.util.io :as ring-io] - [taoensso.timbre :as log])) (defn exception-handler ([status type] @@ -62,6 +42,7 @@ :user-not-found (exception-handler 404 :user-not-found) :email-not-found (exception-handler 404 :email-not-found) :reminder-not-found (exception-handler 404 :reminder-not-found) + :qbits.spandex/response-exception (exception-handler 500 :internal-server-error :print-stack) ;; Return 500 and print stack trace for exceptions that are not @@ -90,6 +71,7 @@ [{:keys [db emailer search mailchimp aws ptv] :as ctx}] (ring/ring-handler (ring/router + [["/favicon.ico" {:get {:no-doc true @@ -738,162 +720,8 @@ :body (core/search-lois-with-params search body-params)})}}] (ptv-handler/routes ctx)] + (v1/routes ctx) (v2/routes ctx)] - ;; PTV - ["/actions/get-ptv-integration-candidates" - {:post - {:no-doc false - :require-role :ptv/manage - :parameters {:body map?} - :handler - (fn [{:keys [body-params]}] - {:status 200 - :body (core/get-ptv-integration-candidates search body-params)})}}] - - ["/actions/generate-ptv-descriptions" - {:post - {:no-doc false - :require-role :ptv/manage - :parameters {:body map?} - :handler - (fn [{:keys [body-params]}] - {:status 200 - :body (core/generate-ptv-descriptions search body-params)})}}] - - ["/actions/generate-ptv-service-descriptions" - {:post - {:no-doc false - :require-role :ptv/manage - :parameters {:body map?} - :handler - (fn [{:keys [body-params]}] - {:status 200 - :body (core/generate-ptv-service-descriptions search body-params)})}}] - - ["/actions/save-ptv-service" - {:post - {:no-doc false - :require-role :ptv/manage - :parameters {:body map?} - :handler - (fn [{:keys [body-params]}] - {:status 200 - :body (core/upsert-ptv-service! body-params)})}}] - - ["/actions/fetch-ptv-services" - {:post - {:no-doc false - :require-role :ptv/manage - :parameters {:body map?} - :handler - (fn [{:keys [body-params]}] - {:status 200 - :body (core/fetch-ptv-services body-params)})}}] - - ["/actions/save-ptv-service-location" - {:post - {:no-doc false - :require-role :ptv/manage - :parameters {:body map?} - :handler - (fn [{:keys [body-params identity]}] - {:status 200 - :body (core/upsert-ptv-service-location! db search identity body-params)})}}] - - ["/actions/save-ptv-meta" - {:post - {:no-doc false - :require-role :ptv/manage - :parameters {:body map?} - :handler - (fn [{:keys [body-params identity]}] - {:status 200 - :body (core/save-ptv-integration-definitions db search identity body-params)})}}]] - - ;; legacy routes - ["/v1/api" - ["/swagger.json" - {:get - {:no-doc true - :swagger {:id ::legacy - :info {:title "Lipas-API (legacy) v1"}} - :tags [{:name "sport-places" - :description "Sport places"} - {:name "sport-place-types" - :description "Sport place types"}] - :handler (swagger/create-swagger-handler)}}] - ["/sports-places" - {:swagger {:id ::legacy} - :parameters {:query :lipas.legacy.api/search-params - :collection-format "multi"} - :get - {:tags ["sport-places"] - :handler - (fn [{:keys [parameters]}] - (let [spec :lipas.legacy.api/search-params - search-params (-> parameters :query) - valid? (s/valid? spec search-params)] - (if valid? - {:status 200 - :body (legacy-api/get-sports-places search search-params)} - {:status 400 - :body (s/explain-data spec search-params)})))}}] - ["/sports-places/:sports-place-id" - {:swagger {:id ::legacy} - :parameters {:query (s/keys :opt-un [:lipas.api/lang]) - :path {:sports-place-id int?}} - :get - {:tags ["sport-places"] - :handler - (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :fi) - sports-place-id (-> req :parameters :path :sports-place-id)] - {:status 200 - :body (str "hello world" sports-place-id)}))}}] - ["/deleted-sports-places" - {:swagger {:id ::legacy} - :parameters {:query (s/keys :opt-un [:lipas.legacy.api/since])} - :get - {:tags ["sport-places"] - :handler - (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :fi) - since (or (-> req :parameters :query :since) "1984-01-01T00:00:00")] - {:status 200 - :body (str "hello world" since locale)}))}}] - - ["/sports-place-types" - {:swagger {:id ::legacy} - :parameters {:query (s/keys :opt-un [:lipas.api/lang])} - :get - {:tags ["sport-place-types"] - :handler - (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :fi)] - {:status 200 - :body (legacy-api/sports-place-types locale)}))}}] - ["/sports-place-types/:type-code" - {:swagger {:id ::legacy} - :parameters {:query (s/keys :opt-un [:lipas.api/lang]) - :path {:type-code int?}} - :get - {:tags ["sport-place-types"] - :handler - (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :fi) - type-code (-> req :parameters :path :type-code)] - {:status 200 - :body (legacy-api/sports-place-by-type-code locale type-code)}))}}] - ["/categories" - {:tags ["sport-place-types"] - :swagger {:id ::legacy} - :parameters {:query (s/keys :opt-un [:lipas.api/lang])} - :get - {:handler - (fn [req] - (let [locale (or (-> req :parameters :query :lang keyword) :fi)] - {:status 200 - :body (legacy-api/categories locale)}))}}]]] {:data {:coercion reitit.coercion.spec/coercion @@ -919,5 +747,4 @@ mw/privilege-middleware]}}) (ring/routes (swagger-ui/create-swagger-ui-handler {:path "/api/swagger-ui" :url "/api/swagger.json"}) - (swagger-ui/create-swagger-ui-handler {:path "/v1/api/swagger-ui" :url "/v1/api/swagger.json"}) (ring/create-default-handler)))) diff --git a/webapp/src/clj/lipas/backend/legacy/api.clj b/webapp/src/clj/lipas/backend/legacy/api.clj index f8013ed91..37f4f12d4 100644 --- a/webapp/src/clj/lipas/backend/legacy/api.clj +++ b/webapp/src/clj/lipas/backend/legacy/api.clj @@ -1,10 +1,9 @@ (ns lipas.backend.legacy.api - (:require - [lipas.backend.core :as core] - [lipas.data.prop-types :as prop-types] - [lipas.data.types :as types] - [lipas.schema.core :as schema] - [lipas.utils :as utils])) + (:require [lipas.backend.core :as core] + [lipas.data.prop-types :as prop-types] + [lipas.data.types :as types] + [lipas.schema.core-legacy :refer [legacy-fields]] + [lipas.utils :as utils])) (def keys-vec [:type-code :name :description :geometry-type :sub-category]) @@ -49,16 +48,16 @@ (-> (types/all type-code) (->legacy-api-with-properties locale))) -(defn collect-sport-place-types [sub-category-type-code] +(defn collect-sport-place-types [sub-category-type-code] (->> (vals types/all) (filter #(= (% :sub-category) sub-category-type-code)) (map :type-code))) -(defn- collect-subcategories [type-code locale] +(defn- collect-subcategories [type-code locale] (->> (vals types/sub-categories) (filter #(= (% :main-category) (str type-code))) - (map (fn [x] + (map (fn [x] {:typeCode (x :type-code) :name (-> x :name locale) :sportsPlaceTypes (collect-sport-place-types (x :type-code))})))) @@ -66,7 +65,7 @@ (defn categories [locale] (mapv (fn [cat] {:name (-> cat :name locale) :typeCode (cat :type-code) - :subCategories (collect-subcategories (cat :type-code) locale)}) + :subCategories (collect-subcategories (cat :type-code) locale)}) (vals types/main-categories))) @@ -74,12 +73,12 @@ {:simple_query_string {:query "*" :analyze_wildcard true, - :fields (vec schema/legacy-fields), + :fields legacy-fields, :default_operator "and"}}}) (defn ->es-query [params] ;; TODO: use params query-template) -(defn get-sports-places [search params] +(defn get-sports-places [search params] (core/search search (->es-query params))) diff --git a/webapp/src/cljc/lipas/schema/core.cljc b/webapp/src/cljc/lipas/schema/core.cljc index 8f12e43ce..86990daf3 100644 --- a/webapp/src/cljc/lipas/schema/core.cljc +++ b/webapp/src/cljc/lipas/schema/core.cljc @@ -2078,7 +2078,7 @@ (s/def :lipas.legacy.api/closeToDistanceKm (number-in {:min 0 :max 99999})) (s/def :lipas.legacy.api/harrastuspassi boolean?) (s/def :lipas.legacy.api/pageSize (int-in 0 1000)) -(s/def :lipas.legacy.api/searchString (str-in 3 100)) +(s/def :lipas.legacy.api/searchString (str-in 3 100)) (s/def :lipas.legacy.api/since :lipas/timestamp) @@ -2097,45 +2097,7 @@ :max-count (count city-codes) :into [])) -(def legacy-fields #{"properties" - "schoolUse" - "email" - "type.name" - "reservationsLink" - "location.sportsPlaces" - "renovationYears" - "admin" - "location.coordinates.tm35fin" - "www" - "location.geometries" - "name" - "type.typeCode" - "location.locationId" - "constructionYear" - "freeUse" - "location.city.name" - "lastModified" - "marketingName" - "location.postalCode" - "location.postalOffice" - "location.city.cityCode" - "phoneNumber" - "location.neighborhood" - "owner" - "location.coordinates.wgs84" - "location.address"}) - -(s/def :lipas.legacy.api/field - (st/spec {:spec legacy-fields - :swagger/type "enum" - :swagger/enum legacy-fields})) - - (s/def :lipas.legacy.api/fields - (s/coll-of :lipas.legacy.api/field - :distinct true - :min-count 1 - :max-count (count legacy-fields) - :into [])) + (s/def :lipas.legacy.api/typeCode* (into #{} type-codes)) @@ -2155,21 +2117,7 @@ :collectionFormat "multi"})) -(s/def :lipas.legacy.api/search-params - (s/keys :opt-un [:lipas.legacy.api/typeCodes - :lipas.legacy.api/closeToLon - :lipas.legacy.api/closeToLat - :lipas.api/lang - :lipas.legacy.api/modifiedAfter - :lipas.legacy.api/fields - :lipas.legacy.api/retkikartta - :lipas.legacy.api/closeToMatch - :lipas.legacy.api/page - :lipas.legacy.api/closeToDistanceKm - :lipas.legacy.api/harrastuspassi - :lipas.legacy.api/pageSize - :lipas.legacy.api/cityCodes - :lipas.legacy.api/searchString])) + ;;; HTTP-API ;;; diff --git a/webapp/src/cljc/lipas/schema/core_legacy.cljc b/webapp/src/cljc/lipas/schema/core_legacy.cljc index bbc0646de..740031770 100644 --- a/webapp/src/cljc/lipas/schema/core_legacy.cljc +++ b/webapp/src/cljc/lipas/schema/core_legacy.cljc @@ -1,16 +1,74 @@ (ns lipas.schema.core-legacy (:require [clojure.spec.alpha :as s] - [lipas.schema.core])) + [lipas.schema.common :as common-schema] + [lipas.schema.core] + [lipas.schema.sports-sites.location :as location-schema] + [lipas.schema.sports-sites.types :as types-schema] + [malli.core :as m] + [malli.util :as mu])) -(s/def :lipas-legacy.api.parameter/lang :lipas.api/lang) -(s/def :lipas-legacy.api.response/sports-place-types - (s/keys :req-un [:lipas-legacy.sports-site/typeCode - :lipas.sports-site/name - :lipas.sports-site/description - :lipas-legacy.sports-site/geometryType - :lipas-legacy.sports-site/subCategory])) +(def lang [:enum "fi" "en" "se"]) -(s/def :lipas-legacy.sports-site/geometryType #{"Point" "LineString"}) -(s/def :lipas-legacy.sports-site/typeCode (s/int-in 1 5000)) -(s/def :lipas-legacy.sports-site/subCategory (s/int-in 1 5000)) +(def legacy-fields ["properties" + "schoolUse" + "email" + "type.name" + "reservationsLink" + "location.sportsPlaces" + "renovationYears" + "admin" + "location.coordinates.tm35fin" + "www" + "location.geometries" + "name" + "type.typeCode" + "location.locationId" + "constructionYear" + "freeUse" + "location.city.name" + "lastModified" + "marketingName" + "location.postalCode" + "location.postalOffice" + "location.city.cityCode" + "phoneNumber" + "location.neighborhood" + "owner" + "location.coordinates.wgs84" + "location.address"]) + +(def search-params + (let [schema [:map + [:typeCodes #'types-schema/active-type-codes] + [:closeToLon [:int {:min -180 :max 180}]] + [:closeToLat [:int {:min -90 :max 90}]] + [:lang #'lang] + [:modifiedAfter [:re #"\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]) (?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d\.\d{3}"]] + [:fields [:set (into [:enum] legacy-fields)]] + [:retkikartta :boolean] + [:closeToMatch [:enum "start-point" "any-point"]] + [:page [:int {:min 0}]] + [:closeToDistanceKm [common-schema/number]] + [:harrastuspassi :boolean] + [:pageSize [:int {:min 1 :max 100}]] + [:cityCodes #'location-schema/city-codes] + [:searchString [:string {:min 1 :max 2056}]]]] + (mu/optional-keys schema (mu/keys schema)))) + +(comment + (m/validate [:re #"\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]) (?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d\.\d{3}"] + "2025-02-04 14:48:00.000") + (s/def :lipas-legacy.api.parameter/lang :lipas.api/lang) + + (s/def :lipas-legacy.api.response/sports-place-types + (s/keys :req-un [:lipas-legacy.sports-site/typeCode + :lipas.sports-site/name + :lipas.sports-site/description + :lipas-legacy.sports-site/geometryType + :lipas-legacy.sports-site/subCategory])) + + (s/def :lipas-legacy.sports-site/geometryType #{"Point" "LineString"}) + (s/def :lipas-legacy.sports-site/typeCode (s/int-in 1 5000)) + (s/def :lipas-legacy.sports-site/subCategory (s/int-in 1 5000)) + )