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, } type ObjectType string func (t ObjectType) Prefix() string { switch t { case TypeOrganization: return "metlorg" case TypeProject: return "metlprj" case TypeUser: return "metlusr" default: return "" } } 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 } var _ Service = &service{} type service struct { logger *zap.SugaredLogger publisher *events.Publisher 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 } func New(publisher *events.Publisher, 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 }