Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ Counters also has support for Docker Compose, a extended version of docker that
- [] Add API Integration
- [] Add Logging (similar to Votes "Actions")
- [] Time Zone support


67 changes: 45 additions & 22 deletions internal/database/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package database

import (
"context"
"fmt"
"strings"
"time"

"github.com/ComputerScienceHouse/counters/internal/models"
"github.com/jackc/pgx/v5"
)

const MAX_COUNTERS_IN_QUERY = 10

func CreateCounterWithReset(ctx context.Context, userId string, username string, cReq *models.CreateCounterInput, rReq *models.CreateResetInput) (string, error) {
tx, err := db.Begin(ctx)
if err != nil {
Expand Down Expand Up @@ -45,42 +49,61 @@ func CreateCounterWithReset(ctx context.Context, userId string, username string,
return counterId, nil
}

func GetCounters(ctx context.Context) ([]*models.CounterListPart, error) {
rows, err := db.Query(ctx, `
SELECT c.counter_id, c.user_id, c.title, c.description, r.description, r.username, r.occured_at FROM counters c
func GetCounters(ctx context.Context, filter *models.GetCountersFilter) ([]*models.GetCountersCounterPart, error) {
args := make([]any, 0)

postQuery := `
SELECT c.counter_id, c.title, r.description, r.username, r.occured_at FROM counters c
JOIN resets r ON c.last_reset = r.reset_id
ORDER BY r.occured_at DESC
`)
`
if filter.Timestamp != nil {
args = append(args, time.Unix(*filter.Timestamp, 0))

if filter.SortDescending {
postQuery = fmt.Sprintf("%s AND r.occured_at < $%d", postQuery, len(args))
} else {
postQuery = fmt.Sprintf("%s AND r.occured_at > $%d", postQuery, len(args))
}
}

if filter.Name != nil && strings.TrimSpace(*filter.Name) != "" {
args = append(args, "%"+*filter.Name+"%")
postQuery = fmt.Sprintf("%s AND c.title ILIKE $%d", postQuery, len(args))
}

if filter.SortDescending {
postQuery = fmt.Sprintf("%s ORDER BY r.occured_at DESC LIMIT %d", postQuery, MAX_COUNTERS_IN_QUERY)
} else {
postQuery = fmt.Sprintf("%s ORDER BY r.occured_at ASC LIMIT %d", postQuery, MAX_COUNTERS_IN_QUERY)
}

rows, err := db.Query(ctx, postQuery, args...)
if err != nil {
return nil, err
}
defer rows.Close()

counters := make([]*models.CounterListPart, 0)
counters := make([]*models.GetCountersCounterPart, 0)

for rows.Next() {
var (
counterId string
counterOwnerID string
counterTitle string
counterDescription string
resetDescription string
resetUsername string
resetCreatedAt time.Time
counterId string
counterTitle string
resetDescription string
resetUsername string
resetCreatedAt time.Time
)

if err := rows.Scan(&counterId, &counterOwnerID, &counterTitle, &counterDescription, &resetDescription, &resetUsername, &resetCreatedAt); err != nil {
if err := rows.Scan(&counterId, &counterTitle, &resetDescription, &resetUsername, &resetCreatedAt); err != nil {
return nil, err
}

counters = append(counters, &models.CounterListPart{
CounterID: counterId,
CounterOwner: counterOwnerID,
CounterTitle: counterTitle,
CounterDescription: counterDescription,
ResetDescription: resetDescription,
ResetUsername: resetUsername,
ResetOccuredAt: resetCreatedAt.Unix(),
counters = append(counters, &models.GetCountersCounterPart{
CounterID: counterId,
CounterTitle: counterTitle,
ResetDescription: resetDescription,
ResetUsername: resetUsername,
ResetTimestamp: resetCreatedAt.Unix(),
})
}

Expand Down
36 changes: 29 additions & 7 deletions internal/database/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package database

import (
"context"
"fmt"
"time"

"github.com/ComputerScienceHouse/counters/internal/models"
"github.com/ComputerScienceHouse/counters/internal/util"
"github.com/jackc/pgx/v5"
)

const MAX_RESETS_IN_QUERY = 10

func CreateReset(ctx context.Context, userID string, username string, req *models.CreateResetInput) (string, error) {
resetID, err := GenerateUUID()
if err != nil {
Expand Down Expand Up @@ -73,12 +76,31 @@ func GetCounterFromReset(ctx context.Context, resetID string) (string, error) {
return counterID, nil
}

func GetResetsFromCounterId(ctx context.Context, counterID string) ([]*models.ResetListPart, error) {
rows, err := db.Query(ctx, `
func GetResetsFromCounterId(ctx context.Context, counterID string, filter *models.GetResetsFilter) ([]*models.ResetListPart, error) {
args := make([]any, 0)
args = append(args, counterID)

postQuery := `
SELECT r.reset_id, r.description, r.user_id, r.username, r.occured_at FROM resets r
WHERE r.counter_id = $1
ORDER BY r.occured_at DESC
`, counterID)
`
if filter.Timestamp != nil {
args = append(args, time.Unix(*filter.Timestamp, 0))

if filter.SortDescending {
postQuery = fmt.Sprintf("%s AND r.occured_at < $%d", postQuery, len(args))
} else {
postQuery = fmt.Sprintf("%s AND r.occured_at > $%d", postQuery, len(args))
}
}

if filter.SortDescending {
postQuery = fmt.Sprintf("%s ORDER BY r.occured_at DESC LIMIT %d", postQuery, MAX_RESETS_IN_QUERY)
} else {
postQuery = fmt.Sprintf("%s ORDER BY r.occured_at ASC LIMIT %d", postQuery, MAX_RESETS_IN_QUERY)
}

rows, err := db.Query(ctx, postQuery, args...)
if err != nil {
return nil, err
}
Expand All @@ -92,10 +114,10 @@ func GetResetsFromCounterId(ctx context.Context, counterID string) ([]*models.Re
resetDescription string
resetOwner string
resetUsername string
resetOccuredAt time.Time
resetOccurredAt time.Time
)

if err := rows.Scan(&resetId, &resetDescription, &resetOwner, &resetUsername, &resetOccuredAt); err != nil {
if err := rows.Scan(&resetId, &resetDescription, &resetOwner, &resetUsername, &resetOccurredAt); err != nil {
return nil, err
}

Expand All @@ -104,7 +126,7 @@ func GetResetsFromCounterId(ctx context.Context, counterID string) ([]*models.Re
ResetDescription: resetDescription,
ResetOwner: resetOwner,
ResetUsername: resetUsername,
ResetOccuredAt: resetOccuredAt.Unix(),
ResetOccurredAt: resetOccurredAt.Unix(),
})
}

Expand Down
25 changes: 25 additions & 0 deletions internal/models/api_counter.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
package models

type GetCountersFilter struct {
Timestamp *int64 `json:"timestamp"`
SortDescending bool `json:"sort_descending"`
Name *string `json:"name"`
}

type GetCountersCounterPart struct {
CounterID string `json:"counter_id"`
CounterTitle string `json:"counter_title"`
ResetDescription string `json:"reset_description"`
ResetUsername string `json:"reset_username"`
ResetTimestamp int64 `json:"reset_timestamp"`
}

type GetCountersInput struct {
CounterID string `json:"counter_id"`
Filter GetCountersFilter `json:"filter"`
}

type GetCountersOutput struct {
Counters []*GetCountersCounterPart `json:"counters"`
HasMore bool `json:"has_more"`
Timestamp *int64 `json:"timestamp"`
}

type CreateCounterWithResetInput struct {
Counter CreateCounterInput `json:"counter"`
Reset CreateResetInput `json:"reset"`
Expand Down
24 changes: 24 additions & 0 deletions internal/models/api_reset.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
package models

type GetResetsFilter struct {
Timestamp *int64 `json:"timestamp"`
SortDescending bool `json:"sort_descending"`
}

type GetResetsResetPart struct {
Id string `json:"id"`
Username string `json:"username"`
Description string `json:"description"`
Timestamp int64 `json:"timestamp"`
CanDelete bool `json:"can_delete"`
}

type GetResetsInput struct {
CounterID string `json:"counter_id"`
Filter GetResetsFilter `json:"filter"`
}

type GetResetsOutput struct {
Resets []*GetResetsResetPart `json:"resets"`
HasMore bool `json:"has_more"`
Timestamp *int64 `json:"timestamp"`
}

type CreateResetInput struct {
CounterID string `json:"counter_id"`
Description string `json:"description"`
Expand Down
2 changes: 1 addition & 1 deletion internal/models/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ type ResetListPart struct {
ResetDescription string `json:"reset_description"`
ResetOwner string `json:"reset_owner"`
ResetUsername string `json:"reset_username"`
ResetOccuredAt int64 `json:"reset_occured_at"`
ResetOccurredAt int64 `json:"reset_occurred_at"`
}
57 changes: 36 additions & 21 deletions internal/routes/api/v1/counters/counter.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package counters

import (
"context"
"net/http"
"time"

"github.com/ComputerScienceHouse/counters/internal/database"
"github.com/ComputerScienceHouse/counters/internal/logging"
Expand All @@ -12,38 +14,42 @@ import (
"github.com/sirupsen/logrus"
)

func GetCounter(c *gin.Context) {
user, err := users.GetCSHAuth(c)
func GetCounters(c *gin.Context) {
_, err := users.GetCSHAuth(c)
if err != nil {
c.Status(http.StatusUnauthorized)
return
}
idToUse := c.Param("id")

counter, err := database.GetCounterFromId(c.Request.Context(), idToUse)
if err != nil {
ctx, cancel := context.WithTimeout(c.Request.Context(), time.Second*15)
defer cancel()

var req models.GetCountersInput

if err := c.ShouldBindJSON(&req); err != nil {
logging.Logger.WithFields(logrus.Fields{"module": "counter", "error": err}).Warning("Error binding req")
c.Status(http.StatusBadRequest)
return
}

resets, err := database.GetResetsFromCounterId(c.Request.Context(), idToUse)
counters, err := database.GetCounters(ctx, &req.Filter)
if err != nil {
logging.Logger.WithFields(logrus.Fields{"module": "counter", "error": err}).Warning("error getting counters!")
c.Status(http.StatusBadRequest)
return
}

c.HTML(200, "counter.tmpl", gin.H{
"Id": counter.CounterID,
"CounterID": counter.CounterOwner,
"Title": counter.CounterTitle,
"Description": counter.CounterDescription,
"Timestamp": counter.ResetOccuredAt,
"History": resets,
"Username": user.Username,
"FullName": user.FullName,
"UserID": user.Uuid,
"EBoard": users.IsEboard(user),
"RTP": users.IsActiveRTP(user),
var hasMore bool = false
var timestamp int64 = 0
if len(counters) > 1 {
hasMore = (len(counters) > (database.MAX_COUNTERS_IN_QUERY)-1)
timestamp = counters[len(counters)-1].ResetTimestamp
}

c.JSON(http.StatusOK, models.GetCountersOutput{
Counters: counters,
HasMore: hasMore,
Timestamp: &timestamp,
})
}

Expand All @@ -55,14 +61,17 @@ func DeleteCounter(c *gin.Context) {
return
}

ctx, cancel := context.WithTimeout(c.Request.Context(), time.Second*15)
defer cancel()

var req models.DeleteCounterInput
if err := c.ShouldBindJSON(&req); err != nil {
logging.Logger.WithFields(logrus.Fields{"module": "counter", "error": err}).Warning("Error casting request")
c.Status(http.StatusBadRequest)
return
}

counterOwner, err := database.GetCounterOwner(c.Request.Context(), req.RowID)
counterOwner, err := database.GetCounterOwner(ctx, req.RowID)
if err != nil {
logging.Logger.WithFields(logrus.Fields{"module": "counter", "error": err}).Warning("Error getting counter owner")
c.Status(http.StatusBadRequest)
Expand All @@ -76,11 +85,12 @@ func DeleteCounter(c *gin.Context) {
return
}

if err := database.DeleteCounter(c.Request.Context(), req.RowID); err != nil {
if err := database.DeleteCounter(ctx, req.RowID); err != nil {
logging.Logger.WithFields(logrus.Fields{"module": "counter", "error": err}).Warning("Error deleting counter")
c.Status(http.StatusInternalServerError)
return
}

c.Status(http.StatusNoContent)
}

Expand All @@ -91,6 +101,9 @@ func CreateCounter(c *gin.Context) {
return
}

ctx, cancel := context.WithTimeout(c.Request.Context(), time.Second*15)
defer cancel()

var req models.CreateCounterWithResetInput

if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil {
Expand All @@ -105,7 +118,7 @@ func CreateCounter(c *gin.Context) {
return
}

counterID, err := database.CreateCounterWithReset(c.Request.Context(), user.Uuid, user.Username, &req.Counter, &req.Reset)
counterID, err := database.CreateCounterWithReset(ctx, user.Uuid, user.Username, &req.Counter, &req.Reset)
if err != nil {
logging.Logger.WithFields(logrus.Fields{"module": "counters", "error": err}).Warning("Error creating new database")
c.Status(http.StatusBadRequest)
Expand All @@ -121,4 +134,6 @@ func Routes(r *gin.RouterGroup) {
cGroup.GET("/:id")
cGroup.POST("", CreateCounter)
cGroup.DELETE("", DeleteCounter)

cGroup.Handle("QUERY", "", GetCounters)
}
Loading
Loading