Feat v0.9.4 package adoption + roles
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 21s
CI/CD / test-frontend (pull_request) Has been skipped
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-sqlite (pull_request) Successful in 2m52s
CI/CD / test-go-pg (pull_request) Successful in 3m6s
CI/CD / build-and-deploy (pull_request) Successful in 1m41s
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 21s
CI/CD / test-frontend (pull_request) Has been skipped
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-sqlite (pull_request) Successful in 2m52s
CI/CD / test-go-pg (pull_request) Successful in 3m6s
CI/CD / build-and-deploy (pull_request) Successful in 1m41s
Packages can declare `adoptable: true` in their manifest. When a team adopts an adoptable package, a team-scoped copy is created that references the original via `adopted_from` (shared assets, no disk duplication). The package's `requires_roles` auto-populate into a new `team_role_catalog` table so team admins know which roles to assign. Migration 017: adoptable/adopted_from columns on packages, team_role_catalog table. 4 new endpoints (adopt, list adoptable, unadopt, role catalog). AdoptTeamWorkflow deprecated. 11 new tests, all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -208,6 +208,23 @@ type TeamStore interface {
|
||||
|
||||
// RemoveAllUserRoles deletes all additional roles for a member (cleanup on removal).
|
||||
RemoveAllUserRoles(ctx context.Context, teamID, userID string) error
|
||||
|
||||
// ── v0.9.4 — Team Role Catalog ──
|
||||
|
||||
// AddRoleToCatalog registers a known role for the team. Idempotent.
|
||||
AddRoleToCatalog(ctx context.Context, teamID, role, sourcePkgID string) error
|
||||
|
||||
// ListRoleCatalog returns all known roles for a team.
|
||||
ListRoleCatalog(ctx context.Context, teamID string) ([]TeamRoleCatalogEntry, error)
|
||||
|
||||
// RemoveRoleCatalogBySource removes all catalog entries sourced from a package.
|
||||
RemoveRoleCatalogBySource(ctx context.Context, teamID, sourcePkgID string) error
|
||||
}
|
||||
|
||||
// TeamRoleCatalogEntry represents a known role sourced from an adopted package.
|
||||
type TeamRoleCatalogEntry struct {
|
||||
Role string `json:"role"`
|
||||
SourcePackageID string `json:"source_package_id,omitempty"`
|
||||
}
|
||||
|
||||
// =========================================
|
||||
|
||||
@@ -93,6 +93,14 @@ type PackageStore interface {
|
||||
|
||||
// DeleteTeamSettings removes team-scoped overrides, reverting to global defaults.
|
||||
DeleteTeamSettings(ctx context.Context, pkgID, teamID string) error
|
||||
|
||||
// ── v0.9.4 — Package Adoption ───────────────
|
||||
|
||||
// ListAdoptable returns global, adoptable, enabled packages.
|
||||
ListAdoptable(ctx context.Context) ([]PackageRegistration, error)
|
||||
|
||||
// GetByAdoptedFrom returns a team's adopted copy of a source package, or nil.
|
||||
GetByAdoptedFrom(ctx context.Context, sourceID, teamID string) (*PackageRegistration, error)
|
||||
}
|
||||
|
||||
// PackageRegistration is a row from the packages table.
|
||||
@@ -114,6 +122,8 @@ type PackageRegistration struct {
|
||||
SchemaVersion int `json:"schema_version" db:"schema_version"`
|
||||
PackageSettings json.RawMessage `json:"package_settings" db:"package_settings"`
|
||||
Source string `json:"source" db:"source"`
|
||||
Adoptable bool `json:"adoptable" db:"adoptable"`
|
||||
AdoptedFrom *string `json:"adopted_from,omitempty" db:"adopted_from"`
|
||||
InstalledAt string `json:"installed_at" db:"installed_at"`
|
||||
UpdatedAt string `json:"updated_at" db:"updated_at"`
|
||||
}
|
||||
|
||||
@@ -107,15 +107,15 @@ func (s *PackageStore) Create(ctx context.Context, pkg *store.PackageRegistratio
|
||||
return DB.QueryRowContext(ctx, `
|
||||
INSERT INTO packages (id, title, type, version, description, author, tier,
|
||||
is_system, scope, team_id, installed_by, manifest, enabled, status,
|
||||
schema_version, package_settings, source)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)
|
||||
schema_version, package_settings, source, adoptable, adopted_from)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19)
|
||||
RETURNING installed_at, updated_at`,
|
||||
pkg.ID, pkg.Title, pkg.Type, pkg.Version, pkg.Description, pkg.Author,
|
||||
pkg.Tier, pkg.IsSystem, pkg.Scope,
|
||||
nullStrPtr(pkg.TeamID), nullStrPtr(pkg.InstalledBy),
|
||||
manifestJSON, pkg.Enabled, pkg.Status,
|
||||
pkg.SchemaVersion, defaultJSON(pkg.PackageSettings),
|
||||
pkg.Source,
|
||||
pkg.Source, pkg.Adoptable, nullStrPtr(pkg.AdoptedFrom),
|
||||
).Scan(&pkg.InstalledAt, &pkg.UpdatedAt)
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ func (s *PackageStore) ListForUser(ctx context.Context, userID string) ([]store.
|
||||
var result []store.UserPackage
|
||||
for rows.Next() {
|
||||
var up store.UserPackage
|
||||
var teamID, installedBy sql.NullString
|
||||
var teamID, installedBy, adoptedFrom sql.NullString
|
||||
var manifestJSON []byte
|
||||
var pkgSettings []byte
|
||||
var userEnabled sql.NullBool
|
||||
@@ -185,13 +185,15 @@ func (s *PackageStore) ListForUser(ctx context.Context, userID string) ([]store.
|
||||
&teamID, &installedBy,
|
||||
&manifestJSON, &up.Enabled, &up.Status,
|
||||
&up.SchemaVersion, &pkgSettings,
|
||||
&up.Source, &up.InstalledAt, &up.UpdatedAt,
|
||||
&up.Source, &up.Adoptable, &adoptedFrom,
|
||||
&up.InstalledAt, &up.UpdatedAt,
|
||||
&userEnabled, &userSettings,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
up.TeamID = NullableStringPtr(teamID)
|
||||
up.InstalledBy = NullableStringPtr(installedBy)
|
||||
up.AdoptedFrom = NullableStringPtr(adoptedFrom)
|
||||
json.Unmarshal(manifestJSON, &up.Manifest)
|
||||
up.PackageSettings = json.RawMessage(pkgSettings)
|
||||
if userEnabled.Valid {
|
||||
@@ -250,11 +252,12 @@ const pkgCols = `p.id, p.title, p.type, p.version, p.description, p.author,
|
||||
p.tier, p.is_system, p.scope, p.team_id, p.installed_by,
|
||||
p.manifest, p.enabled, p.status,
|
||||
p.schema_version, p.package_settings,
|
||||
p.source, p.installed_at, p.updated_at`
|
||||
p.source, p.adoptable, p.adopted_from,
|
||||
p.installed_at, p.updated_at`
|
||||
|
||||
func (s *PackageStore) scanOne(ctx context.Context, query string, args ...interface{}) (*store.PackageRegistration, error) {
|
||||
var pkg store.PackageRegistration
|
||||
var teamID, installedBy sql.NullString
|
||||
var teamID, installedBy, adoptedFrom sql.NullString
|
||||
var manifestJSON []byte
|
||||
var pkgSettings []byte
|
||||
err := DB.QueryRowContext(ctx, query, args...).Scan(
|
||||
@@ -263,7 +266,8 @@ func (s *PackageStore) scanOne(ctx context.Context, query string, args ...interf
|
||||
&teamID, &installedBy,
|
||||
&manifestJSON, &pkg.Enabled, &pkg.Status,
|
||||
&pkg.SchemaVersion, &pkgSettings,
|
||||
&pkg.Source, &pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
&pkg.Source, &pkg.Adoptable, &adoptedFrom,
|
||||
&pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
@@ -273,6 +277,7 @@ func (s *PackageStore) scanOne(ctx context.Context, query string, args ...interf
|
||||
}
|
||||
pkg.TeamID = NullableStringPtr(teamID)
|
||||
pkg.InstalledBy = NullableStringPtr(installedBy)
|
||||
pkg.AdoptedFrom = NullableStringPtr(adoptedFrom)
|
||||
json.Unmarshal(manifestJSON, &pkg.Manifest)
|
||||
pkg.PackageSettings = json.RawMessage(pkgSettings)
|
||||
return &pkg, nil
|
||||
@@ -288,7 +293,7 @@ func (s *PackageStore) scanMany(ctx context.Context, query string, args ...inter
|
||||
var result []store.PackageRegistration
|
||||
for rows.Next() {
|
||||
var pkg store.PackageRegistration
|
||||
var teamID, installedBy sql.NullString
|
||||
var teamID, installedBy, adoptedFrom sql.NullString
|
||||
var manifestJSON []byte
|
||||
var pkgSettings []byte
|
||||
if err := rows.Scan(
|
||||
@@ -297,12 +302,14 @@ func (s *PackageStore) scanMany(ctx context.Context, query string, args ...inter
|
||||
&teamID, &installedBy,
|
||||
&manifestJSON, &pkg.Enabled, &pkg.Status,
|
||||
&pkg.SchemaVersion, &pkgSettings,
|
||||
&pkg.Source, &pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
&pkg.Source, &pkg.Adoptable, &adoptedFrom,
|
||||
&pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg.TeamID = NullableStringPtr(teamID)
|
||||
pkg.InstalledBy = NullableStringPtr(installedBy)
|
||||
pkg.AdoptedFrom = NullableStringPtr(adoptedFrom)
|
||||
json.Unmarshal(manifestJSON, &pkg.Manifest)
|
||||
pkg.PackageSettings = json.RawMessage(pkgSettings)
|
||||
result = append(result, pkg)
|
||||
@@ -397,6 +404,23 @@ func (s *PackageStore) DeleteTeamSettings(ctx context.Context, pkgID, teamID str
|
||||
return err
|
||||
}
|
||||
|
||||
// ── v0.9.4 — Package Adoption ───────────────
|
||||
|
||||
func (s *PackageStore) ListAdoptable(ctx context.Context) ([]store.PackageRegistration, error) {
|
||||
return s.scanMany(ctx, `
|
||||
SELECT `+pkgCols+`
|
||||
FROM packages p
|
||||
WHERE p.adoptable = true AND p.scope = 'global' AND p.enabled = true
|
||||
ORDER BY p.title`)
|
||||
}
|
||||
|
||||
func (s *PackageStore) GetByAdoptedFrom(ctx context.Context, sourceID, teamID string) (*store.PackageRegistration, error) {
|
||||
return s.scanOne(ctx, `
|
||||
SELECT `+pkgCols+`
|
||||
FROM packages p
|
||||
WHERE p.adopted_from = $1 AND p.team_id = $2`, sourceID, teamID)
|
||||
}
|
||||
|
||||
// nullStrPtr converts *string to sql.NullString for nullable FK columns.
|
||||
func nullStrPtr(s *string) sql.NullString {
|
||||
if s == nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"armature/models"
|
||||
"armature/store"
|
||||
)
|
||||
|
||||
type TeamStore struct{}
|
||||
@@ -393,3 +394,40 @@ func (s *TeamStore) RemoveAllUserRoles(ctx context.Context, teamID, userID strin
|
||||
teamID, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
// ── v0.9.4 — Team Role Catalog ──
|
||||
|
||||
func (s *TeamStore) AddRoleToCatalog(ctx context.Context, teamID, role, sourcePkgID string) error {
|
||||
_, err := DB.ExecContext(ctx, `
|
||||
INSERT INTO team_role_catalog (id, team_id, role, source_package_id)
|
||||
VALUES (gen_random_uuid(), $1, $2, $3)
|
||||
ON CONFLICT (team_id, role) DO NOTHING`,
|
||||
teamID, role, sourcePkgID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TeamStore) ListRoleCatalog(ctx context.Context, teamID string) ([]store.TeamRoleCatalogEntry, error) {
|
||||
rows, err := DB.QueryContext(ctx, `
|
||||
SELECT role, COALESCE(source_package_id, '') FROM team_role_catalog
|
||||
WHERE team_id = $1 ORDER BY role`, teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var result []store.TeamRoleCatalogEntry
|
||||
for rows.Next() {
|
||||
var e store.TeamRoleCatalogEntry
|
||||
if err := rows.Scan(&e.Role, &e.SourcePackageID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, e)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *TeamStore) RemoveRoleCatalogBySource(ctx context.Context, teamID, sourcePkgID string) error {
|
||||
_, err := DB.ExecContext(ctx, `
|
||||
DELETE FROM team_role_catalog WHERE team_id = $1 AND source_package_id = $2`,
|
||||
teamID, sourcePkgID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -109,18 +109,22 @@ func (s *PackageStore) Create(ctx context.Context, pkg *store.PackageRegistratio
|
||||
if pkg.Status == "" {
|
||||
pkg.Status = "active"
|
||||
}
|
||||
adoptableInt := 0
|
||||
if pkg.Adoptable {
|
||||
adoptableInt = 1
|
||||
}
|
||||
_, err := DB.ExecContext(ctx, `
|
||||
INSERT INTO packages (id, title, type, version, description, author, tier,
|
||||
is_system, scope, team_id, installed_by, manifest, enabled, status,
|
||||
schema_version, package_settings, source,
|
||||
schema_version, package_settings, source, adoptable, adopted_from,
|
||||
installed_at, updated_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
pkg.ID, pkg.Title, pkg.Type, pkg.Version, pkg.Description, pkg.Author,
|
||||
pkg.Tier, pkg.IsSystem, pkg.Scope,
|
||||
nullStrPtr(pkg.TeamID), nullStrPtr(pkg.InstalledBy),
|
||||
manifestJSON, pkg.Enabled, pkg.Status,
|
||||
pkg.SchemaVersion, defaultJSON(pkg.PackageSettings),
|
||||
pkg.Source,
|
||||
pkg.Source, adoptableInt, nullStrPtr(pkg.AdoptedFrom),
|
||||
now.Format(timeFmt), now.Format(timeFmt),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -184,11 +188,12 @@ func (s *PackageStore) ListForUser(ctx context.Context, userID string) ([]store.
|
||||
var result []store.UserPackage
|
||||
for rows.Next() {
|
||||
var up store.UserPackage
|
||||
var teamID, installedBy sql.NullString
|
||||
var teamID, installedBy, adoptedFrom sql.NullString
|
||||
var manifestJSON string
|
||||
var pkgSettings string
|
||||
var enabledInt int
|
||||
var isSystemInt int
|
||||
var adoptableInt int
|
||||
var userEnabled sql.NullBool
|
||||
var userSettings []byte
|
||||
|
||||
@@ -198,15 +203,18 @@ func (s *PackageStore) ListForUser(ctx context.Context, userID string) ([]store.
|
||||
&teamID, &installedBy,
|
||||
&manifestJSON, &enabledInt, &up.Status,
|
||||
&up.SchemaVersion, &pkgSettings,
|
||||
&up.Source, &up.InstalledAt, &up.UpdatedAt,
|
||||
&up.Source, &adoptableInt, &adoptedFrom,
|
||||
&up.InstalledAt, &up.UpdatedAt,
|
||||
&userEnabled, &userSettings,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
up.IsSystem = isSystemInt != 0
|
||||
up.Enabled = enabledInt != 0
|
||||
up.Adoptable = adoptableInt != 0
|
||||
up.TeamID = NullableStringPtr(teamID)
|
||||
up.InstalledBy = NullableStringPtr(installedBy)
|
||||
up.AdoptedFrom = NullableStringPtr(adoptedFrom)
|
||||
json.Unmarshal([]byte(manifestJSON), &up.Manifest)
|
||||
up.PackageSettings = json.RawMessage(pkgSettings)
|
||||
if userEnabled.Valid {
|
||||
@@ -263,22 +271,25 @@ const pkgCols = `p.id, p.title, p.type, p.version, p.description, p.author,
|
||||
p.tier, p.is_system, p.scope, p.team_id, p.installed_by,
|
||||
p.manifest, p.enabled, p.status,
|
||||
p.schema_version, p.package_settings,
|
||||
p.source, p.installed_at, p.updated_at`
|
||||
p.source, p.adoptable, p.adopted_from,
|
||||
p.installed_at, p.updated_at`
|
||||
|
||||
func (s *PackageStore) scanOne(ctx context.Context, query string, args ...interface{}) (*store.PackageRegistration, error) {
|
||||
var pkg store.PackageRegistration
|
||||
var teamID, installedBy sql.NullString
|
||||
var teamID, installedBy, adoptedFrom sql.NullString
|
||||
var manifestJSON string
|
||||
var pkgSettings string
|
||||
var enabledInt int
|
||||
var isSystemInt int
|
||||
var adoptableInt int
|
||||
err := DB.QueryRowContext(ctx, query, args...).Scan(
|
||||
&pkg.ID, &pkg.Title, &pkg.Type, &pkg.Version, &pkg.Description,
|
||||
&pkg.Author, &pkg.Tier, &isSystemInt, &pkg.Scope,
|
||||
&teamID, &installedBy,
|
||||
&manifestJSON, &enabledInt, &pkg.Status,
|
||||
&pkg.SchemaVersion, &pkgSettings,
|
||||
&pkg.Source, &pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
&pkg.Source, &adoptableInt, &adoptedFrom,
|
||||
&pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
@@ -288,8 +299,10 @@ func (s *PackageStore) scanOne(ctx context.Context, query string, args ...interf
|
||||
}
|
||||
pkg.IsSystem = isSystemInt != 0
|
||||
pkg.Enabled = enabledInt != 0
|
||||
pkg.Adoptable = adoptableInt != 0
|
||||
pkg.TeamID = NullableStringPtr(teamID)
|
||||
pkg.InstalledBy = NullableStringPtr(installedBy)
|
||||
pkg.AdoptedFrom = NullableStringPtr(adoptedFrom)
|
||||
json.Unmarshal([]byte(manifestJSON), &pkg.Manifest)
|
||||
pkg.PackageSettings = json.RawMessage(pkgSettings)
|
||||
return &pkg, nil
|
||||
@@ -305,25 +318,29 @@ func (s *PackageStore) scanMany(ctx context.Context, query string, args ...inter
|
||||
var result []store.PackageRegistration
|
||||
for rows.Next() {
|
||||
var pkg store.PackageRegistration
|
||||
var teamID, installedBy sql.NullString
|
||||
var teamID, installedBy, adoptedFrom sql.NullString
|
||||
var manifestJSON string
|
||||
var pkgSettings string
|
||||
var enabledInt int
|
||||
var isSystemInt int
|
||||
var adoptableInt int
|
||||
if err := rows.Scan(
|
||||
&pkg.ID, &pkg.Title, &pkg.Type, &pkg.Version, &pkg.Description,
|
||||
&pkg.Author, &pkg.Tier, &isSystemInt, &pkg.Scope,
|
||||
&teamID, &installedBy,
|
||||
&manifestJSON, &enabledInt, &pkg.Status,
|
||||
&pkg.SchemaVersion, &pkgSettings,
|
||||
&pkg.Source, &pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
&pkg.Source, &adoptableInt, &adoptedFrom,
|
||||
&pkg.InstalledAt, &pkg.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg.IsSystem = isSystemInt != 0
|
||||
pkg.Enabled = enabledInt != 0
|
||||
pkg.Adoptable = adoptableInt != 0
|
||||
pkg.TeamID = NullableStringPtr(teamID)
|
||||
pkg.InstalledBy = NullableStringPtr(installedBy)
|
||||
pkg.AdoptedFrom = NullableStringPtr(adoptedFrom)
|
||||
json.Unmarshal([]byte(manifestJSON), &pkg.Manifest)
|
||||
pkg.PackageSettings = json.RawMessage(pkgSettings)
|
||||
result = append(result, pkg)
|
||||
@@ -418,6 +435,23 @@ func (s *PackageStore) DeleteTeamSettings(ctx context.Context, pkgID, teamID str
|
||||
return err
|
||||
}
|
||||
|
||||
// ── v0.9.4 — Package Adoption ───────────────
|
||||
|
||||
func (s *PackageStore) ListAdoptable(ctx context.Context) ([]store.PackageRegistration, error) {
|
||||
return s.scanMany(ctx, `
|
||||
SELECT `+pkgCols+`
|
||||
FROM packages p
|
||||
WHERE p.adoptable = 1 AND p.scope = 'global' AND p.enabled = 1
|
||||
ORDER BY p.title`)
|
||||
}
|
||||
|
||||
func (s *PackageStore) GetByAdoptedFrom(ctx context.Context, sourceID, teamID string) (*store.PackageRegistration, error) {
|
||||
return s.scanOne(ctx, `
|
||||
SELECT `+pkgCols+`
|
||||
FROM packages p
|
||||
WHERE p.adopted_from = ? AND p.team_id = ?`, sourceID, teamID)
|
||||
}
|
||||
|
||||
func nullStrPtr(s *string) sql.NullString {
|
||||
if s == nil {
|
||||
return sql.NullString{}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
"armature/models"
|
||||
"armature/store"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type TeamStore struct{}
|
||||
@@ -399,3 +401,40 @@ func (s *TeamStore) RemoveAllUserRoles(ctx context.Context, teamID, userID strin
|
||||
teamID, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
// ── v0.9.4 — Team Role Catalog ──
|
||||
|
||||
func (s *TeamStore) AddRoleToCatalog(ctx context.Context, teamID, role, sourcePkgID string) error {
|
||||
_, err := DB.ExecContext(ctx, `
|
||||
INSERT INTO team_role_catalog (id, team_id, role, source_package_id)
|
||||
VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT (team_id, role) DO NOTHING`,
|
||||
uuid.New().String(), teamID, role, sourcePkgID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TeamStore) ListRoleCatalog(ctx context.Context, teamID string) ([]store.TeamRoleCatalogEntry, error) {
|
||||
rows, err := DB.QueryContext(ctx, `
|
||||
SELECT role, COALESCE(source_package_id, '') FROM team_role_catalog
|
||||
WHERE team_id = ? ORDER BY role`, teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var result []store.TeamRoleCatalogEntry
|
||||
for rows.Next() {
|
||||
var e store.TeamRoleCatalogEntry
|
||||
if err := rows.Scan(&e.Role, &e.SourcePackageID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, e)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *TeamStore) RemoveRoleCatalogBySource(ctx context.Context, teamID, sourcePkgID string) error {
|
||||
_, err := DB.ExecContext(ctx, `
|
||||
DELETE FROM team_role_catalog WHERE team_id = ? AND source_package_id = ?`,
|
||||
teamID, sourcePkgID)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user