Compare commits
1 Commits
main
...
production
| Author | SHA1 | Date | |
|---|---|---|---|
| 94d6777c48 |
67
db/data/009_question_types_seed.sql
Normal file
67
db/data/009_question_types_seed.sql
Normal 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);
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user