Compare commits

..

1 Commits

Author SHA1 Message Date
94d6777c48 more seed data 2026-03-23 05:24:29 -07:00
4 changed files with 89 additions and 14 deletions

View File

@ -0,0 +1,67 @@
-- Seed TRUE_FALSE and SHORT_ANSWER question types
-- Ensures question sets contain non-MCQ questions for end-to-end testing.
-- ======================================================
-- TRUE_FALSE questions (stored in questions + question_options)
-- ======================================================
INSERT INTO questions (
id,
question_text,
question_type,
difficulty_level,
points,
status,
created_at
)
VALUES
(27, 'The Python interpreter executes Python code top-to-bottom.', 'TRUE_FALSE', 'EASY', 1, 'PUBLISHED', CURRENT_TIMESTAMP)
ON CONFLICT (id) DO NOTHING;
-- question_options for TRUE_FALSE: use two options with exactly one correct
INSERT INTO question_options (question_id, option_text, option_order, is_correct)
VALUES
(27, 'True', 1, TRUE),
(27, 'False', 2, FALSE)
ON CONFLICT DO NOTHING;
-- ======================================================
-- SHORT_ANSWER questions (stored in questions + question_short_answers)
-- ======================================================
INSERT INTO questions (
id,
question_text,
question_type,
difficulty_level,
points,
status,
created_at
)
VALUES
(29, 'What keyword is used in Python to define a function?', 'SHORT_ANSWER', 'EASY', 1, 'PUBLISHED', CURRENT_TIMESTAMP)
ON CONFLICT (id) DO NOTHING;
INSERT INTO question_short_answers (question_id, acceptable_answer, match_type)
VALUES
(29, 'def', 'EXACT')
ON CONFLICT DO NOTHING;
-- ======================================================
-- Link new questions into existing question sets
-- Question Set 1: Initial Assessment (set_id = 1, PUBLISHED)
-- Question Set 2: Python Basics Assessment (set_id = 2, PUBLISHED)
-- ======================================================
INSERT INTO question_set_items (set_id, question_id, display_order)
VALUES
(1, 27, 17),
(1, 29, 18),
(2, 27, 3),
(2, 29, 4)
ON CONFLICT (set_id, question_id) DO NOTHING;
-- ======================================================
-- Reset sequences to avoid ID collisions after seeding
-- ======================================================
SELECT setval(pg_get_serial_sequence('questions', 'id'), COALESCE((SELECT MAX(id) FROM questions), 1), true);
SELECT setval(pg_get_serial_sequence('question_options', 'id'), COALESCE((SELECT MAX(id) FROM question_options), 1), true);
SELECT setval(pg_get_serial_sequence('question_short_answers', 'id'), COALESCE((SELECT MAX(id) FROM question_short_answers), 1), true);

View File

@ -19,6 +19,9 @@ type RBACStore interface {
GetPermissionByKey(ctx context.Context, key string) (domain.Permission, error) GetPermissionByKey(ctx context.Context, key string) (domain.Permission, error)
SetRolePermissions(ctx context.Context, roleID int64, permissionIDs []int64) error SetRolePermissions(ctx context.Context, roleID int64, permissionIDs []int64) error
// AddRolePermissions inserts permissions into role without removing existing ones.
// It is safe to call repeatedly (idempotent) as it relies on ON CONFLICT DO NOTHING.
AddRolePermissions(ctx context.Context, roleID int64, permissionIDs []int64) error
GetRolePermissions(ctx context.Context, roleID int64) ([]domain.Permission, error) GetRolePermissions(ctx context.Context, roleID int64) ([]domain.Permission, error)
GetAllRolesWithPermissions(ctx context.Context) (map[string]map[string]struct{}, error) GetAllRolesWithPermissions(ctx context.Context) (map[string]map[string]struct{}, error)

View File

@ -150,6 +150,17 @@ func (s *Store) SetRolePermissions(ctx context.Context, roleID int64, permission
return nil return nil
} }
func (s *Store) AddRolePermissions(ctx context.Context, roleID int64, permissionIDs []int64) error {
if len(permissionIDs) == 0 {
return nil
}
// Uses ON CONFLICT DO NOTHING at the SQL level (see rbac.sql).
return s.queries.BulkAssignPermissionsToRole(ctx, dbgen.BulkAssignPermissionsToRoleParams{
RoleID: roleID,
Column2: permissionIDs,
})
}
func (s *Store) GetRolePermissions(ctx context.Context, roleID int64) ([]domain.Permission, error) { func (s *Store) GetRolePermissions(ctx context.Context, roleID int64) ([]domain.Permission, error) {
rows, err := s.queries.GetRolePermissions(ctx, roleID) rows, err := s.queries.GetRolePermissions(ctx, roleID)
if err != nil { if err != nil {

View File

@ -76,15 +76,7 @@ func (s *Service) SeedDefaultRolePermissions(ctx context.Context) error {
continue continue
} }
existing, err := s.store.GetRolePermissions(ctx, role.ID) // Insert missing permissions without wiping existing role permissions.
if err != nil {
return fmt.Errorf("check existing permissions for %s: %w", roleName, err)
}
if len(existing) > 0 {
s.logger.Info("role already has permissions, skipping seed", "role", roleName, "count", len(existing))
continue
}
var permIDs []int64 var permIDs []int64
for _, key := range permKeys { for _, key := range permKeys {
perm, err := s.store.GetPermissionByKey(ctx, key) perm, err := s.store.GetPermissionByKey(ctx, key)
@ -95,12 +87,14 @@ func (s *Service) SeedDefaultRolePermissions(ctx context.Context) error {
permIDs = append(permIDs, perm.ID) permIDs = append(permIDs, perm.ID)
} }
if len(permIDs) > 0 { if len(permIDs) == 0 {
if err := s.store.SetRolePermissions(ctx, role.ID, permIDs); err != nil { continue
return fmt.Errorf("seed permissions for role %s: %w", roleName, err)
}
s.logger.Info("seeded default permissions for role", "role", roleName, "count", len(permIDs))
} }
if err := s.store.AddRolePermissions(ctx, role.ID, permIDs); err != nil {
return fmt.Errorf("seed role permissions for %s: %w", roleName, err)
}
s.logger.Info("ensured default permissions for role", "role", roleName, "count", len(permIDs))
} }
return nil return nil
} }