core

package module
v0.0.0-...-829e17e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 25, 2016 License: MIT Imports: 51 Imported by: 0

README

gopypi

Gopypi is private pypi repository implemented in go language. Gopypi can store your private packages and support pip install. Gopypi supports distutils/setuptools setup.py uploading of packages. And it does more thant this. Gopypi supports management of active users, maintainers of packages and lot more.

This project is under development and should NOT be used in production for now. I am working to implement all features and bring alpha version in short time. Stay tuned!

Features

If you install gopypi you will get out of the box following features:

  • auth module - maintain users with access permissions, maintainers access to packages
  • packages - uploading of packages, versions, files (directly from setup.py)
  • Licenses - list of licenses for uploaded packages
  • Clean admin interface - Simple but powerful modern SPA admin interface (written in vue.js)
  • Embedded static data - all static files are embedded directly into binary

Gopypi has some optional features that can be enabled in administration, such as:

  • download stats - stats about downloaded packages
  • automatically assign maintainers - reads information from python packages and assigns users

Admin

Gopypi has modern SPA admin interface where you can browse information about packages, maintain user credentials etc. Currently only admin can login into this administration, but that will change in near future, so all active users can log into admin and see information about packages they maintain.

Command line interface

gopypi is single binary which has multiple subcommands, you can see them by typing gopypi -h

Installation

The easiest way is to download precompiled binaries from github repository releases. Current version ccan be found here:

https://github.com/phonkee/gopypi/releases/tag/0.5.3

Otherwise you can type

go get github.com/phonkee/gopypi/...

After you have installed gopypi you need to create config file. You can use gopypi command makeconfig that helps you with generating new configuration interactively.

./gopypi makeconfig
Database

Gopypi currently supports postgres, mysql databases. To correctly setup database dsn refer to

Now it's time to run database migrations:

./gopypi migrate --config gopypi.conf

This will apply all database migrations automaticaly. For every new gopypi release migrate command is needed to be run.

After you have applied all database migrations it's time to create new admin user, so you can access admin interface:

./gopypi createadmin --config gopypi.conf

After you have succesfully created admin user you can now run server.

./gopypi runserver --config gopypi.conf

Future features

Gopypi has following features planned:

  • list of package classifiers with stats
  • only admin can login into admin interface
  • enable registration from python setup.py register
  • email notifications about package changes
  • classifiers - create page in admin groupping packages by classifier

Upload package

For instructions how to write setup.py you can find more information in admin interface HOWTO.

Screenshots

Here are some screenshots from gopypi admin

Admin dashboard

Dashboard

Admin user list

User list

Admin add user

Add user

Admin package detail

Package detail

Admin download stats

Download stats

Contributors

You are welcome!

Author

phonkee

Documentation

Overview

Set of commands that can be run from command line.

Various database (gorm) helpers

Features

gopypi provides optional features that can be enabled/disabled directly from administration

List of features:

* auto_maintainers - automatically assign maintainers from package emails (users must exist in database) * download_stats - enable download statistics for downloads

filter provides set of FilterFunc functions.

Filter maintains various filters from http forms (get/post)

Managers is set of manager structs that provide querying methods

parser package handles parsing of submitted packages.

Various string helpers

tasks is set of background tasks

timeutil is set of functions to work with time.Time such as: alignment, stripping of time values etc..

Index

Constants

View Source
const (
	FEATURE_AUTO_MAINTAINERS = "auto_maintainers"
	FEATURE_DOWNLOAD_STATS   = "download_stats"
)
View Source
const (
	DESCRIPTION_FEATURE_AUTO_MAINTAINERS = "Automatically assign maintainers on uploading package"
	DESCRIPTION_FEATURE_DOWNLOAD_STATS   = "Store download statistics of packages"
)
View Source
const (
	VERSION = "0.5.3"
                                                              o
                                                            _<|>_

   o__ __o/    o__ __o    \o_ __o     o      o   \o_ __o      o
  /v     |    /v     v\    |    v\   <|>    <|>   |    v\    <|>
 />     / \  />       <\  / \    <\  < >    < >  / \    <\   / \
 \      \o/  \         /  \o/     /   \o    o/   \o/     /   \o/
  o      |    o       o    |     o     v\  /v     |     o     |
  <\__  < >   <\__ __/>   / \ __/>      <\/>     / \ __/>    / \
         |                \o/            /       \o/
 o__     o                 |            o         |
 <\__ __/>                / \        __/>        / \

                                                     v ` + VERSION
)
View Source
const (
	TOKEN_HEADER_NAME = "Authorization"
	TOKEN_ISSUER      = "gopypi"
	TOKEN_EXPIRATION  = 24 * 3600
)

token related constants

View Source
const (
	CONTEXT_TOKEN_USER = iota + 1000
	CONTEXT_ROUTE_NAME
)

Context constants

View Source
const (
	POST_PACKAGE_ACTION_VERIFY = iota + 1
	POST_PACKAGE_ACTION_SUBMIT
	POST_PACKAGE_ACTION_DOC_UPLOAD
	POST_PACKAGE_ACTION_REMOVE_PKG
	POST_PACKAGE_ACTION_FILE_UPLOAD

	// find out how this works
	POST_PACKAGE_ACTION_USER
	POST_PACKAGE_ACTION_PASSWORD_RESET
)

WHen setup.py calls gopypi it can define following actions

View Source
const (
	PASSWORD_SALT_BYTES = 32
	PASSWORD_HASH_BYTES = 128
	PASSWORD_ITERATIONS = 16384
)

constants for pasword hashing

View Source
const (
	PASSWORD_MIN_LENGTH = 5
	PASSWORD_MAX_LENGTH = 20

	USERNAME_MIN_LENGTH = 5
	USERNAME_MAX_LENGTH = 20
)

Model related constants

View Source
const (
	CONFGEN_SECRET_KEY_LENGTH = 64
)

confgen constants

View Source
const CONF_TEMPLATE = `` /* 215-byte string literal not displayed */

template used for confgen

Variables

View Source
var (

	// Config
	ErrUnknownDBDriver = errors.New("Unknown database driver")

	// Auth
	ErrInvalidAuthHeader = errors.New("Invalid authorization header")
	ErrLoginUsername     = errors.New("invalid username")
	ErrLoginPassword     = errors.New("invalid password")
	ErrTokenExpired      = errors.New("token expired")
	ErrTokenInvalid      = errors.New("invalid token")
	ErrTokenUserInvalid  = errors.New("invalid user in token")

	// Model errors
	ErrUsernameAlreadyExists = errors.New("user with this username already exists")
	ErrPasswordsMustMatch    = errors.New("passwords must match")
	ErrInvalidEmail          = errors.New("invalid email address")
	ErrEmailAlreadyExists    = errors.New("user with this email already exists")

	ErrLicenseNotFound  = errors.New("license not found")
	ErrPlatformNotFound = errors.New("license not found")

	ErrPackageNotFound = errors.New("package not found")

	// http errors
	ErrUserCannotRetrievePackages = errors.New("user cannot retrieve packages")
	ErrUserCannotDownloadPackages = errors.New("user cannot download packages")

	// Post package errors
	ErrPostPackageInvalidAction  = errors.New("action not recognized")
	ErrPostPackageInvalidName    = errors.New("invalid name")
	ErrPostPackageInvalidVersion = errors.New("invalid version")

	// generic error for all methods that return single object
	ErrObjectNotFound = errors.New("object not found")
)
View Source
var (
	AVAILABLE_DB_DRIVERS = []string{"postgres", "mysql"}
	DEFAULT_DB_DRIVER    = "postgres"
)
View Source
var (
	CliApp *cli.App
)
View Source
var (
	// Prepare common paginator for all api
	CommonPaginator = paginator.NewFactory(
		paginator.PerPage(20, 30),
	)
)
View Source
var DoNotBypass = func(r *http.Request) bool {
	return false
}

DoNotBypass is function that doesn't bypasses basic auth

View Source
var (
	// when package is posted following actions can apply
	POST_PACKAGE_ACTIONS = map[string]int{
		"verify":         POST_PACKAGE_ACTION_VERIFY,
		"submit":         POST_PACKAGE_ACTION_SUBMIT,
		"doc_upload":     POST_PACKAGE_ACTION_DOC_UPLOAD,
		"remove_pkg":     POST_PACKAGE_ACTION_REMOVE_PKG,
		"file_upload":    POST_PACKAGE_ACTION_FILE_UPLOAD,
		"user":           POST_PACKAGE_ACTION_USER,
		"password_reset": POST_PACKAGE_ACTION_PASSWORD_RESET,
	}
)
View Source
var (

	// ValidatePassword validate password
	ValidatePassword = StringMinMaxValidator(PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH)
)

Functions

func ApplyFilterFuncs

func ApplyFilterFuncs(db *gorm.DB, funcs ...FilterFunc) *gorm.DB

ApplyFilterFuncs applies all FilterFuncs and returns db

func Asset

func Asset(name string) ([]byte, error)

Asset loads and returns the asset for the given name. It returns an error if the asset could not be found or could not be loaded.

func AssetDir

func AssetDir(name string) ([]string, error)

AssetDir returns the file names below a certain directory embedded in the file by go-bindata. For example if you run go-bindata on data/... and data contains the following hierarchy:

data/
  foo.txt
  img/
    a.png
    b.png

then AssetDir("data") would return []string{"foo.txt", "img"} AssetDir("data/img") would return []string{"a.png", "b.png"} AssetDir("foo.txt") and AssetDir("notexist") would return an error AssetDir("") will return []string{"data"}.

func AssetInfo

func AssetInfo(name string) (os.FileInfo, error)

AssetInfo loads and returns the asset info for the given name. It returns an error if the asset could not be found or could not be loaded.

func AssetNames

func AssetNames() []string

AssetNames returns the names of the assets.

func Atoui

func Atoui(value string, def ...uint) uint

Atoui parses string and converts to uint, if error occures either 0 is returned or given default value (optional)

func BasicAuthLoginRequired

func BasicAuthLoginRequired(cfg Config, bypassAuth func(r *http.Request) bool, permfuncs ...func(User) error) alice.Constructor

LoginRequired checks if username provided correct auth

bypassAuth is function that when returns true, auth is bypassed (!WARNING!)

func Bind

func Bind(r *http.Request, target interface{}) (err error)

Bind json unmarshals request body to target

func BindFilter

func BindFilter(r *http.Request, filter Filter) (err error)

BindFilter binds filter to given url values

func CommonMiddleware

func CommonMiddleware(cfg Config, router *mux.Router) alice.Constructor

func ContextSetTokenUser

func ContextSetTokenUser(ctx context.Context, user User) (result context.Context)

Return token claims from request context

func CountQueryset

func CountQueryset(db *gorm.DB, p paginator.Paginator) *gorm.DB

CountQueryset performs count query and sets paginator count

func CreateToken

func CreateToken(db *gorm.DB, user User, secret string, expiration int) (result string, err error)

CreateToken creates auth token

func Exit

func Exit(why interface{}, code ...int)

Exit exits with priting error message.

func GenerateSalt

func GenerateSalt(length int) (result []byte)

GenerateSalt generates salt for hashing passwords

func GetPostAction

func GetPostAction(r *http.Request) (result int, err error)

Return action from form

func GetRequestToken

func GetRequestToken(r *http.Request) (result string, err error)

GetRequestToken returns token from request

func InitRouter

func InitRouter(config Config) (chain alice.Chain, err error)

GetRouter instantiates router with all its registered routes

func IsEnabledOption

func IsEnabledOption(option []bool) bool

IsEnabledOption returns whether last varargs option is enabled

func LimitQueryset

func LimitQueryset(db *gorm.DB, p paginator.Paginator) *gorm.DB

LimitQueryset sets limits to queryset and returns it

func MD5

func MD5(input string) string

MD5 shortcut to create md5 hash

func Migrate

func Migrate(config Config) (err error)

Migrate runs AutoMigrate on all models.

func MigrateAction

func MigrateAction(c *cli.Context) (err error)

func MustAsset

func MustAsset(name string) []byte

MustAsset is like Asset but panics when Asset would return an error. It simplifies safe initialization of global variables.

func NewConfGen

func NewConfGen() *confgen

NewConfGen returns ConfGen instance

func NormalizePackageName

func NormalizePackageName(name string) string

NormalizePackageName normalizes package name by PEP 503

func PostEndpointCheckMiddleware

func PostEndpointCheckMiddleware(cfg Config) alice.Constructor

PostEndpointCheckMiddleware custom middleware just for post package

func RestoreAsset

func RestoreAsset(dir, name string) error

RestoreAsset restores an asset under the given directory

func RestoreAssets

func RestoreAssets(dir, name string) error

RestoreAssets restores an asset under the given directory recursively

func RunserverAction

func RunserverAction(c *cli.Context) (err error)

func StringListContains

func StringListContains(where []string, what string) bool

StringListContains returns whether string is in stringlist

func StringMinMaxValidator

func StringMinMaxValidator(min int, max int) func(field string, value *string, vr ValidationResult) bool

StringMinMaxValidator returns validator for length

func StringParseBool

func StringParseBool(value string) (result *bool)

StringParseBool returns boolean value by given string value

func TerminalGetBoolValue

func TerminalGetBoolValue(question string, def bool) (result bool)

func TerminalGetIntValue

func TerminalGetIntValue(question string, def int) (result int)

GetTermIntValue waits for user input for number

func TerminalGetPasswordValue

func TerminalGetPasswordValue(question string) (result string)

TerminalGetPasswordValue return password

func TerminalGetStringValue

func TerminalGetStringValue(question string, defs ...string) (result string)

GetTermValue calls user input.

func TerminalGetStringValueChoices

func TerminalGetStringValueChoices(question string, choices []string, defs ...string) (result string)

func TimeAlignMonth

func TimeAlignMonth(t time.Time) time.Time

TimeAlignWeek aligns date to first day of month and strips time and timezone

func TimeAlignWeek

func TimeAlignWeek(t time.Time) time.Time

TimeAlignWeek aligns date to first day of week and strips time and timezone

func TimeAlignYear

func TimeAlignYear(t time.Time) time.Time

TimeAlignWeek aligns date to first day of year and strips time and timezone

func TimeStripTime

func TimeStripTime(t time.Time) time.Time

TimeStripTime strips time part from time

func TokenAuthLoginRequired

func TokenAuthLoginRequired(cfg Config, permissions ...func(User) error) alice.Constructor

TokenAuthLoginRequired is token auth verification. It reads `gopypi-token`from headers

func ValidateEmail

func ValidateEmail(field string, value *string, vr ValidationResult, required ...bool) bool

ValidateEmail validates whether the value is valid email. Also it supports optional argument required.

func ValidateUsername

func ValidateUsername(field string, value *string, vr ValidationResult) bool

ValidateUsername validates username and adds error if available

Types

type ChangePasswordCommand

type ChangePasswordCommand struct {
	Config Config
}

ChangePasswordCommand command line command to change password

func (*ChangePasswordCommand) Run

func (c *ChangePasswordCommand) Run() (err error)

Run

type Classifier

type Classifier struct {
	ID       uint   `gorm:"primary_key" json:"id"`
	Approved bool   `json:"approved"`
	Name     string `gorm:"unique_index" json:"name"`
}

Classifier model

We store just values for now, later we can do some sort of tree

type ClassifierManager

type ClassifierManager struct {
	DB *gorm.DB
}

ClassifierManager database manager

func (*ClassifierManager) ListOrCreate

func (c *ClassifierManager) ListOrCreate(result *[]Classifier, cd []string) (err error)

ListOrCreate returns list of classifiers from given string list

func (*ClassifierManager) NormalizeName

func (c *ClassifierManager) NormalizeName(name string) string

NormalizeName normalizes classifier name

type Command

type Command interface {

	// Run runs command
	Run() error
}

Command interface that all commands use

type Config

type Config interface {

	// Core returns CoreConfig
	Core() CoreConfig

	// getter for database connection
	DB() *gorm.DB

	// logger instance
	Logger() zap.Logger

	// validates configuration
	Validate() error

	// parse html template
	ParseTemplate(name, filename string) (*gbht.Template, error)

	// parse multiple templates
	ParseTemplateFiles(name string, filenames ...string) (*gbht.Template, error)

	// render template with given data
	RenderTemplate(data interface{}, name, filename string) (string, error)

	// render templates with given data
	RenderTemplateFiles(data interface{}, name string, filenames ...string) (string, error)

	// return router
	Router() *mux.Router

	// DownloadStats return config for download stats
	DownloadStats() DownloadStatsConfig

	// packages returns configuration for packages
	Packages() PackagesConfig

	// Manager returns interface that supplies multiple db managers
	Manager(tx ...*gorm.DB) ManagerConfig
}

Config interface

func NewConfig

func NewConfig(tree *toml.TomlTree) (result Config, err error)

Return config from string

func NewConfigFromFilename

func NewConfigFromFilename(filename string) (result Config, err error)

NewConfigFromFilename returns configuration from filename

type CoreConfig

type CoreConfig interface {
	// Listen returns listen address with port
	Listen() string

	// Host returns host when defined
	Host() string

	// SecretKey returns secret key for hashing and crypto
	SecretKey() string
}

type CreateAdminCommand

type CreateAdminCommand struct {
	Config Config
}

CreateAdminCommand creates new admin in database

func (*CreateAdminCommand) Run

func (c *CreateAdminCommand) Run() error

Run is method that runs the command

type DownloadStats

type DownloadStats struct {
	ID               uint            `gorm:"primary_key" json:"-"`
	PackageVersion   *PackageVersion `gorm:"ForeignKey:PackageVersionID" json:"version,omitempty"`
	PackageVersionID uint            `json:"-"`
	Downloads        int             `json:"downloads"`
	CreatedAt        time.Time       `json:"created_at"`
}

DownloadStats is common structure for all of them

CreatedAt is aligned on every struct that embeds DownloadStats with given aggregation level. This assures that records are groupped correctly and no need to do grouping no database level.

type DownloadStatsConfig

type DownloadStatsConfig interface {

	// Returns how many weeks we should store weekly statistics
	ArchiveWeekly() int

	// Returns how many months we should store monthly statistics
	ArchiveMonthly() int
}

type DownloadStatsManager

type DownloadStatsManager struct {
	DB     *gorm.DB
	Config Config
}

DownloadStatsManager database manager for all download stats

func (*DownloadStatsManager) AddDownload

func (s *DownloadStatsManager) AddDownload(version *PackageVersion) (err error)

AddDownload adds download for given package version

func (*DownloadStatsManager) AddDownloadFile

func (d *DownloadStatsManager) AddDownloadFile(versionfile *PackageVersionFile) (err error)

AddDownloadFile adds download by PackageVersionFile

func (*DownloadStatsManager) Cleanup

func (d *DownloadStatsManager) Cleanup()

Cleanup deletes weekly and monthly stats from database, yearly stats will stay forever

func (*DownloadStatsManager) GetAllStats

func (d *DownloadStatsManager) GetAllStats(target map[string][]StatsDownloadItem, filter ...FilterFunc) (err error)

Returns all download stats

func (*DownloadStatsManager) GetCount

func (d *DownloadStatsManager) GetCount(target interface{}) (err error)

GetSum returns count of all downloads

func (*DownloadStatsManager) GetStats

func (d *DownloadStatsManager) GetStats(aggregation StatsAggregation, target *[]StatsDownloadItem, filter ...FilterFunc) *gorm.DB

type DownloadStatsMonthly

type DownloadStatsMonthly struct {
	DownloadStats
}

DownloadStatsMonthly represents download stats aggregated by month

func (*DownloadStatsMonthly) BeforeCreate

func (s *DownloadStatsMonthly) BeforeCreate() error

BeforeSave aligns CreatedAt correctly

type DownloadStatsWeekly

type DownloadStatsWeekly struct {
	DownloadStats
}

DownloadStatsWeekly represents download stats aggregated by week

func (*DownloadStatsWeekly) BeforeCreate

func (s *DownloadStatsWeekly) BeforeCreate() error

BeforeSave aligns CreatedAt correctly

type DownloadStatsYearly

type DownloadStatsYearly struct {
	DownloadStats
}

DownloadStatsYearly represents download stats aggregated by year

func (*DownloadStatsYearly) BeforeCreate

func (s *DownloadStatsYearly) BeforeCreate() error

BeforeSave aligns CreatedAt correctly

type FallbackFileSystem

type FallbackFileSystem struct {
	// contains filtered or unexported fields
}

FallbackFileSystem wraps http Filesystem which checks if requested file is not found, it falls back to given default. This is useful for SPA with html5 mode so it opens any url in index.html

func (*FallbackFileSystem) Open

func (f *FallbackFileSystem) Open(name string) (result http.File, err error)

Open opens and reads file

type Feature

type Feature struct {
	ID          string `gorm:"primary_key" json:"id"`
	Description string `json:"description"`
	Value       bool   `json:"value"`
}

Feature is model for enabling/disabling gopypi features

type FeatureAPIViewSet

type FeatureAPIViewSet struct {
	classy.SlugViewSet

	// config instance to access managers, etc..
	Config Config
}

FeatureAPIViewSet provides rest endpoints for features

func (*FeatureAPIViewSet) List

func (f *FeatureAPIViewSet) List(w http.ResponseWriter, r *http.Request) response.Response

List returns list of all features

func (*FeatureAPIViewSet) Retrieve

func (f *FeatureAPIViewSet) Retrieve(w http.ResponseWriter, r *http.Request) response.Response

Retrieve retrieves single feature from database

func (*FeatureAPIViewSet) Update

func (f *FeatureAPIViewSet) Update(w http.ResponseWriter, r *http.Request) response.Response

Update updates given feature (just value)

type FeatureManager

type FeatureManager struct {
	*Manager
}

FeatureManager database manager for model Feature

func (*FeatureManager) IsEnabledFeature

func (f *FeatureManager) IsEnabledFeature(feature string) (result bool, err error)

IsEnabledFeature returns whether given feature is available

type FeatureSerializer

type FeatureSerializer struct {
	Value bool `json:"value"`
}

FeatureSerializer enables/disables feature

type Filter

type Filter interface {

	// returns list of filter funcs
	Apply(queryset *gorm.DB) *gorm.DB
}

Filter is interface to filter database querysets

func NewUserListFilter

func NewUserListFilter(r *http.Request) Filter

NewUserListFilter returns new UserListFilter

type FilterFunc

type FilterFunc func(*gorm.DB) *gorm.DB

FilterFunc is callback which is called in some methods (Get, List)

func FFDownloadStatsPackage

func FFDownloadStatsPackage(pack *Package) FilterFunc

FFDownloadStatsPackage filters stats by package

func FFDownloadStatsPackageVersion

func FFDownloadStatsPackageVersion(pv *PackageVersion) FilterFunc

FFDownloadStatsPackage filters stats by package

func FFID

func FFID(id interface{}) FilterFunc

FFID adds clause to search by id

func FFOrderBy

func FFOrderBy(order string) FilterFunc

FFOrderBy orders by

func FFPackagesFor

func FFPackagesFor(user User) FilterFunc

FFPackagesFor filters packages by given user, user is either author or maintainer

func FFPreload

func FFPreload(preloads ...string) FilterFunc

FFPreload add preloads to que

func FFUsername

func FFUsername(username string) FilterFunc

FilterUsername filters user by username

func FFWhere

func FFWhere(query interface{}, args ...interface{}) FilterFunc

FFWhere is shorthand for gorm Where

type InfoAPIView

type InfoAPIView struct {
	classy.GenericView

	// store config instance
	Config Config
}

InfoAPIView serves GET request and returns information about gopypi server

func (*InfoAPIView) GET

func (i *InfoAPIView) GET(w http.ResponseWriter, r *http.Request) response.Response

GET method returns information about gopypi such as version, features, system info

type License

type License struct {
	ID       uint   `gorm:"primary_key" json:"id"`
	Approved bool   `json:"approved"`
	Code     string `json:"code"`
	Content  string `json:"content"`
	Name     string `json:"name"`
}

License model

Holds informations about available licenses.

type LicenseAPIViewSet

type LicenseAPIViewSet struct {
	classy.ViewSet

	// configuration
	Config Config
}

LicenseAPIViewSet provides rest apis for handling of license model

func (*LicenseAPIViewSet) List

func (l *LicenseAPIViewSet) List(w http.ResponseWriter, r *http.Request) response.Response

List all licenses

func (*LicenseAPIViewSet) MetadataList

func (l *LicenseAPIViewSet) MetadataList(w http.ResponseWriter, r *http.Request) response.Response

Metadata for list endpoints

func (*LicenseAPIViewSet) Retrieve

func (l *LicenseAPIViewSet) Retrieve(w http.ResponseWriter, r *http.Request) response.Response

Retrieve single license from database

func (*LicenseAPIViewSet) Update

func (l *LicenseAPIViewSet) Update(w http.ResponseWriter, r *http.Request) response.Response

Update updates license with serializer data

type LicenseManager

type LicenseManager struct {
	DB *gorm.DB
}

LicenseManager database manager

func (*LicenseManager) GetByCode

func (l *LicenseManager) GetByCode(code string) (license License, err error)

GetByCode returns license by code, if not exist create new one

type LicenseUpdateSerializer

type LicenseUpdateSerializer struct {
	ID       uint   `json:"-"`
	Approved bool   `json:"approved"`
	Name     string `json:"name"`
	Content  string `json:"content"`
}

LicenseUpdateSerializer handles license update

func (*LicenseUpdateSerializer) UpdateLicense

func (l *LicenseUpdateSerializer) UpdateLicense(license *License)

Update license with data form serializer

func (*LicenseUpdateSerializer) Validate

func (l *LicenseUpdateSerializer) Validate(cfg Config, ID int) (result ValidationResult)

Validate runs validation on serializer fields

type LoginAPIView

type LoginAPIView struct {
	classy.GenericView

	Config Config
}

LoginAPIView servers single POST method

func (*LoginAPIView) POST

func (l *LoginAPIView) POST(w http.ResponseWriter, r *http.Request) response.Response

POST checks for username and password in JSON format and returns appropriate json response. If succeeds, Authorization header is added with correct token

type LoginSerializer

type LoginSerializer struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

LoginSerializer is login form for json

func (*LoginSerializer) Validate

func (l *LoginSerializer) Validate() (err error)

type Manager

type Manager struct {
	DB *gorm.DB
}

Generic manager

Should have following methods:

* List * Get

func (*Manager) Get

func (p *Manager) Get(target interface{}, filter ...FilterFunc) *gorm.DB

Get calls Where method from given pack with filter funcs applied

func (*Manager) List

func (p *Manager) List(target interface{}, filter ...FilterFunc) *gorm.DB

List calls Where method from given pack with filter funcs applied

type ManagerConfig

type ManagerConfig interface {
	// ClassifierManager returns new ClassifierManager instance
	Classifier(tx ...*gorm.DB) *ClassifierManager

	// DownloadStatsManager returns new DownloadStatsManager instance
	DownloadStats(tx ...*gorm.DB) *DownloadStatsManager

	// FeatureManager
	Feature(tx ...*gorm.DB) *FeatureManager

	// LicenseManager returns new LicenseManager instance
	License(tx ...*gorm.DB) *LicenseManager

	// PackageManager returns new PackageManager instance
	Package(tx ...*gorm.DB) *PackageManager

	// PackageVersionManager returns new PackageVersionManager instance
	PackageVersion(tx ...*gorm.DB) *PackageVersionManager

	// PackageVersionFileManager
	PackageVersionFile(tx ...*gorm.DB) *PackageVersionFileManager

	// PlatformManager returns new PlatformManager instance
	Platform(tx ...*gorm.DB) *PlatformManager

	// UserManager returns UserManager instance to query user data
	User(tx ...*gorm.DB) *UserManager
}

type MeAPIView

type MeAPIView struct {
	classy.GenericView

	// store config
	Config Config
}

MeAPIView gives information about currently logged in user

GET method returns information about user from token POST method updates information

func (*MeAPIView) GET

func (m *MeAPIView) GET(w http.ResponseWriter, r *http.Request) response.Response

GET returns information about user (from auth token)

func (*MeAPIView) POST

func (m *MeAPIView) POST(w http.ResponseWriter, r *http.Request) response.Response

POST updates user information

type MeChangePasswordAPIView

type MeChangePasswordAPIView struct {
	classy.GenericView

	// store config
	Config Config
}

MeChangePasswordAPIView provides api to change password for currently logged user

func (*MeChangePasswordAPIView) POST

func (m *MeChangePasswordAPIView) POST(w http.ResponseWriter, r *http.Request) response.Response

POST method updates password for currently logged user

type MyPackageAPIView

type MyPackageAPIView struct {
	classy.ListView

	// store config
	Config Config
}

MyPackageAPIView gives information about packages for currently logged in user

func (*MyPackageAPIView) List

func (m *MyPackageAPIView) List(w http.ResponseWriter, r *http.Request) response.Response

List returns list of packages for given logged user. User must be either author or maintainer of packege to be returned in list.

type Package

type Package struct {
	ID          uint             `gorm:"primary_key" json:"id"`
	Name        string           `json:"name"`
	Versions    []PackageVersion `gorm:"ForeignKey:PackageID" json:"versions,omitempty"`
	Maintainers []User           `gorm:"many2many:package_maintainers;" json:"maintainers,omitempty"`
	CreatedAt   time.Time        `json:"created_at"`
	UpdatedAt   time.Time        `json:"updated_at"`
	Author      *User            `gorm:"ForeignKey:AuthorID" json:"author,omitempty"`
	AuthorID    uint             `json:"-"`
}

Package model.

func GetPostedPackage

func GetPostedPackage(cfg Config, r *http.Request) (pack Package, err error)

GetPackage parses request form and returns package

func (*Package) BeforeCreate

func (p *Package) BeforeCreate() error

BeforeCreate sets CreatedAt

func (*Package) BeforeSave

func (p *Package) BeforeSave() error

BeforeSave sets UpdatedAt

type PackageAPIViewSet

type PackageAPIViewSet struct {
	classy.ViewSet

	// store config
	Config Config
}

PackageAPIViewSet provides following methods

List - list packages Retrieve - retrieve single package

func (*PackageAPIViewSet) List

func (p *PackageAPIViewSet) List(w http.ResponseWriter, r *http.Request) response.Response

List retrieves list of packages

func (*PackageAPIViewSet) Retrieve

func (p *PackageAPIViewSet) Retrieve(w http.ResponseWriter, r *http.Request) response.Response

Retrieve returns single package

type PackageDetailView

type PackageDetailView struct {
	classy.SlugDetailView

	// config instance
	Config Config
}

PackageDetailView returns detail of package

func (*PackageDetailView) Retrieve

func (p *PackageDetailView) Retrieve(w http.ResponseWriter, r *http.Request) response.Response

Retrieve is GET method

type PackageDownloadView

type PackageDownloadView struct {
	classy.BaseView

	Config Config
}

PackageDownloadView serves download

func (*PackageDownloadView) Download

func (p *PackageDownloadView) Download(w http.ResponseWriter, r *http.Request) response.Response

Download returns content of requested file.

Aside of that, when download_stats feature is enabled, stats will be recorded to database.

func (*PackageDownloadView) Routes

func (p *PackageDownloadView) Routes() (result map[string]classy.Mapping)

Routes returns list of routes with predefined method maps

type PackageListView

type PackageListView struct {
	classy.ListView

	// config instance
	Config Config
}

PackageListView returns list of packages

func (*PackageListView) List

func (p *PackageListView) List(rw http.ResponseWriter, r *http.Request) response.Response

List (http GET) returns list of all packages

if `format` url query is set to json, json response will be returned

type PackageMaintainerAPIViewSet

type PackageMaintainerAPIViewSet struct {
	classy.ViewSet

	// config instance
	Config Config
}

PackageMaintainerAPIViewSet provides rest endpoints for maintainers of given package

func (*PackageMaintainerAPIViewSet) Delete

func (p *PackageMaintainerAPIViewSet) Delete(w http.ResponseWriter, r *http.Request) response.Response

Delete removes association of maintainer to given package

func (*PackageMaintainerAPIViewSet) GetPackage

func (p *PackageMaintainerAPIViewSet) GetPackage(r *http.Request) (result Package, err error)

GetPackage returns package from request

func (*PackageMaintainerAPIViewSet) List

func (p *PackageMaintainerAPIViewSet) List(w http.ResponseWriter, r *http.Request) response.Response

List method lists all maintainers for given package

func (*PackageMaintainerAPIViewSet) Update

func (p *PackageMaintainerAPIViewSet) Update(w http.ResponseWriter, r *http.Request) response.Response

Update association means that when it's not present, it's created, Update method doesn't handle request body.

type PackageManager

type PackageManager struct {
	DB *gorm.DB
}

PackageManager database manager

func (*PackageManager) Get

func (p *PackageManager) Get(pack *Package, filter ...FilterFunc) *gorm.DB

Get calls Where method from given pack with preloads

func (*PackageManager) IsMaintainer

func (p *PackageManager) IsMaintainer(pack *Package, user *User) bool

check if user is maintainer

func (*PackageManager) List

func (p *PackageManager) List(packages *[]Package, filter ...FilterFunc) *gorm.DB

Get calls Where method from given pack with preloads

func (*PackageManager) UpdateVersionOrder

func (p *PackageManager) UpdateVersionOrder(pack Package) (err error)

UpdateVersionOrder updates order for all versions

type PackageVersion

type PackageVersion struct {
	ID           uint                 `gorm:"primary_key" json:"id"`
	PackageID    uint                 `json:"-"`
	CreatedAt    time.Time            `json:"created_at"`
	UpdatedAt    time.Time            `json:"updated_at"`
	Author       *User                `gorm:"ForeignKey:AuthorID" json:"author,omitempty"`
	AuthorID     uint                 `json:"-"`
	Comment      string               `json:"comment"`
	Description  string               `json:"description"`
	Summary      string               `json:"summary"`
	HomePage     string               `json:"home_page"`
	License      *License             `gorm:"ForeignKey:LicenseID" json:"license,omitempty"`
	LicenseID    uint                 `json:"-"`
	Version      string               `gorm:"index" json:"version"`
	VersionOrder int                  `gorm:"index" json:"version_order"`
	Files        []PackageVersionFile `gorm:"ForeignKey:PackageVersionID" json:"files,omitempty"`
	Classifiers  []Classifier         `gorm:"many2many:package_version_classifiers;" json:"classifiers,omitempty"`
}

PackageVersion model that holds information about given package version

func GetPostedPackageVersion

func GetPostedPackageVersion(cfg Config, pack Package, r *http.Request) (pv PackageVersion, err error)

Return package version

func (*PackageVersion) BeforeCreate

func (p *PackageVersion) BeforeCreate() error

BeforeCreate sets CreatedAt

func (*PackageVersion) BeforeSave

func (p *PackageVersion) BeforeSave() error

BeforeSave sets UpdatedAt

type PackageVersionFile

type PackageVersionFile struct {
	ID               uint      `gorm:"primary_key" json:"id"`
	PackageVersionID uint      `json:"-"`
	Filename         string    `json:"filename"`
	RelativePath     string    `json:"relative_path"`
	MD5Digest        string    `gorm:"column:md5_digest" json:"md5_digest"`
	Author           *User     `gorm:"ForeignKey:AuthorID" json:"author,omitempty"`
	AuthorID         uint      `json:"-"`
	CreatedAt        time.Time `json:"created_at"`

	// this field is used to have pregenearated download url
	DownloadURL string `gorm:"-" json:"download_url,omitempty"`
}

PackageVersionFile model

func GetPostedPackageVersionFile

func GetPostedPackageVersionFile(config Config, pv PackageVersion, r *http.Request) (result PackageVersionFile, filename string, err error)

Returns PackageVersionFile along with tempfile

func (*PackageVersionFile) BeforeCreate

func (p *PackageVersionFile) BeforeCreate() error

BeforeCreate sets CreatedAt

func (PackageVersionFile) GenerateRelativePath

func (p PackageVersionFile) GenerateRelativePath() string

GenerateRelativePath generates random relative path

type PackageVersionFileManager

type PackageVersionFileManager struct {
	*Manager

	PackagesDir string
	Router      *mux.Router
}

PackageVersionFileManager database manager

func (*PackageVersionFileManager) GetAbsoluteFilename

func (p *PackageVersionFileManager) GetAbsoluteFilename(pvf *PackageVersionFile) string

GetAbsoluteFilename returns full package filename

func (*PackageVersionFileManager) GetDownloadURL

func (p *PackageVersionFileManager) GetDownloadURL(pvf *PackageVersionFile) string

GetDownloadURL returns full url for downloading package

func (*PackageVersionFileManager) GetRelativeFilename

func (p *PackageVersionFileManager) GetRelativeFilename(pvf *PackageVersionFile) string

GetAbsoluteFilename returns full package filename

type PackageVersionManager

type PackageVersionManager struct {
	*Manager
}

PackageVersionManager database manager

type PackageVersionPathInfo

type PackageVersionPathInfo struct {
	FullPath         string
	RelativePath     string
	RelativeFilename string
	FullFilename     string
}

func GetPackageVersionFilePath

func GetPackageVersionFilePath(packagedir, packagename, filename string) (pvi PackageVersionPathInfo, err error)

GetPackageRelativePath returns relative filename with added directory to packages dir

type PackagesConfig

type PackagesConfig interface {
	// Directory returns full directory containint all packages
	Directory() string

	// JoinDirectory joins given parts with directory
	JoinDirectory(part ...string) string
}

type Platform

type Platform struct {
	ID          uint   `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
}

Platform model tracks all platforms such as: Linux, Darwin even more esoteric.

type PlatformAPIViewSet

type PlatformAPIViewSet struct {
	classy.ViewSet

	// configuration
	Config Config
}

PlatformAPIViewSet provides rest endpoints for platform (RU)

func (*PlatformAPIViewSet) List

func (p *PlatformAPIViewSet) List(w http.ResponseWriter, r *http.Request) response.Response

List returns all platform stored in database

func (*PlatformAPIViewSet) Retrieve

func (p *PlatformAPIViewSet) Retrieve(w http.ResponseWriter, r *http.Request) response.Response

Retrieve returns single platform

type PlatformManager

type PlatformManager struct {
	DB *gorm.DB
}

PlatformManager database manager for model Platform

func (*PlatformManager) Get

func (p *PlatformManager) Get(platform *Platform) *gorm.DB

Get returns platform by set fields

func (*PlatformManager) List

func (p *PlatformManager) List(platforms *[]Platform, filter ...FilterFunc) *gorm.DB

List returns list of platforms

func (*PlatformManager) ListOrCreate

func (p *PlatformManager) ListOrCreate(target *[]Platform, platforms []string) error

Return multiple platforms

type PostPackageView

type PostPackageView struct {
	classy.GenericView

	Config Config
}

PostPackageView handles all methods on package

func (*PostPackageView) ActionFileUpload

func (p *PostPackageView) ActionFileUpload(r *http.Request) response.Response

ActionFileUpload Handles file upload which is called with

`python setup.py upload`

func (*PostPackageView) Before

func (p *PostPackageView) Before(w http.ResponseWriter, r *http.Request) (resp response.Response)

Before every request we need to check permissions

func (*PostPackageView) POST

func (p *PostPackageView) POST(w http.ResponseWriter, r *http.Request) response.Response

POST method for handling package

type SearchResult

type SearchResult struct {
	// contains filtered or unexported fields
}

SearchResult item

type SearchService

type SearchService struct {
	Config Config
}

SearchService xml rpc service for searching packages

func (*SearchService) Dispatch

func (s *SearchService) Dispatch(method string, root *etree.Element) (doc *etree.Document, err error)

Dispatch dispatches method on service, do not use this method directly. root is params *etree.Element (actually "methodCall/params"

func (*SearchService) ListMethods

func (s *SearchService) ListMethods() []string

ListMethods returns list of all available methods for given service

func (*SearchService) MethodExists

func (s *SearchService) MethodExists(method string) (ok bool)

MethodExists returns whether rpc method is available on service

type Server

type Server interface {
	// Config returns server config
	Config() Config

	// Router returns server router
	Router() *mux.Router

	// ListenAndServe runs http server
	ListenAndServe() error
}

func New

func New(cfg Config) (result Server, err error)

New returns fresh gopypi server instance bound to config.

type Stats

type Stats struct {
	Packages    int `json:"packages"`
	ActiveUsers int `json:"active_users"`
	Licenses    int `json:"licenses"`
	Downloads   int `json:"downloads"`
}

Stats result

type StatsAPIView

type StatsAPIView struct {
	classy.GenericView

	Config Config
}

StatsAPIView returns some statistic information for admin dashboard.

func (*StatsAPIView) GET

func (s *StatsAPIView) GET(w http.ResponseWriter, r *http.Request) response.Response

GET returns summary statistics about gopypi

type StatsAggregation

type StatsAggregation int
const (
	STATS_DOWNLOAD_WEEKLY StatsAggregation = iota + 1
	STATS_DOWNLOAD_MONTHLY
	STATS_DOWNLOAD_ALL
)

type StatsDownloadAllAPIView

type StatsDownloadAllAPIView struct {
	classy.ListView

	// config instance
	Config Config
}

StatsDownloadAllAPIView provides stats of package downloads

func (*StatsDownloadAllAPIView) List

func (s *StatsDownloadAllAPIView) List(w http.ResponseWriter, r *http.Request) response.Response

List returns download stats about all packages (sums)

type StatsDownloadItem

type StatsDownloadItem struct {
	PackageVersion   *PackageVersion `gorm:"ForeignKey:PackageVersionID" json:"version,omitempty"`
	PackageVersionID uint            `json:"-"`
	Downloads        int             `json:"downloads"`
	CreatedAt        time.Time       `json:"created_at"`
}

type StatsDownloadPackageAPIView

type StatsDownloadPackageAPIView struct {
	classy.DetailView

	// config instance
	Config Config
}

StatsDownloadPackageAPIView provides stats of package downloads

func (*StatsDownloadPackageAPIView) Retrieve

func (s *StatsDownloadPackageAPIView) Retrieve(w http.ResponseWriter, r *http.Request) response.Response

List returns download stats about all packages (sum)

type StatsDownloadPackageVersionAPIView

type StatsDownloadPackageVersionAPIView struct {
	classy.DetailView

	// config instance
	Config Config
}

StatsDownloadPackageVersionAPIView provides stats of package version downloads

func (*StatsDownloadPackageVersionAPIView) Retrieve

func (s *StatsDownloadPackageVersionAPIView) Retrieve(w http.ResponseWriter, r *http.Request) response.Response

List returns download stats about all package versions (sum)

type Task

type Task interface {

	// Run executes task
	Run(config Config) error
}

Task interface for background running tasks

type TemplateView

type TemplateView struct {
	classy.GenericView

	// configuration
	Config Config

	// Data for template
	Data map[string]interface{}

	// TemplateFilename
	TemplateName string
}

TemplateView can be directly instantiated when registering view. Be careful that template view needs Config and TemplateName.

Example how ::

classy.New(TemplateView{Config: cfg, TemplateName: "homepage.tpl.html"}).Register(router, "homepage")

func (TemplateView) GET

GET handles return rendered template

type TokenClaims

type TokenClaims struct {
	UserID uint `json:"user_id"`
	jwt.StandardClaims
}

TokenClaims holds information for token

func ParseToken

func ParseToken(r *http.Request, secret string) (claims *TokenClaims, err error)

ParseToken parses token and returns claims

type User

type User struct {
	ID        uint   `gorm:"primary_key" json:"id"`
	Username  string `gorm:"type:varchar(20);unique_index" json:"username"`
	Email     string `gorm:"type:varchar(100);index" json:"email"`
	Password  string `gorm:"type:varchar(256)" json:"-"`
	FirstName string `gorm:"type:varchar(40)" json:"first_name"`
	LastName  string `gorm:"type:varchar(40)" json:"last_name"`
	IsActive  bool   `json:"is_active"`
	IsAdmin   bool   `json:"is_admin"`

	// permissions
	CanList     bool `json:"can_list"`
	CanCreate   bool `json:"can_create"`
	CanDownload bool `json:"can_download"`
	CanUpdate   bool `json:"can_update"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

User model

To set/verify user password please use UserManager

func ContextGetTokenUser

func ContextGetTokenUser(ctx context.Context) (user User, err error)

Return token claims from request context

func (*User) BeforeCreate

func (u *User) BeforeCreate() error

BeforeCreate sets CreatedAt

func (*User) BeforeSave

func (u *User) BeforeSave() error

BeforeSave sets UpdatedAt

type UserAPIViewSet

type UserAPIViewSet struct {
	classy.ViewSet

	// store config
	Config Config
}

UserAPIViewSet handles basic crud on users

func (*UserAPIViewSet) Create

func (u *UserAPIViewSet) Create(w http.ResponseWriter, r *http.Request) response.Response

Create is called when new user is created

func (*UserAPIViewSet) List

func (u *UserAPIViewSet) List(w http.ResponseWriter, r *http.Request) response.Response

List of users

func (*UserAPIViewSet) Retrieve

func (u *UserAPIViewSet) Retrieve(w http.ResponseWriter, r *http.Request)

Retrieve single user

func (*UserAPIViewSet) Update

func (u *UserAPIViewSet) Update(w http.ResponseWriter, r *http.Request) response.Response

Update is called when POST is called to update user

type UserAddSerializer

type UserAddSerializer struct {
	Username    string `json:"username"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"`
	Email       string `json:"email"`
	Password    string `json:"password"`
	Password2   string `json:"password2"`
	IsActive    bool   `json:"is_active"`
	IsAdmin     bool   `json:"is_admin"`
	CanList     bool   `json:"can_list"`
	CanDownload bool   `json:"can_download"`
	CanCreate   bool   `json:"can_create"`
	CanUpdate   bool   `json:"can_update"`
}

Serializer to create new user

func (*UserAddSerializer) GetUser

func (u *UserAddSerializer) GetUser(cfg Config) (user User)

return user initialized with serializer values

func (*UserAddSerializer) Validate

func (u *UserAddSerializer) Validate(cfg Config) (result ValidationResult)

Validate validates information about new user

@TODO: add ValidateUsername and ValidateEmail

type UserChangePasswordSerializer

type UserChangePasswordSerializer struct {
	Current   string `json:"current"`
	Password  string `json:"password"`
	Password2 string `json:"password2"`
	User      User   `json:"-"`
}

Serializer to change password for currently logged user

func (*UserChangePasswordSerializer) ChangePassword

func (u *UserChangePasswordSerializer) ChangePassword(config Config, user *User) (err error)

ChangePassword changes password for given user

func (*UserChangePasswordSerializer) Validate

func (u *UserChangePasswordSerializer) Validate(cfg Config) (result ValidationResult)

Validate validates serializer data

type UserListFilter

type UserListFilter struct {
	IsActive *bool
}

UserListFilter filters users from url

func (UserListFilter) Apply

func (u UserListFilter) Apply(queryset *gorm.DB) *gorm.DB

Apply applies filter to queryset

type UserManager

type UserManager struct {
	DB        *gorm.DB
	SecretKey string
}

UserManager groups functionality to query user model instances

func (*UserManager) ExistsEmail

func (u *UserManager) ExistsEmail(email string) bool

ExistsEmail returns whether user with given email exists in database

func (*UserManager) ExistsUsername

func (u *UserManager) ExistsUsername(username string) bool

ExistsUsername returns whether user with given username exists in database

func (*UserManager) Get

func (u *UserManager) Get(user *User) *gorm.DB

Get

func (*UserManager) SetPassword

func (u *UserManager) SetPassword(user *User, password string)

SetPassword sets password for given user

func (*UserManager) VerifyPassword

func (u *UserManager) VerifyPassword(user User, password string) bool

VerifyPassword verifies password for user

type UserProfileSerializer

type UserProfileSerializer struct {
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Email     string `json:"email"`
}

Serializer for updating user profile

func (*UserProfileSerializer) Validate

func (u *UserProfileSerializer) Validate(cfg Config) (result ValidationResult)

Validate validates serializer

type UserUpdateSerializer

type UserUpdateSerializer struct {
	ID          uint   `json:"-"`
	Username    string `json:"username"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"`
	Email       string `json:"email"`
	Password    string `json:"password"`
	Password2   string `json:"password2"`
	IsActive    bool   `json:"is_active"`
	IsAdmin     bool   `json:"is_admin"`
	CanList     bool   `json:"can_list"`
	CanDownload bool   `json:"can_download"`
	CanCreate   bool   `json:"can_create"`
	CanUpdate   bool   `json:"can_update"`
	// contains filtered or unexported fields
}

Serializer to update existing user

func (*UserUpdateSerializer) UpdateUser

func (u *UserUpdateSerializer) UpdateUser(cfg Config, user *User)

UpdateUser updates user with serializer data

func (*UserUpdateSerializer) Validate

func (u *UserUpdateSerializer) Validate(cfg Config) (result ValidationResult)

Validate validates data in serializer

type ValidationError

type ValidationError struct {
	Code  string `json:"code,omitempty"`
	Error string `json:"error"`
}

type ValidationResult

type ValidationResult interface {
	// add field related error
	AddFieldError(field string, err error, code ...string) ValidationResult

	// add not field related error
	AddUnboundError(err error, code ...string) ValidationResult

	// returns whether result is valid
	IsValid() bool

	// marshals result to json
	MarshalJSON() ([]byte, error)

	// returns whether field has error
	HasFieldError(field string) bool
}

func NewValidationResult

func NewValidationResult() ValidationResult

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL