From 857380655bb3d1b6931215c9cfd9d40dc19a78c0 Mon Sep 17 00:00:00 2001 From: itsscb Date: Mon, 18 Sep 2023 15:17:15 +0200 Subject: [PATCH] ft/adds person and store --- db/query/person.sql | 45 +++++++++ db/sqlc/models.go | 26 ++--- db/sqlc/person.sql.go | 221 +++++++++++++++++++++++++++++++++++++++++ db/sqlc/person_test.go | 122 +++++++++++++++++++++++ db/sqlc/store.go | 39 ++++++++ 5 files changed, 440 insertions(+), 13 deletions(-) create mode 100644 db/query/person.sql create mode 100644 db/sqlc/person.sql.go create mode 100644 db/sqlc/person_test.go create mode 100644 db/sqlc/store.go diff --git a/db/query/person.sql b/db/query/person.sql new file mode 100644 index 0000000..a7f4f79 --- /dev/null +++ b/db/query/person.sql @@ -0,0 +1,45 @@ +-- name: GetPerson :one +SELECT * FROM persons +WHERE "ID" = $1 LIMIT 1; + +-- name: CreatePerson :one +INSERT INTO persons ( + "customerID", + firstname, + lastname, + birthday, + city, + zip, + street, + country, + creator, + changer +) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 +) RETURNING *; + +-- name: ListPersons :many +SELECT * FROM persons +ORDER BY lastname, firstname +LIMIT $1 +OFFSET $2; + +-- name: UpdatePerson :one +UPDATE persons +SET + "customerID" = COALESCE(sqlc.narg(customerID), "customerID"), + firstname = COALESCE(sqlc.narg(firstname), firstname), + lastname = COALESCE(sqlc.narg(lastname), lastname), + birthday = COALESCE(sqlc.narg(birthday), birthday), + city = COALESCE(sqlc.narg(city), city), + zip = COALESCE(sqlc.narg(zip), zip), + street = COALESCE(sqlc.narg(street), street), + country = COALESCE(sqlc.narg(country), country), + changer = $2, + changed = now() +WHERE "ID" = $1 +RETURNING *; + +-- name: DeletePerson :exec +DELETE FROM persons +WHERE "ID" = $1; \ No newline at end of file diff --git a/db/sqlc/models.go b/db/sqlc/models.go index 28e885d..66b54d0 100644 --- a/db/sqlc/models.go +++ b/db/sqlc/models.go @@ -74,19 +74,19 @@ type Payment struct { } type Person struct { - ID int64 `json:"ID"` - CustomerID sql.NullInt64 `json:"customerID"` - Firstname string `json:"firstname"` - Lastname string `json:"lastname"` - Birthday time.Time `json:"birthday"` - City string `json:"city"` - Zip string `json:"zip"` - Street string `json:"street"` - Country string `json:"country"` - Creator string `json:"creator"` - Created time.Time `json:"created"` - Changer string `json:"changer"` - Changed time.Time `json:"changed"` + ID int64 `json:"ID"` + CustomerID int64 `json:"customerID"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Birthday time.Time `json:"birthday"` + City string `json:"city"` + Zip string `json:"zip"` + Street string `json:"street"` + Country string `json:"country"` + Creator string `json:"creator"` + Created time.Time `json:"created"` + Changer string `json:"changer"` + Changed time.Time `json:"changed"` } type Provider struct { diff --git a/db/sqlc/person.sql.go b/db/sqlc/person.sql.go new file mode 100644 index 0000000..c2ec297 --- /dev/null +++ b/db/sqlc/person.sql.go @@ -0,0 +1,221 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.21.0 +// source: person.sql + +package db + +import ( + "context" + "database/sql" + "time" +) + +const createPerson = `-- name: CreatePerson :one +INSERT INTO persons ( + "customerID", + firstname, + lastname, + birthday, + city, + zip, + street, + country, + creator, + changer +) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 +) RETURNING "ID", "customerID", firstname, lastname, birthday, city, zip, street, country, creator, created, changer, changed +` + +type CreatePersonParams struct { + CustomerID int64 `json:"customerID"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Birthday time.Time `json:"birthday"` + City string `json:"city"` + Zip string `json:"zip"` + Street string `json:"street"` + Country string `json:"country"` + Creator string `json:"creator"` + Changer string `json:"changer"` +} + +func (q *Queries) CreatePerson(ctx context.Context, arg CreatePersonParams) (Person, error) { + row := q.db.QueryRowContext(ctx, createPerson, + arg.CustomerID, + arg.Firstname, + arg.Lastname, + arg.Birthday, + arg.City, + arg.Zip, + arg.Street, + arg.Country, + arg.Creator, + arg.Changer, + ) + var i Person + err := row.Scan( + &i.ID, + &i.CustomerID, + &i.Firstname, + &i.Lastname, + &i.Birthday, + &i.City, + &i.Zip, + &i.Street, + &i.Country, + &i.Creator, + &i.Created, + &i.Changer, + &i.Changed, + ) + return i, err +} + +const deletePerson = `-- name: DeletePerson :exec +DELETE FROM persons +WHERE "ID" = $1 +` + +func (q *Queries) DeletePerson(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deletePerson, id) + return err +} + +const getPerson = `-- name: GetPerson :one +SELECT "ID", "customerID", firstname, lastname, birthday, city, zip, street, country, creator, created, changer, changed FROM persons +WHERE "ID" = $1 LIMIT 1 +` + +func (q *Queries) GetPerson(ctx context.Context, id int64) (Person, error) { + row := q.db.QueryRowContext(ctx, getPerson, id) + var i Person + err := row.Scan( + &i.ID, + &i.CustomerID, + &i.Firstname, + &i.Lastname, + &i.Birthday, + &i.City, + &i.Zip, + &i.Street, + &i.Country, + &i.Creator, + &i.Created, + &i.Changer, + &i.Changed, + ) + return i, err +} + +const listPersons = `-- name: ListPersons :many +SELECT "ID", "customerID", firstname, lastname, birthday, city, zip, street, country, creator, created, changer, changed FROM persons +ORDER BY lastname, firstname +LIMIT $1 +OFFSET $2 +` + +type ListPersonsParams struct { + Limit int32 `json:"limit"` + Offset int32 `json:"offset"` +} + +func (q *Queries) ListPersons(ctx context.Context, arg ListPersonsParams) ([]Person, error) { + rows, err := q.db.QueryContext(ctx, listPersons, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Person{} + for rows.Next() { + var i Person + if err := rows.Scan( + &i.ID, + &i.CustomerID, + &i.Firstname, + &i.Lastname, + &i.Birthday, + &i.City, + &i.Zip, + &i.Street, + &i.Country, + &i.Creator, + &i.Created, + &i.Changer, + &i.Changed, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updatePerson = `-- name: UpdatePerson :one +UPDATE persons +SET + "customerID" = COALESCE($3, "customerID"), + firstname = COALESCE($4, firstname), + lastname = COALESCE($5, lastname), + birthday = COALESCE($6, birthday), + city = COALESCE($7, city), + zip = COALESCE($8, zip), + street = COALESCE($9, street), + country = COALESCE($10, country), + changer = $2, + changed = now() +WHERE "ID" = $1 +RETURNING "ID", "customerID", firstname, lastname, birthday, city, zip, street, country, creator, created, changer, changed +` + +type UpdatePersonParams struct { + ID int64 `json:"ID"` + Changer string `json:"changer"` + Customerid sql.NullInt64 `json:"customerid"` + Firstname sql.NullString `json:"firstname"` + Lastname sql.NullString `json:"lastname"` + Birthday sql.NullTime `json:"birthday"` + City sql.NullString `json:"city"` + Zip sql.NullString `json:"zip"` + Street sql.NullString `json:"street"` + Country sql.NullString `json:"country"` +} + +func (q *Queries) UpdatePerson(ctx context.Context, arg UpdatePersonParams) (Person, error) { + row := q.db.QueryRowContext(ctx, updatePerson, + arg.ID, + arg.Changer, + arg.Customerid, + arg.Firstname, + arg.Lastname, + arg.Birthday, + arg.City, + arg.Zip, + arg.Street, + arg.Country, + ) + var i Person + err := row.Scan( + &i.ID, + &i.CustomerID, + &i.Firstname, + &i.Lastname, + &i.Birthday, + &i.City, + &i.Zip, + &i.Street, + &i.Country, + &i.Creator, + &i.Created, + &i.Changer, + &i.Changed, + ) + return i, err +} diff --git a/db/sqlc/person_test.go b/db/sqlc/person_test.go new file mode 100644 index 0000000..ab2b5f7 --- /dev/null +++ b/db/sqlc/person_test.go @@ -0,0 +1,122 @@ +package db + +import ( + "context" + "database/sql" + "testing" + "time" + + "github.com/itsscb/df/util" + "github.com/stretchr/testify/require" +) + +func createRandomPerson(t *testing.T) Person { + account := createRandomCustomer(t) + require.NotEmpty(t, account) + + arg := CreatePersonParams{ + CustomerID: account.ID, + Firstname: util.RandomUser(), + Lastname: util.RandomUser(), + Birthday: time.Date(1990, 1, 1, 0, 0, 0, 0, time.UTC), + City: util.RandomString(15), + Zip: util.RandomString(5), + Street: util.RandomString(20), + Country: util.RandomString(15), + Creator: util.RandomUser(), + } + + person, err := testQueries.CreatePerson(context.Background(), arg) + require.NoError(t, err) + require.NotEmpty(t, person) + + require.Equal(t, arg.CustomerID, person.CustomerID) + require.Equal(t, arg.Firstname, person.Firstname) + require.Equal(t, arg.Lastname, person.Lastname) + require.Equal(t, arg.Birthday, person.Birthday) + require.Equal(t, arg.City, person.City) + require.Equal(t, arg.Zip, person.Zip) + require.Equal(t, arg.Street, person.Street) + require.Equal(t, arg.Country, person.Country) + require.Equal(t, arg.Creator, person.Creator) + + require.NotZero(t, person.ID) + require.NotZero(t, person.Created) + + return person +} + +func TestCreatePerson(t *testing.T) { + createRandomPerson(t) +} + +func TestGetPerson(t *testing.T) { + newperson := createRandomPerson(t) + require.NotEmpty(t, newperson) + + person, err := testQueries.GetPerson(context.Background(), newperson.ID) + require.NoError(t, err) + require.NotEmpty(t, person) + + require.Equal(t, newperson.Firstname, person.Firstname) + require.Equal(t, newperson.Lastname, person.Lastname) + require.Equal(t, newperson.Birthday, person.Birthday) + require.Equal(t, newperson.City, person.City) + require.Equal(t, newperson.Zip, person.Zip) + require.Equal(t, newperson.Street, person.Street) + require.Equal(t, newperson.Country, person.Country) + require.Equal(t, newperson.Creator, person.Creator) + + require.WithinDuration(t, newperson.Created, person.Created, time.Second) +} + +func TestDeletePerson(t *testing.T) { + person1 := createRandomPerson(t) + err := testQueries.DeletePerson(context.Background(), person1.ID) + require.NoError(t, err) + + person2, err := testQueries.GetPerson(context.Background(), person1.ID) + require.Error(t, err) + require.EqualError(t, err, sql.ErrNoRows.Error()) + require.Empty(t, person2) +} + +func TestUpdatePerson(t *testing.T) { + person1 := createRandomPerson(t) + require.NotEmpty(t, person1) + + arg := UpdatePersonParams{ + ID: person1.ID, + Lastname: sql.NullString{ + String: util.RandomPhone(), + Valid: true, + }, + } + + person2, err := testQueries.UpdatePerson(context.Background(), arg) + require.NoError(t, err) + require.NotEmpty(t, person2) + + require.Equal(t, person1.ID, person2.ID) + require.Equal(t, person1.Firstname, person2.Firstname) + require.NotEqual(t, person1.Lastname, person2.Lastname) +} + +func TestListPersons(t *testing.T) { + for i := 0; i < 10; i++ { + createRandomPerson(t) + } + + arg := ListPersonsParams{ + Limit: 5, + Offset: 5, + } + + persons, err := testQueries.ListPersons(context.Background(), arg) + require.NoError(t, err) + require.Len(t, persons, 5) + + for _, person := range persons { + require.NotEmpty(t, person) + } +} diff --git a/db/sqlc/store.go b/db/sqlc/store.go new file mode 100644 index 0000000..3dcfb8b --- /dev/null +++ b/db/sqlc/store.go @@ -0,0 +1,39 @@ +package db + +import ( + "context" + "database/sql" + "fmt" +) + +// Store provides all functions to execute db queries and transactions +type Store struct { + *Queries + db *sql.DB +} + +func NewStore(db *sql.DB) *Store { + return &Store{ + db: db, + Queries: New(db), + } +} + +// execTx executes a function within a database transaction +func (store *Store) execTx(ctx context.Context, fn func(*Queries) error) error { + tx, err := store.db.BeginTx(ctx, nil) + if err != nil { + return err + } + + q := New(tx) + err = fn(q) + if err != nil { + if rbErr := tx.Rollback(); rbErr != nil { + return fmt.Errorf("tx err: %v, rb err: %v", err, rbErr) + } + return err + } + + return tx.Commit() +}