Intégration d'un point d'entrée GraphQL et d'un connecteur pour

PostgreSQL

- Possibilité de migrer le schéma de la base de données via drapeau
- Génération du code GraphQL avec https://gqlgen.com/
This commit is contained in:
2020-07-13 09:20:14 +02:00
parent 1120474ad9
commit 591112a800
28 changed files with 984 additions and 15 deletions

View File

@ -0,0 +1,146 @@
package migration
import (
"context"
"github.com/pkg/errors"
)
var (
ErrNoAvailableMigration = errors.New("no available migration")
ErrMigrationNotFound = errors.New("migration not found")
)
type Manager struct {
migrations []Migration
resolver VersionResolver
}
func (m *Manager) Up(ctx context.Context) error {
currentVersion, err := m.resolver.Current(ctx)
if err != nil {
return errors.Wrap(err, "could not retrieve current version")
}
migrate := func(up Migration) error {
if err := up.Up(ctx); err != nil {
return errors.Wrapf(err, "could not apply '%s' up migration", up.Version())
}
if err := m.resolver.Set(ctx, up.Version()); err != nil {
return errors.Wrapf(err, "could not update schema version to '%s'", up.Version())
}
return nil
}
if currentVersion == "" {
up := m.migrations[0]
return migrate(up)
}
for i, mi := range m.migrations {
if mi.Version() != currentVersion && currentVersion != "" {
continue
}
// Already at latest, do nothing
if i >= len(m.migrations)-1 {
return nil
}
up := m.migrations[i+1]
return migrate(up)
}
return errors.WithStack(ErrMigrationNotFound)
}
func (m *Manager) Down(ctx context.Context) error {
currentVersion, err := m.resolver.Current(ctx)
if err != nil {
return errors.Wrap(err, "could not retrieve current version")
}
for i, mi := range m.migrations {
if mi.Version() != currentVersion {
continue
}
if err := mi.Down(ctx); err != nil {
return errors.Wrapf(err, "could not apply '%s' down migration", mi.Version())
}
var version string
// Already at oldest, do nothing
if i != 0 {
down := m.migrations[i-1]
version = down.Version()
}
if err := m.resolver.Set(ctx, version); err != nil {
return errors.Wrapf(err, "could not update schema version to '%s'", version)
}
return nil
}
return errors.WithStack(ErrMigrationNotFound)
}
func (m *Manager) Latest(ctx context.Context) error {
for {
isLatest, err := m.IsLatest(ctx)
if err != nil {
return errors.Wrap(err, "could not retrieve schema state")
}
if isLatest {
return nil
}
if err := m.Up(ctx); err != nil {
return errors.WithStack(err)
}
}
}
func (m *Manager) Register(migrations ...Migration) {
m.migrations = migrations
}
func (m *Manager) CurrentVersion(ctx context.Context) (string, error) {
return m.resolver.Current(ctx)
}
func (m *Manager) LatestVersion() (string, error) {
if len(m.migrations) == 0 {
return "", errors.WithStack(ErrNoAvailableMigration)
}
return m.migrations[len(m.migrations)-1].Version(), nil
}
func (m *Manager) IsLatest(ctx context.Context) (bool, error) {
currentVersion, err := m.resolver.Current(ctx)
if err != nil {
return false, errors.Wrap(err, "could not retrieve current version")
}
latestVersion, err := m.LatestVersion()
if err != nil {
return false, errors.Wrap(err, "could not retrieve latest version")
}
return currentVersion == latestVersion, nil
}
func NewManager(resolver VersionResolver) *Manager {
return &Manager{
resolver: resolver,
migrations: make([]Migration, 0),
}
}

View File

@ -0,0 +1,9 @@
package migration
import "context"
type Migration interface {
Version() string
Up(context.Context) error
Down(context.Context) error
}

View File

@ -0,0 +1,13 @@
package migration
import (
"gitlab.com/wpetit/goweb/service"
)
func ServiceProvider(resolver VersionResolver) service.Provider {
manager := NewManager(resolver)
return func(ctn *service.Container) (interface{}, error) {
return manager, nil
}
}

View File

@ -0,0 +1,33 @@
package migration
import (
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/service"
)
const ServiceName service.Name = "migration"
// From retrieves the migration service in the given container.
func From(container *service.Container) (*Manager, error) {
service, err := container.Service(ServiceName)
if err != nil {
return nil, errors.Wrapf(err, "error while retrieving '%s' service", ServiceName)
}
srv, ok := service.(*Manager)
if !ok {
return nil, errors.Errorf("retrieved service is not a valid '%s' service", ServiceName)
}
return srv, nil
}
// Must retrieves the migration service in the given container or panic otherwise.
func Must(container *service.Container) *Manager {
srv, err := From(container)
if err != nil {
panic(err)
}
return srv
}

View File

@ -0,0 +1,8 @@
package migration
import "context"
type VersionResolver interface {
Current(context.Context) (string, error)
Set(context.Context, string) error
}