Skip to content

Commit

Permalink
Merge pull request #67 from flow-hydraulics/latenssi/clean-up
Browse files Browse the repository at this point in the history
Add documentation, clean up
  • Loading branch information
latenssi authored Oct 4, 2021
2 parents 0e04489 + fdb9d3d commit 06d09a3
Show file tree
Hide file tree
Showing 26 changed files with 303 additions and 232 deletions.
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,69 @@
Deployment
# Flow Pack Distribution Service

## Running the PDS backend service

Docker

cp env.example .env # edit according to your setup
docker run -p 3000:3000 --env-file .env ghcr.io/flow-hydraulics/flow-pds:latest

Dev environment

cp env.example .env
cp env.example .env.test

# If docker-compose installed
# Needs docker-compose installed
make dev

Test
## Testing

cp env.example .env.test

# Standalone (can NOT have emulator running in docker)
./tests-with-emulator.sh

# With docker-compose environment ("make dev" above)
go test -v


## Project layout

Backend service code: `./service`

Contract code (test, deploy): `./go-contracts`

API spec:
- `./models`
- `./reference`

Simple API tests: `./api-scripts`

Cadence source code:
- `./cadence-contracts`
- `./cadence-scripts`
- `./cadence-transactions`

## Configuration

### Database

| Config variable | Environment variable | Description | Default | Examples |
| --------------- | :-------------------------- | ------------------------------------------------------------------------------------------------ | ----------- | ------------------------- |
| DatabaseType | `FLOW_PDS_DATABASE_DSN` | Type of database driver | `sqlite` | `sqlite`, `psql`, `mysql` |
| DatabaseDSN | `FLOW_PDS_DATABASE_TYPE` | Data source name ([DSN](https://en.wikipedia.org/wiki/Data_source_name)) for database connection | `pds.db` | See below |

Examples of Database DSN

mysql://john:pass@localhost:3306/my_db

postgresql://postgres:postgres@localhost:5432/postgres

user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local

host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai

For more: https://gorm.io/docs/connecting_to_the_database.html


### All possible configuration variables

Refer to [service/config/config.go](service/config/config.go) for details and documentation.
File renamed without changes.
21 changes: 12 additions & 9 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ package main
import (
"flag"
"fmt"
"log"

"os"

"github.com/flow-hydraulics/flow-pds/service/app"
"github.com/flow-hydraulics/flow-pds/service/common"
"github.com/flow-hydraulics/flow-pds/service/config"
"github.com/flow-hydraulics/flow-pds/service/errors"
"github.com/flow-hydraulics/flow-pds/service/http"
"github.com/flow-hydraulics/flow-pds/service/transactions"
"github.com/onflow/flow-go-sdk/client"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
)

Expand All @@ -23,6 +23,10 @@ var (
buildTime string // when the executable was built
)

func init() {
log.SetLevel(log.InfoLevel)
}

func main() {
var (
printVersion bool
Expand Down Expand Up @@ -58,13 +62,12 @@ func main() {

func runServer(cfg *config.Config) error {
if cfg == nil {
return &errors.NilConfigError{}
return fmt.Errorf("config not provided")
}

// Application wide loggers
logServer := log.New(os.Stdout, "[SERVER] ", log.LstdFlags|log.Lshortfile)
logger := log.New()

logServer.Printf("Starting server (v%s)...\n", version)
logger.Printf("Starting server (v%s)...\n", version)

// Flow client
// TODO: WithInsecure()?
Expand All @@ -74,7 +77,7 @@ func runServer(cfg *config.Config) error {
}
defer func() {
if err := flowClient.Close(); err != nil {
logServer.Println(err)
logger.Println(err)
}
}()

Expand All @@ -94,11 +97,11 @@ func runServer(cfg *config.Config) error {
}

// Application
app := app.New(cfg, db, flowClient, true)
app := app.New(cfg, logger, db, flowClient, true)
defer app.Close()

// HTTP server
server := http.NewServer(cfg, logServer, app)
server := http.NewServer(cfg, logger, app)

server.ListenAndServe()

Expand Down
21 changes: 18 additions & 3 deletions service/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,28 @@ import (
"github.com/flow-hydraulics/flow-pds/service/config"
"github.com/google/uuid"
"github.com/onflow/flow-go-sdk/client"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
)

// App handles all the application logic and interfaces directly with the database
type App struct {
cfg *config.Config
logger *log.Logger
db *gorm.DB
flowClient *client.Client
contract *Contract
quit chan bool // Chan type does not matter as we only use this to 'close'
}

func New(cfg *config.Config, db *gorm.DB, flowClient *client.Client, poll bool) *App {
contract := NewContract(cfg, flowClient)
func New(cfg *config.Config, logger *log.Logger, db *gorm.DB, flowClient *client.Client, poll bool) *App {
if logger == nil {
panic("no logger")
}

contract := NewContract(cfg, logger, flowClient)
quit := make(chan bool)
app := &App{cfg, db, flowClient, contract, quit}
app := &App{cfg, logger, db, flowClient, contract, quit}

if poll {
go poller(app)
Expand All @@ -29,10 +36,12 @@ func New(cfg *config.Config, db *gorm.DB, flowClient *client.Client, poll bool)
return app
}

// Closes allows the poller to close controllably
func (app *App) Close() {
close(app.quit)
}

// CreateDistribution validates a distribution, resolves it and stores it in database
func (app *App) CreateDistribution(ctx context.Context, distribution *Distribution) error {
if err := distribution.Validate(); err != nil {
return err
Expand All @@ -49,12 +58,15 @@ func (app *App) CreateDistribution(ctx context.Context, distribution *Distributi
return nil
}

// ListDistributions lists all distributions in the database. Uses 'limit' and 'offset' to
// limit the fetched slice size.
func (app *App) ListDistributions(ctx context.Context, limit, offset int) ([]Distribution, error) {
opt := ParseListOptions(limit, offset)

return ListDistributions(app.db, opt)
}

// GetDistribution returns a distribution from database based on its offchain ID (uuid).
func (app *App) GetDistribution(ctx context.Context, id uuid.UUID) (*Distribution, error) {
distribution, err := GetDistribution(app.db, id)
if err != nil {
Expand All @@ -64,6 +76,8 @@ func (app *App) GetDistribution(ctx context.Context, id uuid.UUID) (*Distributio
return distribution, nil
}

// CancelDistribution cancels a distribution. Spec and implementation for
// distribution cancelling is not finished yet.
func (app *App) CancelDistribution(ctx context.Context, id uuid.UUID) error {
return app.db.Transaction(func(tx *gorm.DB) error {
distribution, err := GetDistribution(tx, id)
Expand All @@ -79,6 +93,7 @@ func (app *App) CancelDistribution(ctx context.Context, id uuid.UUID) error {
})
}

// GetPack returns a pack from database based on its offchain ID (uuid).
func (app *App) GetPack(ctx context.Context, id uuid.UUID) (*Pack, error) {
pack, err := GetPack(app.db, id)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion service/app/circulating_pack.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
"gorm.io/gorm"
)

// CirculatingPackContract represents the contract of a pack NFT that has been put into circulation.
// CirculatingPackContract represents the contract of a pack NFT that has been
// put into circulation.
// We need to monitor each circulating packs events.
type CirculatingPackContract struct {
gorm.Model
Expand Down
10 changes: 9 additions & 1 deletion service/app/collectible.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import (
"github.com/onflow/flow-go-sdk"
)

// Collectible is a reference to an NFT which can be included in a pack.
type Collectible struct {
FlowID common.FlowID // ID of the collectible NFT
FlowID common.FlowID // Flow ID of the collectible NFT
ContractReference AddressLocation // Reference to the collectible NFT contract
}

// Collectibles slice type. Allows storing collectibles of a pack
// embedded (as a text column of 'distribution_packs' table) in database.
type Collectibles []Collectible

// CollectibleFromString returns a collectible from the string representation.
func CollectibleFromString(s string) (Collectible, error) {
split := strings.Split(string(s), ".")
address := common.FlowAddress(flow.HexToAddress(split[1]))
Expand All @@ -37,6 +41,8 @@ func (c Collectible) String() string {
return fmt.Sprintf("A.%s.%s.%d", c.ContractReference.Address, c.ContractReference.Name, c.FlowID.Int64)
}

// HashString returns the string representation of a collectible used to
// construct a packs commitmentHash.
func (c Collectible) HashString() string {
return c.String()
}
Expand All @@ -50,6 +56,7 @@ func (Collectibles) GormDataType() string {
return "text"
}

// Scan a collectibles slice from database.
func (cc *Collectibles) Scan(value interface{}) error {
str, ok := value.(string)
if !ok {
Expand All @@ -68,6 +75,7 @@ func (cc *Collectibles) Scan(value interface{}) error {
return nil
}

// Convert a collectibles slice to database storable format.
func (cc Collectibles) Value() (driver.Value, error) {
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(cc)), ","), "[]"), nil
}
Loading

0 comments on commit 06d09a3

Please sign in to comment.