Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: delete disabled events #19

Merged
merged 3 commits into from
Jul 15, 2024
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
4 changes: 3 additions & 1 deletion internal/data/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ type EventsQ interface {
FilterByStatus(...EventStatus) EventsQ
FilterByType(...string) EventsQ
FilterByNotType(types ...string) EventsQ
FilterByUpdatedAtBefore(int64) EventsQ
FilterByExternalID(string) EventsQ
FilterInactiveNotClaimed(types ...string) EventsQ
// FilterByUpdatedAtBefore must be only used with SelectReopenable, because it
// depends on table alias.
FilterByUpdatedAtBefore(int64) EventsQ
}
12 changes: 8 additions & 4 deletions internal/data/pg/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func NewEvents(db *pgdb.DB) data.EventsQ {
updater: squirrel.Update(eventsTable),
deleter: squirrel.Delete(eventsTable),
counter: squirrel.Select("COUNT(*) AS count").From(eventsTable),
reopenable: squirrel.Select("nullifier", "type").Distinct().From(eventsTable + " e1"),
reopenable: squirrel.Select("e1.nullifier", "e1.type").Distinct().From(eventsTable + " e1"),
}
}

Expand Down Expand Up @@ -146,7 +146,10 @@ func (q *events) SelectReopenable() ([]data.ReopenableEvent, error) {
WHERE e2.nullifier = e1.nullifier
AND e2.type = e1.type
AND e2.status IN (?, ?))`, eventsTable)
stmt := q.reopenable.Where(subq, data.EventOpen, data.EventFulfilled)
stmt := q.reopenable.
Where(subq, data.EventOpen, data.EventFulfilled).
Join(balancesTable + " b ON b.nullifier = e1.nullifier").
Where("b.referred_by IS NOT NULL")

var res []data.ReopenableEvent
if err := q.db.Select(&res, stmt); err != nil {
Expand All @@ -168,7 +171,7 @@ func (q *events) SelectAbsentTypes(allTypes ...string) ([]data.ReopenableEvent,
)
SELECT u.nullifier, t.type
FROM (
SELECT nullifier FROM %s
SELECT nullifier FROM %s WHERE referred_by IS NOT NULL
) u
CROSS JOIN types t
LEFT JOIN %s e ON e.nullifier = u.nullifier AND e.type = t.type
Expand Down Expand Up @@ -220,7 +223,8 @@ func (q *events) FilterByExternalID(id string) data.EventsQ {
}

func (q *events) FilterByUpdatedAtBefore(unix int64) data.EventsQ {
return q.applyCondition(squirrel.Lt{"updated_at": unix})
q.reopenable = q.reopenable.Where(squirrel.Lt{"e1.updated_at": unix})
return q
}

func (q *events) FilterInactiveNotClaimed(types ...string) data.EventsQ {
Expand Down
40 changes: 25 additions & 15 deletions internal/service/handlers/create_event_type.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package handlers

import (
"fmt"
"net/http"

"github.com/rarimo/geo-auth-svc/pkg/auth"
Expand Down Expand Up @@ -39,37 +40,46 @@ func CreateEventType(w http.ResponseWriter, r *http.Request) {
}

typeModel := models.ResourceToModel(req.Data.Attributes)
if err = EventTypesQ(r).Insert(typeModel); err != nil {
Log(r).WithError(err).Error("Failed to insert event type")
err = EventsQ(r).Transaction(func() error {
if err = EventTypesQ(r).Insert(typeModel); err != nil {
return fmt.Errorf("insert event type: %w", err)
}
EventTypes(r).Push(typeModel)

// TODO: add cron jobs for limited events and other special logic when updating other fields is supported
if evtypes.FilterNotOpenable(typeModel) {
return nil
}
return openQREvents(r, typeModel)
})

if err != nil {
Log(r).WithError(err).Error("Failed to add event type and open events")
ape.RenderErr(w, problems.InternalError())
return
}
EventTypes(r).Push(typeModel)

if evtypes.FilterNotOpenable(typeModel) {
w.WriteHeader(http.StatusNoContent)
return
}
w.WriteHeader(http.StatusNoContent)
}

func openQREvents(r *http.Request, evType models.EventType) error {
balances, err := BalancesQ(r).FilterDisabled().Select()
if err != nil {
Log(r).WithError(err).Error("Failed to select balances")
ape.RenderErr(w, problems.InternalError())
return
return fmt.Errorf("select balances: %w", err)
}

eventsToInsert := make([]data.Event, 0, len(balances))
for _, b := range balances {
eventsToInsert = append(eventsToInsert, data.Event{
Nullifier: b.Nullifier,
Status: data.EventOpen,
Type: typeModel.Name,
Type: evType.Name,
})
}

if err = EventsQ(r).Insert(eventsToInsert...); err != nil {
Log(r).WithError(err).Error("Failed to insert qr-code events")
ape.RenderErr(w, problems.InternalError())
return
return fmt.Errorf("insert events: %w", err)
}
w.WriteHeader(http.StatusNoContent)

return nil
}
45 changes: 36 additions & 9 deletions internal/service/handlers/update_event_type.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package handlers

import (
"fmt"
"net/http"

"github.com/rarimo/geo-auth-svc/pkg/auth"
"github.com/rarimo/geo-points-svc/internal/data"
"github.com/rarimo/geo-points-svc/internal/data/evtypes/models"
"github.com/rarimo/geo-points-svc/internal/service/requests"
"github.com/rarimo/geo-points-svc/resources"
Expand Down Expand Up @@ -33,27 +35,52 @@ func UpdateEventType(w http.ResponseWriter, r *http.Request) {
}
if evType == nil {
Log(r).Debugf("Event type %s not found", req.Data.Attributes.Name)
ape.RenderErr(w, problems.Conflict())
ape.RenderErr(w, problems.NotFound())
return
}
}

typeModel := models.ResourceToModel(req.Data.Attributes)
res, err := EventTypesQ(r).FilterByNames(typeModel.Name).Update(typeModel.ForUpdate())

var updated []models.EventType
err = EventsQ(r).Transaction(func() error {
updated, err = EventTypesQ(r).FilterByNames(typeModel.Name).Update(typeModel.ForUpdate())
if err != nil {
return fmt.Errorf("update event type: %w", err)
}
if len(updated) != 1 {
return fmt.Errorf("critical: count of updated event types is %d, expected 1", len(updated))
}
// Currently, event cannot be 'not openable' in other ways,
// add extra checks more fields are supported.
if evType.Disabled == typeModel.Disabled {
return nil
}
// Open events if we have enabled the type, otherwise clean them up.
if !typeModel.Disabled {
return openQREvents(r, typeModel)
}

deleted, err := EventsQ(r).
FilterByType(typeModel.Name).
FilterByStatus(data.EventOpen, data.EventFulfilled).
Delete()
if err != nil {
return fmt.Errorf("delete disabled events: %w", err)
}

Log(r).Infof("Deleted %d events on disabling event type %s", deleted, typeModel.Name)
return nil
})

if err != nil {
Log(r).WithError(err).Error("Failed to update event type")
ape.RenderErr(w, problems.InternalError())
return
}

if len(res) == 0 {
Log(r).Error("Count of updated event_types = 0")
ape.RenderErr(w, problems.InternalError())
return
}

EventTypes(r).Push(typeModel)
resp := newEventTypeResponse(res[0], r.Header.Get(langHeader))
resp := newEventTypeResponse(updated[0], r.Header.Get(langHeader))
resp.Data.Attributes.QrCodeValue = typeModel.QRCodeValue
ape.Render(w, resp)
}
Expand Down
10 changes: 8 additions & 2 deletions internal/service/requests/create_event_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,23 @@ func NewCreateEventType(r *http.Request) (req resources.EventTypeResponse, err e
attr := req.Data.Attributes
return req, val.Errors{
// only QR code events can be currently created or updated
// localization is not supported currently
"data/id": val.Validate(req.Data.ID, val.Required),
"data/type": val.Validate(req.Data.Type, val.Required, val.In(resources.EVENT_TYPE)),
"data/attributes/action_url": val.Validate(attr.ActionUrl, is.URL),
"data/attributes/description": val.Validate(attr.Description, val.Required),
"data/attributes/frequency": val.Validate(attr.Frequency, val.Required, val.In(string(models.Unlimited))),
"data/attributes/logo": val.Validate(attr.Logo, is.URL),
"data/attributes/name": val.Validate(attr.Name, val.Required, val.In(req.Data.ID)),
"data/attributes/flag": val.Validate(attr.Flag, val.Empty),
"data/attributes/qr_code_value": val.Validate(attr.QrCodeValue, val.Required),
"data/attributes/qr_code_value": val.Validate(attr.QrCodeValue, val.Required, is.Base64),
"data/attributes/reward": val.Validate(attr.Reward, val.Required, val.Min(1)),
"data/attributes/short_description": val.Validate(attr.ShortDescription, val.Required),
"data/attributes/title": val.Validate(attr.Title, val.Required),
// these fields are not currently supported, because cron jobs implementation is required
"data/attributes/starts_at": val.Validate(attr.StartsAt, val.Empty),
"data/attributes/expires_at": val.Validate(attr.ExpiresAt, val.Empty),
// read-only fields due to reusing the same model
"data/attributes/flag": val.Validate(attr.Flag, val.Empty),
"data/attributes/usage_count": val.Validate(attr.UsageCount, val.Empty),
}.Filter()
}
8 changes: 8 additions & 0 deletions internal/service/requests/update_event_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,13 @@ func NewUpdateEventType(r *http.Request) (req resources.EventTypeResponse, err e
"data/attributes/frequency": val.Validate(attr.Frequency, val.In(string(models.Unlimited))),
"data/attributes/logo": val.Validate(attr.Logo, is.URL),
"data/attributes/reward": val.Validate(attr.Reward, val.Min(1)),
// not updatable, as QR code includes event type name
"data/attributes/qr_code_value": val.Validate(attr.QrCodeValue, val.Empty),
// these fields are not currently supported, because cron jobs implementation is required
"data/attributes/starts_at": val.Validate(attr.StartsAt, val.Empty),
"data/attributes/expires_at": val.Validate(attr.ExpiresAt, val.Empty),
// read-only fields due to reusing the same model
"data/attributes/flag": val.Validate(attr.Flag, val.Empty),
"data/attributes/usage_count": val.Validate(attr.UsageCount, val.Empty),
}.Filter()
}
Loading