package store import ( "context" "database/sql" "errors" "strings" "atlas9.dev/c/core" "atlas9.dev/c/core/dbi" "atlas9.dev/c/demo/lib/ents" "atlas9.dev/c/demo/lib/access" ) // wrapQuotaErr translates the error raised by a quota_check trigger into // a *ents.QuotaExceededError carrying the quota name. All other errors are returned unchanged. func wrapQuotaErr(err error) error { const prefix = "quota exceeded: " if err != nil && strings.HasPrefix(err.Error(), prefix) { return &ents.QuotaExceededError{Name: strings.TrimPrefix(err.Error(), prefix)} } return err } type SqliteEntitlementsStore struct { db dbi.DBI guard access.Guard } var _ ents.Store = (*SqliteEntitlementsStore)(nil) func NewSqliteEntitlementsStore(db dbi.DBI, guard access.Guard) *SqliteEntitlementsStore { return &SqliteEntitlementsStore{db: db, guard: guard} } const entitlementsCols = ` sso_used, sso_max, tenants_used, tenants_max, members_used, members_max, roles_used, roles_max, groups_used, groups_max, invitations_used, invitations_max, todo_lists_used, todo_lists_max, todo_items_used, todo_items_max, audit_log, bots_used, bots_max, bot_keys_used, bot_keys_max, domains_used, domains_max, webhooks_used, webhooks_max, slack_used, slack_max` func scanEntitlements(row interface{ Scan(...any) error }, out *ents.Entitlements) error { return row.Scan( &out.Sso.Used, &out.Sso.Max, &out.Tenants.Used, &out.Tenants.Max, &out.Members.Used, &out.Members.Max, &out.Roles.Used, &out.Roles.Max, &out.Groups.Used, &out.Groups.Max, &out.Invitations.Used, &out.Invitations.Max, &out.TodoLists.Used, &out.TodoLists.Max, &out.TodoItems.Used, &out.TodoItems.Max, &out.AuditLog, &out.Bots.Used, &out.Bots.Max, &out.BotKeys.Used, &out.BotKeys.Max, &out.Domains.Used, &out.Domains.Max, &out.Webhooks.Used, &out.Webhooks.Max, &out.Slack.Used, &out.Slack.Max, ) } func (s *SqliteEntitlementsStore) Get(ctx context.Context, tenant core.ID, out *ents.Entitlements) error { row := s.db.QueryRow(ctx, `SELECT `+entitlementsCols+` FROM entitlements WHERE tenant = $1`, tenant) err := scanEntitlements(row, out) if errors.Is(err, sql.ErrNoRows) { *out = ents.Entitlements{} return nil } return err } func (s *SqliteEntitlementsStore) Create(ctx context.Context, tenant core.ID, e *ents.Entitlements) error { if err := s.guard.Check(ctx, ents.Cap_Entitlements_Write, tenant, ""); err != nil { return err } _, err := s.db.Exec(ctx, ` INSERT INTO entitlements ( tenant, sso_used, sso_max, tenants_used, tenants_max, members_used, members_max, roles_used, roles_max, groups_used, groups_max, invitations_used, invitations_max, todo_lists_used, todo_lists_max, todo_items_used, todo_items_max, audit_log, bots_used, bots_max, bot_keys_used, bot_keys_max, domains_used, domains_max, webhooks_used, webhooks_max, slack_used, slack_max ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28 )`, tenant, e.Sso.Used, e.Sso.Max, e.Tenants.Used, e.Tenants.Max, e.Members.Used, e.Members.Max, e.Roles.Used, e.Roles.Max, e.Groups.Used, e.Groups.Max, e.Invitations.Used, e.Invitations.Max, e.TodoLists.Used, e.TodoLists.Max, e.TodoItems.Used, e.TodoItems.Max, e.AuditLog, e.Bots.Used, e.Bots.Max, e.BotKeys.Used, e.BotKeys.Max, e.Domains.Used, e.Domains.Max, e.Webhooks.Used, e.Webhooks.Max, e.Slack.Used, e.Slack.Max, ) return err } func (s *SqliteEntitlementsStore) Update(ctx context.Context, tenant core.ID, e *ents.Entitlements) error { if err := s.guard.Check(ctx, ents.Cap_Entitlements_Write, tenant, ""); err != nil { return err } res, err := s.db.Exec(ctx, ` UPDATE entitlements SET sso_used = $1, sso_max = $2, tenants_used = $3, tenants_max = $4, members_used = $5, members_max = $6, roles_used = $7, roles_max = $8, groups_used = $9, groups_max = $10, invitations_used = $11, invitations_max = $12, todo_lists_used = $13, todo_lists_max = $14, todo_items_used = $15, todo_items_max = $16, audit_log = $17, bots_used = $18, bots_max = $19, bot_keys_used = $20, bot_keys_max = $21, domains_used = $22, domains_max = $23, webhooks_used = $24, webhooks_max = $25, slack_used = $26, slack_max = $27 WHERE tenant = $28`, e.Sso.Used, e.Sso.Max, e.Tenants.Used, e.Tenants.Max, e.Members.Used, e.Members.Max, e.Roles.Used, e.Roles.Max, e.Groups.Used, e.Groups.Max, e.Invitations.Used, e.Invitations.Max, e.TodoLists.Used, e.TodoLists.Max, e.TodoItems.Used, e.TodoItems.Max, e.AuditLog, e.Bots.Used, e.Bots.Max, e.BotKeys.Used, e.BotKeys.Max, e.Domains.Used, e.Domains.Max, e.Webhooks.Used, e.Webhooks.Max, e.Slack.Used, e.Slack.Max, tenant, ) if err != nil { return err } n, err := res.RowsAffected() if err != nil { return err } if n == 0 { return core.ErrNotFound } return nil }