compojure-api spec приведение на тело ответа

Я пытаюсь выяснить, как сделать пользовательское приведение с помощью compojure-api и spec. Читая документы и код, я смог принуждение к входу (тело), ​​но не могу принуждение к телу ответа.

В частности, у меня есть пользовательский тип, метка времени, которая представлена ​​как длинная в моем приложении, но для веб-API я хочу потреблять и возвращать метки времени ISO (нет, я не хочу использовать Joda для внутреннего использования).

Вот что у меня работает для приведения входных данных, но я не смог должным образом вызвать ответ.

(ns foo
  (:require [clj-time.core :as t]
            [clj-time.coerce :as t.c]
            [spec-tools.conform :as conform]
            [spec-tools.core :as st]))


(def timestamp (st/create-spec
            {:spec pos-int?
             :form `pos-int?
             :json-schema/default "2017-10-12T05:04:57.585Z"
             :type :timestamp}))

(defn json-timestamp->long [_ val]
  (t.c/to-long val))

(def custom-json-conforming
  (st/type-conforming
   (merge
    conform/json-type-conforming
    {:timestamp json-timestamp->long}
    conform/strip-extra-keys-type-conforming)))

(def custom-coercion
  (->  compojure.api.coercion.spec/default-options
       (assoc-in [:body :formats "application/json"] custom-json-
conforming)
       compojure.api.coercion.spec/create-coercion))

;; how do I use this for the response coercion?
(defn timestamp->json-string [_ val]
  (t.c/to-string val))
;; I've tried the following but it doesn't work:
#_(def custom-coercion
  (->  compojure.api.coercion.spec/default-options
       (assoc-in [:body :formats "application/json"] custom-json-
conforming)
       (assoc-in [:response :formats "application/json"]
                 (st/type-conforming
                  {:timestamp timestamp->json-string}))
       compojure.api.coercion.spec/create-coercion))

1 ответ

Решение

Проблема в том, что Spec Conforming - это односторонний конвейер преобразования:

s/conform (и из-за этого st/conform) преобразовывает и проверяет результат. Приведение ответа сначала преобразует целое число в строку даты и проверяет ее по исходному предикату спецификации, который pos-int? и это терпит неудачу на этом.

Для поддержки двусторонних преобразований необходимо определить конечный результат в качестве одного из возможных форматов: например, изменить предикат на что-то вроде #(or (pos-int? %) (string? %)) и это должно работать.

Или вы можете иметь две разные записи Spec, одна для ввода (timestamp-long с pos-int? предикат) и другой для выходных данных (timestamp-string с string? сказуемое). Но нужно помнить, чтобы использовать правильные для запроса и ответов.

CLJ-2251 мог бы помочь, если бы была и дополнительная :transform режим (еще не написан в выпуске), который будет соответствовать без проверки конечных результатов.

Обычно обратные преобразования выполняются кодировщиком формата, но они обычно отправляются для типов значений. Например, Cheshire просто видит Long и не имеет ни малейшего представления, что его следует записать в виде строки даты.

Может быть, люди из #clojure-spec могут помочь. Также хотелось бы узнать, как построить этот вид двустороннего преобразования с помощью spec.

Другие вопросы по тегам