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 list events #12

Merged
merged 4 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions docs/spec/components/schemas/Balance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ allOf:
description: Referral codes. Returned only for the single user.
items:
$ref: '#/components/schemas/ReferralCode'
referred_users_count:
type: integer
format: int
description: Number of invited users
example: 13
level:
type: integer
format: int
Expand Down
5 changes: 5 additions & 0 deletions docs/spec/components/schemas/EventStaticMeta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,8 @@ properties:
type: string
description: Base64-encoded QR code. Must match the code provided in event type.
example: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABaElEQVR4AWP4//8/AyUYw"
usage_count:
type: integer
format: int
description: Number of uses. Only available to the administrator.
example: 1002
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
get:
tags:
- Event types
summary: List QR event types
description: |
Returns configuration of all event types with QR-code.
Basically, it is event static metadata (model `EventStaticMeta`)
for each event type in the system.
Requires **admin** role in JWT.
operationId: getQREventTypes
parameters:
- in: query
name: 'count'
description: Іpecifies whether to return the number of uses of the event
required: false
schema:
type: bool
example: true
- in: query
name: 'filter[name]'
description: Filter by type name. Possible values should be hard-coded in the client.
required: false
schema:
type: array
items:
type: string
example: "passport_scan"
- in: query
name: 'filter[name][not]'
description: |
Inverted filter by type name: excludes provided values
required: false
schema:
type: array
items:
type: string
example: "referral_specific"
- in: query
name: 'filter[flag]'
description: Filter by configuration flags. Values are disjunctive (OR).
required: false
schema:
type: array
items:
type: string
enum:
- active
- not_started
- expired
- disabled
responses:
200:
description: Success
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
type: array
items:
$ref: '#/components/schemas/EventType'
500:
$ref: '#/components/responses/internalError'

post:
tags:
- Event types
summary: Create event type
description: |
Creates a new event type. Requires **admin** role in JWT.
The type must not be present in the system.
operationId: createEventType
requestBody:
required: true
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/EventType'
responses:
204:
description: No content
400:
$ref: '#/components/responses/invalidParameter'
401:
$ref: '#/components/responses/invalidAuth'
409:
description: Event type already exists
content:
application/vnd.api+json:
schema:
$ref: '#/components/schemas/Errors'
500:
$ref: '#/components/responses/internalError'
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@ patch:
schema:
type: string
example: "059c81dd-2a54-44a8-8142-c15ad8f88949"
- in: header
name: Signature
description: Signature of the request
required: true
schema:
type: string
pattern: '^[a-f0-9]{64}$'
requestBody:
required: true
content:
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2118,10 +2118,6 @@ github.com/rarimo/geo-auth-svc v0.2.0 h1:yQvcIBNx+Tc1jJdtpWDfyLc0HogU+okA08HEZ55
github.com/rarimo/geo-auth-svc v0.2.0/go.mod h1:SB4bo1xHYDAsBaQGX2+FoEgD3xxqYmcgr4XTTjy4/OM=
github.com/rarimo/saver-grpc-lib v1.0.0 h1:MGUVjYg7unmodYczVsLqlqZNkT4CIgKqdo6aQtL1qdE=
github.com/rarimo/saver-grpc-lib v1.0.0/go.mod h1:DpugWK5B7Hi0bdC3MPe/9FD2zCxaRwsyykdwxtF1Zgg=
github.com/rarimo/zkverifier-kit v1.0.0 h1:zMW85hyDP3Uk6p9Dk9U4TBzOf0Pry+RNlWpli1tUZ1Q=
github.com/rarimo/zkverifier-kit v1.0.0/go.mod h1:3YDg5dTkDRr4IdfaDHGYetopd6gS/2SuwSeseYTWwNw=
github.com/rarimo/zkverifier-kit v1.1.0-rc.0 h1:5JkObPkEUGwgq4SKJAGInaTBDBILQUHMP4VKZuYPcsM=
github.com/rarimo/zkverifier-kit v1.1.0-rc.0/go.mod h1:3YDg5dTkDRr4IdfaDHGYetopd6gS/2SuwSeseYTWwNw=
github.com/rarimo/zkverifier-kit v1.1.0-rc.1 h1:xtmrFEl7eLAE6mi7IQYOOMKFdwXC3gbe39fYQdvKVZg=
github.com/rarimo/zkverifier-kit v1.1.0-rc.1/go.mod h1:3YDg5dTkDRr4IdfaDHGYetopd6gS/2SuwSeseYTWwNw=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
Expand Down
12 changes: 10 additions & 2 deletions internal/data/evtypes/models/event_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ func (e EventType) Flag() string {
}
}

func (e EventType) Resource() resources.EventStaticMeta {
return resources.EventStaticMeta{
func (e EventType) Resource(qr bool) resources.EventStaticMeta {

res := resources.EventStaticMeta{
Name: e.Name,
Description: e.Description,
ShortDescription: e.ShortDescription,
Expand All @@ -68,9 +69,16 @@ func (e EventType) Resource() resources.EventStaticMeta {
ExpiresAt: e.ExpiresAt,
AutoClaim: e.AutoClaim,
ActionUrl: e.ActionURL,
Disabled: e.Disabled,
Logo: e.Logo,
Flag: e.Flag(),
}

if qr {
res.QrCodeValue = e.QRCodeValue
}

return res
}

func (e EventType) ForUpdate() map[string]any {
Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/claim_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func ClaimEvent(w http.ResponseWriter, r *http.Request) {
return
}

ape.Render(w, newClaimEventResponse(*event, evType.Resource(), *balance))
ape.Render(w, newClaimEventResponse(*event, evType.Resource(false), *balance))
}

// claimEvent requires event to exist
Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/create_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func CreateBalance(w http.ResponseWriter, r *http.Request) {
return
}

ape.Render(w, newBalanceResponse(*balance, referrals))
ape.Render(w, newBalanceResponse(*balance, referrals, 0))
}

func prepareEventsWithRef(nullifier, refBy string, isGenesisRef bool, r *http.Request) []data.Event {
Expand Down
15 changes: 0 additions & 15 deletions internal/service/handlers/fulfill_qr_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package handlers
import (
"net/http"

"github.com/labstack/gommon/log"
"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"
Expand Down Expand Up @@ -36,20 +35,6 @@ func FulfillQREvent(w http.ResponseWriter, r *http.Request) {
return
}

gotSig := r.Header.Get("Signature")
wantSig, err := SigCalculator(r).QREventSignature(event.Nullifier, event.ID, req.Data.Attributes.QrCode)
if err != nil { // must never happen due to preceding validation
Log(r).WithError(err).Error("Failed to calculate HMAC signature")
ape.RenderErr(w, problems.InternalError())
return
}

if gotSig != wantSig {
log.Warnf("QR event fulfillment unauthorized access: HMAC signature mismatch: got %s, want %s", gotSig, wantSig)
ape.RenderErr(w, problems.Forbidden())
return
}

evType := EventTypes(r).Get(event.Type, evtypes.FilterInactive)
if evType == nil {
Log(r).Infof("Event type %s is inactive", event.Type)
Expand Down
23 changes: 21 additions & 2 deletions internal/service/handlers/get_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func GetBalance(w http.ResponseWriter, r *http.Request) {
}

var referrals []data.Referral
var referredUsers int
if req.ReferralCodes {
referrals, err = ReferralsQ(r).
FilterByNullifier(req.Nullifier).
Expand All @@ -52,9 +53,26 @@ func GetBalance(w http.ResponseWriter, r *http.Request) {
ape.RenderErr(w, problems.InternalError())
return
}

// Infinite referral codes initially have 0 uses and,
// accordingly, after use, this value will decrease,
// i.e. the number of invited users for this code will
// be an absolute value
//
// A one-time code is considered used if it has 0 uses,
// because the initial value is 1
for _, ref := range referrals {
if ref.Infinity {
referredUsers += -int(ref.UsageLeft)
continue
}
if ref.UsageLeft == 0 {
referredUsers++
}
}
}

ape.Render(w, newBalanceResponse(*balance, referrals))
ape.Render(w, newBalanceResponse(*balance, referrals, referredUsers))
}

// newBalanceModel forms a balance response without referral fields, which must
Expand All @@ -75,12 +93,13 @@ func newBalanceModel(balance data.Balance) resources.Balance {
}
}

func newBalanceResponse(balance data.Balance, referrals []data.Referral) resources.BalanceResponse {
func newBalanceResponse(balance data.Balance, referrals []data.Referral, referredUsers int) resources.BalanceResponse {
resp := resources.BalanceResponse{Data: newBalanceModel(balance)}
boolP := func(b bool) *bool { return &b }

resp.Data.Attributes.IsDisabled = boolP(balance.ReferredBy == nil)
resp.Data.Attributes.IsVerified = boolP(balance.IsVerified)
resp.Data.Attributes.ReferredUsersCount = &referredUsers

if len(referrals) == 0 {
return resp
Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/get_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@ func GetEvent(w http.ResponseWriter, r *http.Request) {
return
}

ape.Render(w, resources.EventResponse{Data: newEventModel(*event, evType.Resource())})
ape.Render(w, resources.EventResponse{Data: newEventModel(*event, evType.Resource(false))})
}
2 changes: 1 addition & 1 deletion internal/service/handlers/get_event_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ func GetEventType(w http.ResponseWriter, r *http.Request) {
return
}

ape.Render(w, newEventTypeResponse(*evType))
ape.Render(w, newEventTypeResponse(*evType, false))
}
2 changes: 1 addition & 1 deletion internal/service/handlers/list_event_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func ListEventTypes(w http.ResponseWriter, r *http.Request) {
ID: t.Name,
Type: resources.EVENT_TYPE,
},
Attributes: t.Resource(),
Attributes: t.Resource(false),
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/list_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func getOrderedEventsMeta(events []data.Event, r *http.Request) ([]resources.Eve
if evType == nil {
return nil, errors.New("wrong event type is stored in DB: might be bad event config")
}
res[i] = evType.Resource()
res[i] = evType.Resource(false)
}

return res, nil
Expand Down
60 changes: 60 additions & 0 deletions internal/service/handlers/list_qr_event_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package handlers

import (
"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"
"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"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

func ListQREventTypes(w http.ResponseWriter, r *http.Request) {
req, err := requests.NewListEventTypes(r)
if err != nil {
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

if !auth.Authenticates(UserClaims(r), auth.AdminGrant) {
ape.RenderErr(w, problems.Unauthorized())
return
}

types := EventTypes(r).List(
func(ev models.EventType) bool {
return ev.QRCodeValue == nil
},
evtypes.FilterByNames(req.FilterName...),
evtypes.FilterByFlags(req.FilterFlag...),
func(ev models.EventType) bool {
return len(req.FilterNotName) > 0 && !evtypes.FilterByNames(req.FilterNotName...)(ev)
},
)

resTypes := make([]resources.EventType, len(types))
for i, t := range types {
resTypes[i] = resources.EventType{
Key: resources.Key{
ID: t.Name,
Type: resources.EVENT_TYPE,
},
Attributes: t.Resource(true),
}
if req.Count {
evCount, err := EventsQ(r).FilterByType(t.Name).FilterByStatus(data.EventFulfilled, data.EventClaimed).Count()
if err != nil {
Log(r).WithError(err).Errorf("failed to get %s event usage count", t.Name)
ape.RenderErr(w, problems.InternalError())
return
}
resTypes[i].Attributes.UsageCount = &evCount
}
}

ape.Render(w, resources.EventTypeListResponse{Data: resTypes})
}
6 changes: 3 additions & 3 deletions internal/service/handlers/update_event_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ func UpdateEventType(w http.ResponseWriter, r *http.Request) {
}

EventTypes(r).Push(typeModel)
ape.Render(w, newEventTypeResponse(res[0]))
ape.Render(w, newEventTypeResponse(res[0], true))
}

func newEventTypeResponse(evType models.EventType) resources.EventTypeResponse {
func newEventTypeResponse(evType models.EventType, qr bool) resources.EventTypeResponse {
return resources.EventTypeResponse{
Data: resources.EventType{
Key: resources.Key{
ID: evType.Name,
Type: resources.EVENT_TYPE,
},
Attributes: evType.Resource(),
Attributes: evType.Resource(qr),
},
}
}
1 change: 1 addition & 0 deletions internal/service/requests/list_event_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type ListExpiredEvents struct {
FilterName []string `filter:"name"`
FilterFlag []string `filter:"flag"`
FilterNotName []string `url:"filter[name][not]"`
Count bool `url:"count"`
}

func NewListEventTypes(r *http.Request) (req ListExpiredEvents, err error) {
Expand Down
Loading
Loading