Skip to content

06 generating records

Daniel Higginbotham edited this page Oct 28, 2022 · 2 revisions

We now know how to add ents to a potato db, but that’s not enough for test fixtures. We need to generate data that can get inserted into a database.

Configuring your potato-db for data generation

To do that, we need to introduce schemas for generating data, and we need to update our `potato-schema` and `potato-db` to use them:

(ns donut.datapotato-tutorial.06
  (:require [donut.datapotato.core :as dc]
            [malli.generator :as mg]))

(def User
  [:map
   [:id pos-int?]
   [:username string?]])

(def Topic
  [:map
   [:id pos-int?]
   [:owner-id pos-int?]
   [:name string?]])

(def potato-schema
  {:user  {:prefix   :u
           :generate {:schema User}}
   :topic {:prefix    :t
           :generate  {:schema Topic}
           :relations {:owner-id [:user :id]}}})

(def potato-db
  {:schema   potato-schema
   :generate {:generator mg/generate}})

Here, we’re using malli to define schemas and generate data. You could also use clojure.spec or plumatic schema. We’ve updated the potato-schema to include a :generate key. This is where you place entity-type configuration for data generation. The :schema key tells datapotato which malli schema to use, but you could also add a :generator key if you want to configure a generator function separately from the “global” one.

The :generate key in potato-db is where you define the global generator function. The generator function should take one argument. When the generator function gets called, it gets passed the [:generate :schema] value for an entity type.

Generating data with the datapotato.core/generate function

You generate data by calling the function datapotato.core/generate, passing it a potato-db and a query:

(defn ex-01
  []
  (dc/generate potato-db {:user [{:count 1}]}))

This will generate a single user:

(ex-01)
{:u0 {:id 139, :username "Yp8BGKp6rM57xl8reh3h9xc534b4"}}

Internally, datapotato adds the ent :u01 to the potato db. Then it “visits” the ent, using the data associated with it to generate a map. (More on visiting in the next section). When the ent gets visited, datapotato can look up what ent-type it is (:user) and from there look up the [:generate :schema] for the ent. It passes that to the :generator function, mg/generate, and voila - a map is born.

Setting data

Sometimes you want to specify values for generated data. You can do that like so:

(defn ex-02
  []
  (dc/generate potato-db {:user [{:count 1
                                  :set   {:username "nohohank"}}]}))
(ex-02)
;; =>
{:u0 {:id 8648164, :username "nohohank"}}

Here, we’re explicitly setting the :username to "nohohank" instead of using a randomly generated value.

Progressive generation

In the same way you can progressively add more ents, you can progressively generate more random data to be inserted:

(defn ex-03
  []
  (let [result (dc/generate potato-db {:user [{:count 1
                                               :set   {:username "nohohank"}}]})]
    (dc/generate result {:user [{:count 1
                                 :set   {:username "bartleby"}}]})))
(ex-03)
;; =>
{:u0 {:id 85793, :username "nohohank"}
 :u1 {:id 2, :username "bartleby"}}

Dependencies are automatically generated

Just as ent dependencies are automatically added to a potato-db, generating data for a query will generate the data for all implicitly required records:

(defn ex-04
  []
  (dc/generate potato-db {:topic [{:count 1}]}))

{:t0 {:id 4, :owner-id 40034, :name "Q"}
 :u0 {:id 40034, :username "EK8g11"}}

Omitting depencies

If you want to generate a :topic without generating the corresponding :user, you would do that like this:

(defn ex-05
  []
  (dc/generate potato-db {:topic [{:count 1
                                   :refs  {:owner-id ::dc/omit}}]}))
;;=>
{:t0 {:id 6412307, :name "82D8m039P8l3T97nzv0mj7l88cGgu"}}

As you saw in an earlier section, ::dc/omit causes datapotato to act as if a relation doesn’t exist. In this case, we’re telling datapotato to act as if a :topic has not relationship to a :user via its :owner-id key.