implement repository layer

This commit is contained in:
Hayden 2022-12-05 17:51:40 -09:00
parent f1082dafed
commit ba739a02da
No known key found for this signature in database
GPG key ID: 17CF79474E257545
4 changed files with 195 additions and 10 deletions

View file

@ -16,17 +16,16 @@ func mapTErrFunc[T any, Y any](fn func(T) Y) func(T, error) (Y, error) {
}
}
// TODO: Future Usage
// func mapEachFunc[T any, Y any](fn func(T) Y) func([]T) []Y {
// return func(items []T) []Y {
// result := make([]Y, len(items))
// for i, item := range items {
// result[i] = fn(item)
// }
func mapTEachFunc[T any, Y any](fn func(T) Y) func([]T) []Y {
return func(items []T) []Y {
result := make([]Y, len(items))
for i, item := range items {
result[i] = fn(item)
}
// return result
// }
// }
return result
}
}
func mapTEachErrFunc[T any, Y any](fn func(T) Y) func([]T, error) ([]Y, error) {
return func(items []T, err error) ([]Y, error) {

View file

@ -0,0 +1,118 @@
package repo
import (
"context"
"time"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry"
)
// MaintenanceEntryRepository is a repository for maintenance entries that are
// associated with an item in the database. An entry represents a maintenance event
// that has been performed on an item.
type MaintenanceEntryRepository struct {
db *ent.Client
}
type (
MaintenanceEntryCreate struct {
ItemID uuid.UUID `json:"itemId"`
Date time.Time `json:"date"`
Name string `json:"name"`
Description string `json:"description"`
Cost float64 `json:"cost"`
}
MaintenanceEntry struct {
ID uuid.UUID `json:"id"`
Date time.Time `json:"date"`
Name string `json:"name"`
Description string `json:"description"`
Cost float64 `json:"cost"`
}
MaintenanceLog struct {
ItemID uuid.UUID `json:"itemId"`
CostAverage float64 `json:"costAverage"`
CostTotal float64 `json:"costTotal"`
Entries []MaintenanceEntry
}
)
var (
mapMaintenanceEntryErr = mapTErrFunc(mapMaintenanceEntry)
mapEachMaintenanceEntry = mapTEachFunc(mapMaintenanceEntry)
)
func mapMaintenanceEntry(entry *ent.MaintenanceEntry) MaintenanceEntry {
return MaintenanceEntry{
ID: entry.ID,
Date: entry.Date,
Name: entry.Name,
Description: entry.Description,
Cost: entry.Cost,
}
}
func (r *MaintenanceEntryRepository) Create(ctx context.Context, input MaintenanceEntryCreate) (MaintenanceEntry, error) {
item, err := r.db.MaintenanceEntry.Create().
SetItemID(input.ItemID).
SetDate(input.Date).
SetName(input.Name).
SetDescription(input.Description).
SetCost(input.Cost).
Save(ctx)
return mapMaintenanceEntryErr(item, err)
}
func (r *MaintenanceEntryRepository) GetLog(ctx context.Context, itemID uuid.UUID) (MaintenanceLog, error) {
log := MaintenanceLog{
ItemID: itemID,
}
entries, err := r.db.MaintenanceEntry.Query().
Where(maintenanceentry.ItemID(itemID)).
All(ctx)
if err != nil {
return MaintenanceLog{}, err
}
log.Entries = mapEachMaintenanceEntry(entries)
var maybeTotal *float64
var maybeAverage *float64
q := `
SELECT
SUM(cost_total) AS total_of_totals,
AVG(cost_total) AS avg_of_averages
FROM
(
SELECT
strftime('%m-%Y', date) AS my,
SUM(cost) AS cost_total
FROM
maintenance_entries
WHERE
item_id = ?
GROUP BY
my
)`
row := r.db.Sql().QueryRowContext(ctx, q, itemID)
err = row.Scan(&maybeTotal, &maybeAverage)
if err != nil {
return MaintenanceLog{}, err
}
log.CostAverage = orDefault(maybeAverage, 0)
log.CostTotal = orDefault(maybeTotal, 0)
return log, nil
}
func (r *MaintenanceEntryRepository) Delete(ctx context.Context, ID uuid.UUID) error {
return r.db.MaintenanceEntry.DeleteOneID(ID).Exec(ctx)
}

View file

@ -0,0 +1,66 @@
package repo
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestMaintenanceEntryRepository_GetLog(t *testing.T) {
item := useItems(t, 1)[0]
// Create 10 maintenance entries for the item
created := make([]MaintenanceEntryCreate, 10)
lastMonth := time.Now().AddDate(0, -1, 0)
thisMonth := time.Now()
for i := 0; i < 10; i++ {
dt := lastMonth
if i%2 == 0 {
dt = thisMonth
}
created[i] = MaintenanceEntryCreate{
ItemID: item.ID,
Date: dt,
Name: "Maintenance",
Description: "Maintenance description",
Cost: 10,
}
}
for _, entry := range created {
_, err := tRepos.MaintEntry.Create(context.Background(), entry)
if err != nil {
t.Fatalf("failed to create maintenance entry: %v", err)
}
}
// Get the log for the item
log, err := tRepos.MaintEntry.GetLog(context.Background(), item.ID)
if err != nil {
t.Fatalf("failed to get maintenance log: %v", err)
}
assert.Equal(t, item.ID, log.ItemID)
assert.Equal(t, 10, len(log.Entries))
// Calculate the average cost
var total float64
for _, entry := range log.Entries {
total += entry.Cost
}
assert.Equal(t, total, log.CostTotal, "total cost should be equal to the sum of all entries")
assert.Equal(t, total/2, log.CostAverage, "average cost should be the average of the two months")
for _, entry := range log.Entries {
err := tRepos.MaintEntry.Delete(context.Background(), entry.ID)
assert.NoError(t, err)
}
}

View file

@ -12,6 +12,7 @@ type AllRepos struct {
Items *ItemsRepository
Docs *DocumentRepository
Attachments *AttachmentRepo
MaintEntry *MaintenanceEntryRepository
}
func New(db *ent.Client, root string) *AllRepos {
@ -24,5 +25,6 @@ func New(db *ent.Client, root string) *AllRepos {
Items: &ItemsRepository{db},
Docs: &DocumentRepository{db, root},
Attachments: &AttachmentRepo{db},
MaintEntry: &MaintenanceEntryRepository{db},
}
}