Using clojure.spec for api migrations?

196 views
Skip to first unread message

Jeroen van Dijk

unread,
Sep 6, 2016, 2:00:31 PM9/6/16
to clo...@googlegroups.com
Hi, 

I'm playing with the idea of using clojure.spec to make an API backwards compatible by using conform and unform to transform data from the old version format to a new format version. Below is an example of this in code:

Say we have API change that involves changing the name of a nested key:

(do
  ;; Old api
  (s/def :api.v0.input.nested/label keyword?)
  (s/def :api.v0.input/nested (s/keys :req-un [:api.v0.input.nested/label]))
  (s/def :api.v0/input (s/keys :req-un [:api.v0.input/nested]))

  ;; New api
  (s/def :api.v1.input.nested/key keyword?)
  (s/def :api.v1.input/nested (s/keys :req-un [:api.v1.input.nested/key]))
  (s/def :api.v1/input (s/keys :req-un [:api.v1.input/nested]))
  
  ;; Migration specs
  (s/def :api.v0-v1.migration.input/nested (s/conformer (fn [_] (throw (ex-info "Don't use as conformer" {})))
                                                        (fn [x] (clojure.set/rename-keys x {:label :key}))))

  (s/def :api.v0-v1.migration/input (s/conformer (fn [x]
                                                   (s/conform :api.v1/input x))
                                                 (fn [x]
                                                   (s/unform (s/keys :req-un [:api.v0-v1.migration.input/nested]) x))))
  
  ;; Changes {:nested {:label :hi}} to {:nested {:key :hi}}
  (s/unform :api.v0-v1.migration/input {:nested {:label :hi}}))

So this kind of works, but I wonder whether there is an easier way or if I'm thinking about this the wrong way. I was hoping there would be something like with-unformer (like with-gen) that allows me to change the unforming action for a specific key without having to copy most of the old spec.

Any suggestions or advise is appreciated.

Thanks,
Jeroen


Jeroen van Dijk

unread,
Sep 7, 2016, 3:43:25 AM9/7/16
to clo...@googlegroups.com
Oh I meant `clojure.spec/gen` instead of `clojure.spec/with-gen`. `clojure.spec/gen` has an overrides option. I (think I) need something like this to cleanly change the unforming action in a nested spec.

Jeroen
Reply all
Reply to author
Forward
0 new messages