Skip to content
/ debefix Public

Go library to seed database data and/or create fixtures for DB tests, with data relationships.

License

Notifications You must be signed in to change notification settings

rrgmc/debefix

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

debefix - Database seeding and fixtures

GoDoc

debefix is a Go library to seed database data and/or create fixtures for DB tests.

Tables can reference each other using string ids (called RefID), and generated fields (like database auto increment or generated UUID) are supported and can be resolved and used by other table's references.

Dependencies between tables can be detected automatically by reference ids, or manually. This is used to generate a dependency graph and output the insert statements in the correct order.

Row values can reference each other using Value or ValueMultiple interface implementations. They are referenced after they are resolved, so it can reference values generated on execution like autoincrement fields.

Install

go get -u github.com/rrgmc/debefix/v2

Goals

  • For developer seeding or test fixtures, not for inserting a huge amount of records.

Example

import (
    "context"
    "os"

    "github.com/rrgmc/debefix-db/v2/sql"
    "github.com/rrgmc/debefix-db/v2/sql/postgres"
    "github.com/rrgmc/debefix/v2"
)

func ExampleResolve() {
    ctx := context.Background()
    data := debefix.NewData()

    tableTags := debefix.TableName("public.tags")
    tableUsers := debefix.TableName("public.users")
    tablePosts := debefix.TableName("public.posts")
    tablePostTags := debefix.TableName("public.post_tags")

    data.AddValues(tableTags,
        debefix.MapValues{
            // this field value will be generated at resolve time, for example as a database autoincrement.
            "tag_id": debefix.ResolveValueResolve(),
            // sets the RefID to be targeted by `ValueRefID(tableTags, "go", "tag_id")`. Field is not added to the row values.
            "_refid": debefix.SetValueRefID("go"),
            "name":   "Go",
            // set the value as the resolver base time plus 1 hour.
            "created_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(1)),
            "updated_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(1)),
        },
        debefix.MapValues{
            "tag_id":     debefix.ResolveValueResolve(),
            "_refid":     debefix.SetValueRefID("javascript"),
            "name":       "JavaScript",
            "created_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(1), debefix.WithAddMinutes(2)),
            "updated_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(1), debefix.WithAddMinutes(2)),
        },
        debefix.MapValues{
            "tag_id":     debefix.ResolveValueResolve(),
            "_refid":     debefix.SetValueRefID("cpp"),
            "name":       "C++",
            "created_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(1), debefix.WithAddMinutes(2)),
            "updated_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(1), debefix.WithAddMinutes(2)),
        },
    )

    data.AddValues(tableUsers,
        debefix.MapValues{
            "user_id": 1, // fixed ID, not generated by the database
            // sets the RefID to be targeted by `ValueRefID(tableUsers, "johndoe", "user_id")`. Field is not added to the row values.
            "_refid":     debefix.SetValueRefID("johndoe"),
            "name":       "John Doe",
            "email":      "john@example.com",
            "created_at": debefix.ValueBaseTimeAdd(debefix.WithAddMinutes(30)),
            "updated_at": debefix.ValueBaseTimeAdd(debefix.WithAddMinutes(30)),
        },
        debefix.MapValues{
            "user_id":    2,
            "_refid":     debefix.SetValueRefID("janedoe"),
            "name":       "Jane Doe",
            "email":      "jane@example.com",
            "created_at": debefix.ValueBaseTimeAdd(debefix.WithAddMinutes(30)),
            "updated_at": debefix.ValueBaseTimeAdd(debefix.WithAddMinutes(30)),
        },
    )

    // AddWithID returns an internal ID reference that can be used later.
    post1IID := data.AddWithID(tablePosts,
        debefix.MapValues{
            "post_id": debefix.ValueUUIDRandom(), // generates a random UUID value.
            "title":   "First post",
            "text":    "This is the text of the first post",
            // returns the value of the "user_id" field of the row with the "johndoe" RefID in the tableUsers table.
            // this also adds a dependency between "posts" and "users", so "users" will always be resolved before "posts".
            "user_id":    debefix.ValueRefID(tableUsers, "johndoe", "user_id"),
            "created_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(2)),
            "updated_at": debefix.ValueBaseTimeAdd(debefix.WithAddHours(2)),
        })
    post2IID := data.AddWithID(tablePosts,
        debefix.MapValues{
            "post_id": debefix.ValueUUIDRandom(), // generates a random UUID value.
            // data is inserted in the order they were added, so it is possible to reference another row in the same table.
            "parent_post_id": post1IID.ValueForField("post_id"),
            "title":          "Second post",
            "text":           "This is the text of the second post",
            "user_id":        debefix.ValueRefID(tableUsers, "johndoe", "user_id"),
            "created_at":     debefix.ValueBaseTimeAdd(debefix.WithAddHours(2)),
            "updated_at":     debefix.ValueBaseTimeAdd(debefix.WithAddHours(2)),
        },
    )

    data.AddValues(debefix.TableName(tablePostTags),
        debefix.MapValues{
            // returns the value of the "post_id" field of the row referenced by "post1IID", after it was resolved.
            "post_id": post1IID.ValueForField("post_id"),
            // tag_id is generated by the database, so the value will be resolved before being set here.
            "tag_id": debefix.ValueRefID(tableTags, "go", "tag_id"),
        },
        debefix.MapValues{
            "post_id": post1IID.ValueForField("post_id"),
            "tag_id":  debefix.ValueRefID(tableTags, "cpp", "tag_id"),
        },
        debefix.MapValues{
            // returns the value of the "post_id" field of the row referenced by "post2IID", after it was resolved.
            "post_id": post2IID.ValueForField("post_id"),
            "tag_id":  debefix.ValueRefID(tableTags, "javascript", "tag_id"),
        },
    )

    if data.Err() != nil {
        panic(data.Err())
    }

    // outputs all generated queries
    qi := sql.NewDebugQueryInterface(os.Stdout)

    // resolve the rows using a SQL query resolver.
    _, err := debefix.Resolve(ctx, data,
        postgres.ResolveFunc(qi))
    if err != nil {
        panic(err)
    }

    // =============== public.tags ===============
    // INSERT INTO "public.tags" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "tag_id"
    // $$ ARGS: [0:"2024-11-29 08:30:16.028185 -0300 -03 m=+3600.002859876"] [1:"Go"] [2:"2024-11-29 08:30:16.028185 -0300 -03 m=+3600.002859876"]
    // --------------------INSERT INTO "public.tags" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "tag_id"
    // $$ ARGS: [0:"2024-11-29 08:32:16.028185 -0300 -03 m=+3720.002859876"] [1:"JavaScript"] [2:"2024-11-29 08:32:16.028185 -0300 -03 m=+3720.002859876"]
    // --------------------INSERT INTO "public.tags" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "tag_id"
    // $$ ARGS: [0:"2024-11-29 08:32:16.028185 -0300 -03 m=+3720.002859876"] [1:"C++"] [2:"2024-11-29 08:32:16.028185 -0300 -03 m=+3720.002859876"]
    // =============== public.users ===============
    // INSERT INTO "public.users" ("created_at", "email", "name", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5)
    // $$ ARGS: [0:"2024-11-29 08:00:16.028185 -0300 -03 m=+1800.002859876"] [1:"john@example.com"] [2:"John Doe"] [3:"2024-11-29 08:00:16.028185 -0300 -03 m=+1800.002859876"] [4:"1"]
    // --------------------INSERT INTO "public.users" ("created_at", "email", "name", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5)
    // $$ ARGS: [0:"2024-11-29 08:00:16.028185 -0300 -03 m=+1800.002859876"] [1:"jane@example.com"] [2:"Jane Doe"] [3:"2024-11-29 08:00:16.028185 -0300 -03 m=+1800.002859876"] [4:"2"]
    // =============== public.posts ===============
    // INSERT INTO "public.posts" ("created_at", "post_id", "text", "title", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6)
    // $$ ARGS: [0:"2024-11-29 09:30:16.028185 -0300 -03 m=+7200.002859876"] [1:"91a27c77-ff00-4f3b-90d6-51e335b7ad36"] [2:"This is the text of the first post"] [3:"First post"] [4:"2024-11-29 09:30:16.028185 -0300 -03 m=+7200.002859876"] [5:"1"]
    // --------------------INSERT INTO "public.posts" ("created_at", "parent_post_id", "post_id", "text", "title", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7)
    // $$ ARGS: [0:"2024-11-29 09:30:16.028185 -0300 -03 m=+7200.002859876"] [1:"91a27c77-ff00-4f3b-90d6-51e335b7ad36"] [2:"c06a1f3e-3578-4b56-bf51-9fb949ae5dbf"] [3:"This is the text of the second post"] [4:"Second post"] [5:"2024-11-29 09:30:16.028185 -0300 -03 m=+7200.002859876"] [6:"1"]
    // =============== public.post_tags ===============
    // INSERT INTO "public.post_tags" ("post_id", "tag_id") VALUES ($1, $2)
    // $$ ARGS: [0:"91a27c77-ff00-4f3b-90d6-51e335b7ad36"] [1:"223eca8c-d2c3-46ef-bb1f-d175916c97a2"]
    // --------------------INSERT INTO "public.post_tags" ("post_id", "tag_id") VALUES ($1, $2)
    // $$ ARGS: [0:"91a27c77-ff00-4f3b-90d6-51e335b7ad36"] [1:"d5c6e911-f38e-4cef-958b-cd5d8fc787f2"]
    // --------------------INSERT INTO "public.post_tags" ("post_id", "tag_id") VALUES ($1, $2)
    // $$ ARGS: [0:"c06a1f3e-3578-4b56-bf51-9fb949ae5dbf"] [1:"bd2d497f-af58-4338-a040-a24002492436"]
}

Samples

Extra

License

MIT

Author

Rangel Reale (rangelreale@gmail.com)

About

Go library to seed database data and/or create fixtures for DB tests, with data relationships.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages