Error -> should contain keys in clojure.spec

125 Views Asked by At

I'm new to Clojure and I have a problem that I really can't find a solution to.

I have a structure already defined with Clojure Spec, its keys and data types match with a JSON file.

Doing an inspection I get the error I appreciate your help


Spec File

(ns invoice-spec
  (:require
    [clojure.spec.alpha :as s] ))

(s/def :customer/name string?)
(s/def :customer/email string?)
(s/def :invoice/customer (s/keys :req-un [:customer/name
                                        :customer/email]))

;(s/def :tax/tax_category #{:IVA})
(s/def :tax/tax_category string?)
(s/def :tax/tax_rate int?)
(s/def ::tax (s/keys :req-un [:tax/tax_category
                           :tax/tax_rate]))
(s/def :invoice-item/taxes (s/coll-of ::tax :kind vector? :min-count 1))

(s/def :invoice-item/price double?)
(s/def :invoice-item/quantity double?)
(s/def :invoice-item/sku string?)

(s/def ::invoice-item
  (s/keys :req-un [:invoice-item/price
                :invoice-item/quantity
                :invoice-item/sku
                :invoice-item/taxes]))

;(s/def :invoice/issue_date inst?)
(s/def :invoice/issue_date string?)
(s/def :invoice/items (s/coll-of ::invoice-item :kind vector? :min-count 1))

(s/def ::invoice
  (s/keys :req-un [:invoice/issue_date
                :invoice/customer
                :invoice/items]))

Clj File

(defn invoice
  [name_json]
  (json/read (clojure.java.io/reader name_json) :key-fn keyword))

Validation

(s/valid? ::invoice/invoice (invoice "invoice.json"))
=> false

View Errors

(expound/expound ::invoice/invoice (invoice "invoice.json"))
-- Spec failed --------------------

  {:invoice
   {:payment_means_type "DEBITO",
    :order_reference "PEDID_0001",
    :number "1",
    :issue_date "13/10/2020",
    :payment_date "12/11/2020",
    :customer
    {:name "ANDRADE RODRIGUEZ MANUEL ALEJANDRO",
     :email "[email protected]"},
    :payment_means "DEBIT_CARD",
    :items
    [{:price 10000.0,
      :quantity 1.0,
      :sku "SUPER-1",
      :taxes [{:tax_category "IVA", :tax_rate 5}]}
     {:price 20000.0,
      :quantity 1.0,
      :sku "SUPER-2",
      :taxes [{:tax_category "IVA", :tax_rate 19}]}
     {:price 30000.0,
      :quantity 1.0,
      :sku "SUPER-3",
      :taxes [{:tax_category "IVA", :tax_rate 19}]}],
    :retentions
    [{:tax_category "RET_FUENTE", :tax_rate 15.0}
     {:tax_category "RET_IVA", :tax_rate 15.0}]}}

should contain keys: :customer, :issue_date, :items

| key         | spec                                         |
|=============+==============================================|
| :customer   | (keys :req [:customer/name :customer/email]) |
|-------------+----------------------------------------------|
| :issue_date | string?                                      |
|-------------+----------------------------------------------|
| :items      | (coll-of                                     |
|             |  :invoice-spec/invoice-item                  |
|             |  :min-count                                  |
|             |  1                                           |
|             |  :kind                                       |
|             |  vector?)                                    |

-- Relevant specs -------

:invoice-spec/invoice:
  (clojure.spec.alpha/keys
   :req-un
   [:invoice/issue_date :invoice/customer :invoice/items])

-------------------------
Detected 1 error
=> nil

JSON File

{
  "invoice": {
    "issue_date": "13/10/2020",
    "order_reference": "PEDID_0001",
    "payment_date": "12/11/2020",
    "payment_means": "DEBIT_CARD",
    "payment_means_type": "DEBITO",
    "number": "1",
    "customer": {
      "name": "ANDRADE RODRIGUEZ MANUEL ALEJANDRO",
      "email": "[email protected]"
    },
    "items": [
      {
        "price": 10000.00,
        "quantity": 1.00,
        "sku": "SUPER-1",
        "taxes": [
          {
            "tax_category": "IVA",
            "tax_rate": 5
          }
        ]
      },
      {
        "price": 20000.00,
        "quantity": 1.00,
        "sku": "SUPER-2",
        "taxes": [
          {
            "tax_category": "IVA",
            "tax_rate": 19
          }
        ]
      },
      {
        "price": 30000.00,
        "quantity": 1.00,
        "sku": "SUPER-3",
        "taxes": [
          {
            "tax_category": "IVA",
            "tax_rate": 19
          }
        ]
      }
    ],
    "retentions": [
      {
        "tax_category": "RET_FUENTE",
        "tax_rate": 15.00
      },
      {
        "tax_category": "RET_IVA",
        "tax_rate": 15.0
      }
    ]
  }
}

My goal is to obtain the following result

(s/valid? ::invoice/invoice (invoice "invoice.json"))
=> true
1

There are 1 best solutions below

0
On

You have main keyword :invoice, so you need one more s/def, I called ::data

(s/def ::data (s/keys :req-un [::invoice]))

(s/valid? ::invoice/data (invoice "invoice.json"))
=> true

Without that def, you must get value of :invoice, which is the map with keys issue_date, customer and items:

(s/valid? ::invoice/invoice (:invoice (invoice "invoice.json")))
=> true