Files
bridge/internal/service/service.go
2023-07-18 13:36:41 +00:00

140 lines
3.7 KiB
Go

package service
import (
"context"
"go.infratographer.com/x/events"
"go.infratographer.com/x/gidx"
"go.uber.org/zap"
"go.equinixmetal.net/infra9-metal-bridge/internal/metal"
"go.equinixmetal.net/infra9-metal-bridge/internal/permissions"
)
const (
// TypeOrganization defines the organization type.
TypeOrganization ObjectType = "organization"
// TypeProject defines the project type.
TypeProject ObjectType = "project"
// TypeUser defines the user type.
TypeUser ObjectType = "user"
)
// DefaultPrefixMap is the default id prefix to type relationship.
var DefaultPrefixMap = map[string]ObjectType{
TypeOrganization.Prefix(): TypeOrganization,
TypeProject.Prefix(): TypeProject,
TypeUser.Prefix(): TypeUser,
}
// ObjectType defines a type of object.
type ObjectType string
// Prefix returns the objects id prefix.
func (t ObjectType) Prefix() string {
switch t {
case TypeOrganization:
return "metlorg"
case TypeProject:
return "metlprj"
case TypeUser:
return "metlusr"
default:
return ""
}
}
// String returns a string fo the object type.
func (t ObjectType) String() string {
return string(t)
}
// Service defines a bridge service methods
type Service interface {
// IsOrganizationID checks if the provided id has an id prefix which is an organization.
IsOrganizationID(id gidx.PrefixedID) bool
// TouchOrganization triggers a sync of an organization.
TouchOrganization(ctx context.Context, id gidx.PrefixedID) error
// DeleteOrganization deletes an organization and all of its resources.
DeleteOrganization(ctx context.Context, id gidx.PrefixedID) error
// IsProjectID checks if the provided id has an id prefix which is a project.
IsProjectID(id gidx.PrefixedID) bool
// TouchProject triggers a sync of an organization
TouchProject(ctx context.Context, id gidx.PrefixedID) error
// DeleteProject deletes the project and all of its resources.
DeleteProject(ctx context.Context, id gidx.PrefixedID) error
// IsUser checks if the provided id has an id prefix which is a user.
IsUser(id gidx.PrefixedID) bool
// AssignUser assigns a user to the given resource.
AssignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error
// UnassignUser removes the users from the given resource.
UnassignUser(ctx context.Context, userID gidx.PrefixedID, resourceIDs ...gidx.PrefixedID) error
// IsAssignableResource checks if the provided resource ID may have assigned users.
IsAssignableResource(id gidx.PrefixedID) bool
}
// EventPublisher defines the required methods to publish events.
type EventPublisher interface {
PublishChange(ctx context.Context, subjectType string, change events.ChangeMessage) error
}
var _ Service = &service{}
type service struct {
logger *zap.SugaredLogger
publisher EventPublisher
metal metal.Client
perms permissions.Client
idPrefixMap map[string]ObjectType
rootResource prefixedID
roles map[string][]string
}
type prefixedID struct {
id gidx.PrefixedID
}
func (r prefixedID) PrefixedID() gidx.PrefixedID {
return r.id
}
// New creates a new service.
func New(publisher EventPublisher, metal metal.Client, perms permissions.Client, options ...Option) (Service, error) {
svc := &service{
publisher: publisher,
metal: metal,
perms: perms,
idPrefixMap: make(map[string]ObjectType),
}
for _, opt := range options {
if err := opt(svc); err != nil {
return nil, err
}
}
if svc.logger == nil {
svc.logger = zap.NewNop().Sugar()
}
if svc.rootResource.PrefixedID() == gidx.NullPrefixedID {
return nil, ErrRootTenantRequired
}
if svc.idPrefixMap == nil || len(svc.idPrefixMap) == 0 {
svc.idPrefixMap = DefaultPrefixMap
}
if svc.roles == nil {
svc.roles = make(map[string][]string)
}
return svc, nil
}