Skip to content

Commit

Permalink
Add QR code scan event
Browse files Browse the repository at this point in the history
  • Loading branch information
violog committed Jul 2, 2024
1 parent fd74da0 commit 122e16b
Show file tree
Hide file tree
Showing 23 changed files with 366 additions and 14 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2024 Zero Block Global Foundation

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
9 changes: 9 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ event_types:
short_description: Short description
no_auto_open: true
auto_claim: true
- name: meetup_participation
title: Prove your participation by scanning QR code
reward: 5
frequency: unlimited
description: Prove your participation by scanning QR code
short_description: Short description
no_auto_open: true
auto_claim: true
qr_code_value: "qr_code_base64_string"

levels:
levels:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
allOf:
- $ref: '#/components/schemas/PassportEventStateKey'
- $ref: '#/components/schemas/EventClaimingStateKey'
- type: object
required:
- attributes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ properties:
pattern: '^0x[0-9a-fA-F]{64}$'
type:
type: string
enum: [ passport_event_state ]
enum: [ event_claiming_state ]
4 changes: 4 additions & 0 deletions docs/spec/components/schemas/EventStaticMeta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@ properties:
- not_started
- expired
- disabled
qr_code_value:
type: string
description: Base64-encoded QR code. Must match the code provided in event type.
example: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABaElEQVR4AWP4//8/AyUYw"
16 changes: 16 additions & 0 deletions docs/spec/components/schemas/FulfillQREvent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
allOf:
- $ref: '#/components/schemas/FulfillQREventKey'
- type: object
x-go-is-request: true
required:
- attributes
properties:
attributes:
required:
- qr_code
type: object
properties:
qr_code:
type: string
description: Base64-encoded QR code
example: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABaElEQVR4AWP4//8/AyUYw"
12 changes: 12 additions & 0 deletions docs/spec/components/schemas/FulfillQREventKey.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type: object
required:
- id
- type
properties:
id:
type: string
description: Event ID
example: "059c81dd-2a54-44a8-8142-c15ad8f88949"
type:
type: string
enum: [ fulfill_qr_event ]
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ post:
- data
properties:
data:
$ref: '#/components/schemas/PassportEventState'
$ref: '#/components/schemas/EventClaimingState'
400:
$ref: '#/components/responses/invalidParameter'
401:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ post:
- data
properties:
data:
$ref: '#/components/schemas/PassportEventState'
$ref: '#/components/schemas/EventClaimingState'
400:
$ref: '#/components/responses/invalidParameter'
401:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
patch:
tags:
- Events
summary: Fulfill QR code event
description: Fulfill QR code event
operationId: fulfillQREvent
parameters:
- in: path
name: 'id'
required: true
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:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/ClaimEventKey'
responses:
200:
description: Success
content:
application/vnd.api+json:
schema:
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/EventClaimingState'
400:
$ref: '#/components/responses/invalidParameter'
401:
$ref: '#/components/responses/invalidAuth'
403:
description: This event type was disabled and cannot be fulfilled
content:
application/vnd.api+json:
schema:
$ref: '#/components/schemas/Errors'
404:
$ref: '#/components/responses/notFound'
500:
$ref: '#/components/responses/internalError'
7 changes: 7 additions & 0 deletions internal/data/evtypes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type EventConfig struct {
Disabled bool `fig:"disabled"`
ActionURL *url.URL `fig:"action_url"`
Logo *url.URL `fig:"logo"`
QRCodeValue string `fig:"qr_code_value"`
}

func (e EventConfig) Flag() string {
Expand All @@ -73,6 +74,11 @@ func (e EventConfig) Resource() resources.EventStaticMeta {
return &s
}

var qrValue *string
if e.QRCodeValue != "" {
qrValue = &e.QRCodeValue
}

return resources.EventStaticMeta{
Name: e.Name,
Description: e.Description,
Expand All @@ -85,6 +91,7 @@ func (e EventConfig) Resource() resources.EventStaticMeta {
ActionUrl: safeConv(e.ActionURL),
Logo: safeConv(e.Logo),
Flag: e.Flag(),
QrCodeValue: qrValue,
}
}

Expand Down
94 changes: 94 additions & 0 deletions internal/service/handlers/fulfill_qr_event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package handlers

import (
"net/http"

"github.com/rarimo/decentralized-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/service/requests"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

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

event, err := EventsQ(r).FilterByID(req.Data.ID).FilterByStatus(data.EventOpen).Get()
if err != nil {
Log(r).WithError(err).Error("Failed to get event by ID")
ape.RenderErr(w, problems.InternalError())
return
}
if event == nil {
Log(r).Debugf("Event not found for id=%s status=%s", req.Data.ID, data.EventOpen)
ape.RenderErr(w, problems.NotFound())
return
}

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

evType := EventTypes(r).Get(event.Type, evtypes.FilterInactive)
if evType == nil {
Log(r).Infof("Event type %s is inactive", event.Type)
ape.RenderErr(w, problems.Forbidden())
return
}
if evType.QRCodeValue != req.Data.Attributes.QrCode {
Log(r).Debugf("QR code for event %s doesn't match: got %s, want %s", event.Type, req.Data.Attributes.QrCode, evType.QRCodeValue)
ape.RenderErr(w, problems.Forbidden())
return
}

balance, err := BalancesQ(r).FilterByNullifier(event.Nullifier).FilterDisabled().Get()
if err != nil {
Log(r).WithError(err).Error("Failed to get balance by nullifier")
ape.RenderErr(w, problems.InternalError())
return
}
if balance == nil {
Log(r).Infof("Balance nullifier=%s is disabled", event.Nullifier)
ape.RenderErr(w, problems.Forbidden())
return
}

if !evType.AutoClaim {
_, err = EventsQ(r).FilterByID(event.ID).Update(data.EventFulfilled, nil, nil)
if err != nil {
Log(r).WithError(err).Error("Failed to update event status")
ape.RenderErr(w, problems.InternalError())
return
}

ape.Render(w, newEventClaimingStateResponse(balance.Nullifier, false))
return
}

err = EventsQ(r).Transaction(func() error {
event, err = claimEvent(r, event, balance)
return err
})
if err != nil {
Log(r).WithError(err).Errorf("Failed to claim event %s and accrue %d points to the balance %s",
event.ID, evType.Reward, event.Nullifier)
ape.RenderErr(w, problems.InternalError())
return
}

// balance should exist cause of previous logic
balance, err = BalancesQ(r).GetWithRank(event.Nullifier)
if err != nil {
Log(r).WithError(err).Error("Failed to get balance by nullifier with rank")
ape.RenderErr(w, problems.InternalError())
return
}

ape.Render(w, newClaimEventResponse(*event, evType.Resource(), *balance))
}
10 changes: 5 additions & 5 deletions internal/service/handlers/verify_passport.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func VerifyPassport(w http.ResponseWriter, r *http.Request) {
return
}

ape.Render(w, newPassportEventStateResponse(req.Data.ID, nil))
ape.Render(w, newEventClaimingStateResponse(req.Data.ID, true))
return
}

Expand All @@ -145,14 +145,14 @@ func VerifyPassport(w http.ResponseWriter, r *http.Request) {
return
}

ape.Render(w, newPassportEventStateResponse(req.Data.ID, event))
ape.Render(w, newEventClaimingStateResponse(req.Data.ID, event != nil))
}

func newPassportEventStateResponse(id string, event *data.Event) resources.PassportEventStateResponse {
func newEventClaimingStateResponse(id string, isClaimed bool) resources.PassportEventStateResponse {
var res resources.PassportEventStateResponse
res.Data.ID = id
res.Data.Type = resources.PASSPORT_EVENT_STATE
res.Data.Attributes.Claimed = event != nil
res.Data.Type = resources.EVENT_CLAIMING_STATE
res.Data.Attributes.Claimed = isClaimed
return res
}

Expand Down
25 changes: 25 additions & 0 deletions internal/service/requests/fulfill_qr_event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package requests

import (
"encoding/json"
"net/http"

"github.com/go-chi/chi"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
"github.com/rarimo/geo-points-svc/resources"
)

func NewFulfillQREvent(r *http.Request) (req resources.FulfillQrEventRequest, err error) {
id := chi.URLParam(r, "id")
if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
err = newDecodeError("body", err)
return
}

return req, validation.Errors{
"data/id": validation.Validate(req.Data.ID, validation.Required, validation.In(id)),
"data/type": validation.Validate(req.Data.Type, validation.Required, validation.In(resources.FULFILL_QR_EVENT)),
"data/attributes/qr_code": validation.Validate(req.Data.Attributes.QrCode, validation.Required, is.Base64),
}.Filter()
}
1 change: 1 addition & 0 deletions internal/service/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func Run(ctx context.Context, cfg config.Config) {
r.Use(handlers.AuthMiddleware(cfg.Auth(), cfg.Log()))
r.Get("/", handlers.ListEvents)
r.Get("/{id}", handlers.GetEvent)
r.Patch("/{id}/qrcode", handlers.FulfillQREvent)
r.Patch("/{id}", handlers.ClaimEvent)
})
r.Get("/balances", handlers.Leaderboard)
Expand Down
5 changes: 1 addition & 4 deletions internal/service/workers/nooneisforgotten/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,7 @@ func updateReferralUserEvents(db *pgdb.DB, types evtypes.Types) error {
// friends which have passport scanned, if it possible
func claimReferralSpecificEvents(db *pgdb.DB, types evtypes.Types, levels config.Levels) error {
evType := types.Get(evtypes.TypeReferralSpecific, evtypes.FilterInactive)
if evType == nil {
return nil
}
if !evType.AutoClaim {
if evType == nil || !evType.AutoClaim {
return nil
}

Expand Down
Loading

0 comments on commit 122e16b

Please sign in to comment.