Skip to content

Commit

Permalink
Add Swipe records
Browse files Browse the repository at this point in the history
  • Loading branch information
yasuflatland-lf committed Aug 8, 2024
1 parent 9980059 commit 9c39d87
Show file tree
Hide file tree
Showing 14 changed files with 2,343 additions and 205 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ This repository is structured as a mono repository.
- Echo
- gqlgen (GraphQL)

# How to Deploy to Production

## Set up Services on Render
The `render.yaml` is where all configurations are gathered and assosiated with this repository. As soon as a new commit is added to `master` branch, the depolyment is triggered.

## Set up database on Superbase
`Flamingo Armond` uses [Superbase](https://supabase.com/) for the [Database (Postgres)](https://supabase.com/database) and [Auth](https://supabase.com/auth). All configurations and environment valuables are configured on the dashboard. Grab configurations from `.env` file and apply them here, such as database name, user name, user password, SSL enablement, e.g.

## Set up Auth on Superbase
1. Create `Client ID` and `Client Secret` on `Google`
1. Navigate to the dashboard on `Superbase` and chose `Google Auth`
1. Fill out `Client ID` , `Client Secret` and `Authorized Client IDs`

# Run Locally
```
make server
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ CREATE TABLE IF NOT EXISTS cardgroup_users
(
cardgroup_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
state INT DEFAULT 1,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (cardgroup_id, user_id),
Expand All @@ -50,6 +51,7 @@ CREATE TABLE IF NOT EXISTS cardgroup_users
);
CREATE INDEX idx_cardgroup_users_cardgroup_id ON cardgroup_users(cardgroup_id);
CREATE INDEX idx_cardgroup_users_user_id ON cardgroup_users(user_id);
CREATE INDEX idx_cardgroup_users_state ON cardgroup_users(state);

CREATE TABLE IF NOT EXISTS roles
(
Expand All @@ -73,6 +75,18 @@ CREATE TABLE IF NOT EXISTS user_roles
CREATE INDEX idx_user_roles_user_id ON user_roles(user_id);
CREATE INDEX idx_user_roles_role_id ON user_roles(role_id);

CREATE TABLE IF NOT EXISTS swipe_records
(
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
direction TEXT NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);
CREATE INDEX idx_swipe_records_id ON swipe_records(id);
CREATE INDEX idx_swipe_records_user_id ON swipe_records(user_id);

-- +goose statementbegin
-- When the updated column is not changed, assign NULL
CREATE FUNCTION trg_update_timestamp_none() RETURNS trigger AS
Expand Down Expand Up @@ -160,4 +174,5 @@ DROP TABLE IF EXISTS roles;
DROP TABLE IF EXISTS cardgroup_users;
DROP TABLE IF EXISTS cards;
DROP TABLE IF EXISTS cardgroups;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS swipe_records;
DROP TABLE IF EXISTS users;
46 changes: 38 additions & 8 deletions backend/graph/dataloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,26 @@ import (
)

type Loaders struct {
CardLoader dataloader.Interface[int64, *model.Card]
UserLoader dataloader.Interface[int64, *model.User]
RoleLoader dataloader.Interface[int64, *model.Role]
CardGroupLoader dataloader.Interface[int64, *model.CardGroup]
CardLoader dataloader.Interface[int64, *model.Card]
UserLoader dataloader.Interface[int64, *model.User]
RoleLoader dataloader.Interface[int64, *model.Role]
CardGroupLoader dataloader.Interface[int64, *model.CardGroup]
SwipeRecordLoader dataloader.Interface[int64, *model.SwipeRecord]
}

func NewLoaders(srv services.Services) *Loaders {
cardBatcher := &cardBatcher{Srv: srv}
userBatcher := &userBatcher{Srv: srv}
roleBatcher := &roleBatcher{Srv: srv}
cardGroupBatcher := &cardGroupBatcher{Srv: srv}
swipeRecordBatcher := &swipeRecordBatcher{Srv: srv}

return &Loaders{
CardLoader: dataloader.NewBatchedLoader[int64, *model.Card](cardBatcher.BatchGetCards),
UserLoader: dataloader.NewBatchedLoader[int64, *model.User](userBatcher.BatchGetUsers),
RoleLoader: dataloader.NewBatchedLoader[int64, *model.Role](roleBatcher.BatchGetRoles),
CardGroupLoader: dataloader.NewBatchedLoader[int64, *model.CardGroup](cardGroupBatcher.BatchGetCardGroups),
CardLoader: dataloader.NewBatchedLoader[int64, *model.Card](cardBatcher.BatchGetCards),
UserLoader: dataloader.NewBatchedLoader[int64, *model.User](userBatcher.BatchGetUsers),
RoleLoader: dataloader.NewBatchedLoader[int64, *model.Role](roleBatcher.BatchGetRoles),
CardGroupLoader: dataloader.NewBatchedLoader[int64, *model.CardGroup](cardGroupBatcher.BatchGetCardGroups),
SwipeRecordLoader: dataloader.NewBatchedLoader[int64, *model.SwipeRecord](swipeRecordBatcher.BatchGetSwipeRecords),
}
}

Expand Down Expand Up @@ -138,3 +141,30 @@ func (c *cardGroupBatcher) BatchGetCardGroups(ctx context.Context, keys []int64)
}
return results
}

type swipeRecordBatcher struct {
Srv services.Services
}

func (s *swipeRecordBatcher) BatchGetSwipeRecords(ctx context.Context, keys []int64) []*dataloader.Result[*model.SwipeRecord] {
swipeRecords, err := s.Srv.GetSwipeRecordsByIDs(ctx, keys)
if err != nil {
logger.Logger.ErrorContext(ctx, "No swipe records found", err)
return make([]*dataloader.Result[*model.SwipeRecord], len(keys))
}

swipeRecordMap := make(map[int64]*model.SwipeRecord)
for _, swipeRecord := range swipeRecords {
swipeRecordMap[swipeRecord.ID] = swipeRecord
}

results := make([]*dataloader.Result[*model.SwipeRecord], len(keys))
for i, key := range keys {
if swipeRecord, ok := swipeRecordMap[key]; ok {
results[i] = &dataloader.Result[*model.SwipeRecord]{Data: swipeRecord}
} else {
results[i] = &dataloader.Result[*model.SwipeRecord]{Error: errors.New("swipe record not found")}
}
}
return results
}
8 changes: 8 additions & 0 deletions backend/graph/db/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@ type Role struct {
Created time.Time `gorm:"column:created;autoCreateTime"`
Updated time.Time `gorm:"column:updated;autoCreateTime"`
}

type SwipeRecord struct {
ID int64 `gorm:"column:id;primaryKey" validate:"number"`
UserID int64 `gorm:"column:user_id" validate:"number"`
Direction string `gorm:"column:direction;not null" validate:"required"`
Created time.Time `gorm:"column:created;autoCreateTime"`
Updated time.Time `gorm:"column:updated;autoCreateTime"`
}
55 changes: 55 additions & 0 deletions backend/graph/db/swiperecord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package db

import (
customValidator "backend/pkg/validator"
"fmt"
"github.com/go-playground/validator/v10"
"github.com/m-mizutani/goerr"
"gorm.io/gorm"
"time"
)

// BeforeCreate hook to validate the SwipeRecord fields
func (s *SwipeRecord) BeforeCreate(tx *gorm.DB) (err error) {
return s.validateAtCreate(s)
}

// BeforeUpdate hook to validate the SwipeRecord fields
func (s *SwipeRecord) BeforeUpdate(tx *gorm.DB) (err error) {
return s.validateStruct(s)
}

// validateStruct validates the entire SwipeRecord struct
func (s *SwipeRecord) validateStruct(swipeRecord *SwipeRecord) error {
v := customValidator.NewValidateWrapper()
err := v.Validator().Struct(swipeRecord)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
return goerr.Wrap(err, fmt.Sprintf("Field validation for '%s' failed on the '%s' tag", err.Field(), err.Tag()))
}
}
return nil
}

// validateAtCreate validates specific fields during the creation of a SwipeRecord
func (s *SwipeRecord) validateAtCreate(swipeRecord *SwipeRecord) error {
v := customValidator.NewValidateWrapper()

err := v.Validator().Var(swipeRecord.UserID, "required")
if err != nil {
return goerr.Wrap(err, fmt.Sprintf("Field validation for 'user_id' failed %+v", err))
}

err = v.Validator().Var(swipeRecord.Direction, "required,oneof=left right up down") // Assuming 'Direction' has specific allowed values
if err != nil {
return goerr.Wrap(err, fmt.Sprintf("Field validation for 'direction' failed %+v", err))
}

return nil
}

// AfterUpdate hook to update the timestamp in the same transaction
func (s *SwipeRecord) AfterUpdate(tx *gorm.DB) (err error) {
tx.Model(&SwipeRecord{}).Where("id = ?", s.ID).Update("updated", time.Now())
return nil
}
Loading

0 comments on commit 9c39d87

Please sign in to comment.