Fork me on GitHub
#malli
<
2021-07-14
>
wcalderipe16:07:37

Hey folks, I'm trying to use Malli to parse keys and values of a map to something else but I'm having a bit of difficulty understanding how and what function to use. I've tried m/decode and m/parse so far but not with much success. How can I achieve a transformation like the one below using Malli?

;; input
  {:foobar "baz" 
   :qux    "999"}

;; output
  {:foo :baz ;; Changes the key from :foobar to :foo
   :qux 999  ;; Coerces the value from string to integer 
   }
For context, the use case I have in mind is parsing the response of an API to something else. I'm sharing it because maybe there's a better way to use Malli for schema checking of services we don't control plus parsing the data to something else.

ikitommi17:07:52

@wcalderipe maybe:

(def decode
  (m/decoder
    [:map
     [:foo :keyword]
     [:qux :int]]
    (mt/transformer
      (mt/key-transformer {:decode #(get {:foobar :foo} % %)})
      (mt/string-transformer))))

(decode
  {:foobar "baz"
   :qux "999"})
; => {:foo :baz, :qux 999}

wcalderipe17:07:51

Got it.. thanks for taking the time to reply 🙌 I have another question between these lines. Can I reuse the same schema for validating the API response and do the whole transformation? Let's say we're integrating a Todo API and we need to do some transformation on their response.

;; Todo API schema as it is
(def todo-api
  [:map
   [:name [:string {:min 1}]]
   [:done [:boolean]]
   [:create_at [:string]]])

(malli/validate todo-api {:name      "Ask for help at #malli channel"
                          :done      true
                          :create_at "1615007456"})
;; => true
1. Validate if responses are following the schema 2. Parse the response to something else as you've shown
;; Output
{:name      "Ask for help at #malli channel"
 :done      true
 :timestamp 1615007456} ;; Rename the key and transform the value
Do I need two schemas 1 for the input and 1 for the output in a scenario like this? :thinking_face:

greg10:07:18

My understanding is that Malli encourage to transform first and validate next. For example, let's say data you are receiving are invalid. You decode, validate it. It turned out something is wrong you might want to return an original piece of data by transforming it back (using m/encode) and adding some info what's wrong. If the response has a different schema by its identity, make sense to have a different schema. Disclaimer, I'm not an expert in Malli 🙂

👍 2
greg10:07:12

So should you have one schema or two, is question are these data same thing or two different things. And answer is not always clear. E.g. In layered architectures, like MVP, depends on implementation you might have several schemas (representation) of the same data.

👍 2
ikitommi12:07:01

if the web-facing values can be mapped automatically to internal values, you should be able to do the same transformation for the schemas too, e.g. define just one and generate the other. Reason for wanting to have two schemas could be apidocs: they should be generated from “web-facing” schemas, not the internal model.

ikitommi12:07:12

not sure if there are all the needed helpers in malli.util , but easy to add if something is missing.

wcalderipe06:07:42

Thanks for taking the time @UBVL1LR5F > E.g. In layered architectures, like MVP, depends on implementation you might have several schemas (representation) of the same data. I think the problem I have in mind, the data means the same thing but it has a different shape inside the boundaries of my app. Malli will be used near to the edges of the app into something like an anti-corruption layer to translate an external concept to an internal one. That's renaming some keys and coerce some of the values too.

greg13:07:48

If the Malli resides only on the one side of the ACL architecture, on the internal one, I think you could work with one schema. If both would use Malli, then I would go with two. Going back to you question > Can I reuse the same schema for validating the API response and do the whole transformation? I think yes, you could use one. You received response from external (behind ACL) system, you transform (including the keys) and then validate.

greg13:07:10

If you want validate first for some reason, you might need two.

wcalderipe08:07:05

Thanks for sharing @U055NJ5CC My doubt is more a question of design than which functions to use. @UBVL1LR5F got it... > If you want validate first for some reason, you might need two. Yeah, I think I'll end up with two instead of one because I was thinking of doing validations as well in development mode. Thanks a bunch for the help!