From e983e68ed6f4aacae847fd2c385f09705d97c763 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Fri, 24 Feb 2023 15:37:00 +0100 Subject: [PATCH] add endpoints for listing requirements. --- cmd/stufflog3/main.go | 2 + ports/httpapi/requirements.go | 18 ++++ ports/mysql/mysqlcore/db.go | 20 ++++ ports/mysql/mysqlcore/projects.sql.go | 144 ++++++++++++++++++++++---- ports/mysql/projects.go | 20 +++- ports/mysql/queries/projects.sql | 87 ++++++++++++---- usecases/projects/repository.go | 1 + usecases/projects/result.go | 6 +- usecases/projects/service.go | 42 ++++++++ 9 files changed, 292 insertions(+), 48 deletions(-) create mode 100644 ports/httpapi/requirements.go diff --git a/cmd/stufflog3/main.go b/cmd/stufflog3/main.go index 63efefb..cf9f77c 100644 --- a/cmd/stufflog3/main.go +++ b/cmd/stufflog3/main.go @@ -101,6 +101,7 @@ func main() { apiV1ScopesSub := apiV1Scopes.Group("/:scope_id") apiV1ScopesSub.Use(httpapi.ScopeMiddleware(scopesService, authService)) httpapi.Projects(apiV1ScopesSub.Group("/projects"), projectsService) + httpapi.Requirements(apiV1ScopesSub.Group("/requirements"), projectsService) httpapi.Items(apiV1ScopesSub.Group("/items"), itemsService) httpapi.Sprints(apiV1ScopesSub.Group("/sprints"), sprintsService) httpapi.Stats(apiV1ScopesSub.Group("/stats"), statsService) @@ -108,6 +109,7 @@ func main() { apiV1AllScopes.Use(httpapi.AllScopeMiddleware(scopesService, authService)) httpapi.ItemsAllScopes(apiV1AllScopes.Group("/items"), itemsService) httpapi.SprintsAllScopes(apiV1AllScopes.Group("/sprints"), sprintsService) + httpapi.RequirementsAllScopes(apiV1AllScopes.Group("/requirements"), projectsService) if *flagLocal { exitSignal := make(chan os.Signal) diff --git a/ports/httpapi/requirements.go b/ports/httpapi/requirements.go new file mode 100644 index 0000000..153a776 --- /dev/null +++ b/ports/httpapi/requirements.go @@ -0,0 +1,18 @@ +package httpapi + +import ( + "git.aiterp.net/stufflog3/stufflog3/usecases/projects" + "github.com/gin-gonic/gin" +) + +func RequirementsAllScopes(g *gin.RouterGroup, service *projects.Service) { + g.GET("", handler("requirements", func(c *gin.Context) (interface{}, error) { + return service.ListRequirementAllScopes(c.Request.Context()) + })) +} + +func Requirements(g *gin.RouterGroup, service *projects.Service) { + g.GET("", handler("requirements", func(c *gin.Context) (interface{}, error) { + return service.ListRequirements(c.Request.Context()) + })) +} diff --git a/ports/mysql/mysqlcore/db.go b/ports/mysql/mysqlcore/db.go index f96dd5e..842b31e 100644 --- a/ports/mysql/mysqlcore/db.go +++ b/ports/mysql/mysqlcore/db.go @@ -180,9 +180,15 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) { if q.listProjectRequirementsStmt, err = db.PrepareContext(ctx, listProjectRequirements); err != nil { return nil, fmt.Errorf("error preparing query ListProjectRequirements: %w", err) } + if q.listProjectRequirementsByScopeIDStmt, err = db.PrepareContext(ctx, listProjectRequirementsByScopeID); err != nil { + return nil, fmt.Errorf("error preparing query ListProjectRequirementsByScopeID: %w", err) + } if q.listProjectRequirementsStatsStmt, err = db.PrepareContext(ctx, listProjectRequirementsStats); err != nil { return nil, fmt.Errorf("error preparing query ListProjectRequirementsStats: %w", err) } + if q.listProjectRequirementsStatsByScopeIDStmt, err = db.PrepareContext(ctx, listProjectRequirementsStatsByScopeID); err != nil { + return nil, fmt.Errorf("error preparing query ListProjectRequirementsStatsByScopeID: %w", err) + } if q.listProjectsStmt, err = db.PrepareContext(ctx, listProjects); err != nil { return nil, fmt.Errorf("error preparing query ListProjects: %w", err) } @@ -508,11 +514,21 @@ func (q *Queries) Close() error { err = fmt.Errorf("error closing listProjectRequirementsStmt: %w", cerr) } } + if q.listProjectRequirementsByScopeIDStmt != nil { + if cerr := q.listProjectRequirementsByScopeIDStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing listProjectRequirementsByScopeIDStmt: %w", cerr) + } + } if q.listProjectRequirementsStatsStmt != nil { if cerr := q.listProjectRequirementsStatsStmt.Close(); cerr != nil { err = fmt.Errorf("error closing listProjectRequirementsStatsStmt: %w", cerr) } } + if q.listProjectRequirementsStatsByScopeIDStmt != nil { + if cerr := q.listProjectRequirementsStatsByScopeIDStmt.Close(); cerr != nil { + err = fmt.Errorf("error closing listProjectRequirementsStatsByScopeIDStmt: %w", cerr) + } + } if q.listProjectsStmt != nil { if cerr := q.listProjectsStmt.Close(); cerr != nil { err = fmt.Errorf("error closing listProjectsStmt: %w", cerr) @@ -704,7 +720,9 @@ type Queries struct { listItemsScheduledBetweenStmt *sql.Stmt listItemsScheduledBetweenNoScopeStmt *sql.Stmt listProjectRequirementsStmt *sql.Stmt + listProjectRequirementsByScopeIDStmt *sql.Stmt listProjectRequirementsStatsStmt *sql.Stmt + listProjectRequirementsStatsByScopeIDStmt *sql.Stmt listProjectsStmt *sql.Stmt listScopeMembersStmt *sql.Stmt listScopeMembersMultiStmt *sql.Stmt @@ -783,7 +801,9 @@ func (q *Queries) WithTx(tx *sql.Tx) *Queries { listItemsScheduledBetweenStmt: q.listItemsScheduledBetweenStmt, listItemsScheduledBetweenNoScopeStmt: q.listItemsScheduledBetweenNoScopeStmt, listProjectRequirementsStmt: q.listProjectRequirementsStmt, + listProjectRequirementsByScopeIDStmt: q.listProjectRequirementsByScopeIDStmt, listProjectRequirementsStatsStmt: q.listProjectRequirementsStatsStmt, + listProjectRequirementsStatsByScopeIDStmt: q.listProjectRequirementsStatsByScopeIDStmt, listProjectsStmt: q.listProjectsStmt, listScopeMembersStmt: q.listScopeMembersStmt, listScopeMembersMultiStmt: q.listScopeMembersMultiStmt, diff --git a/ports/mysql/mysqlcore/projects.sql.go b/ports/mysql/mysqlcore/projects.sql.go index c3b9c3e..3a5e403 100644 --- a/ports/mysql/mysqlcore/projects.sql.go +++ b/ports/mysql/mysqlcore/projects.sql.go @@ -22,7 +22,9 @@ func (q *Queries) ClearItemProjectRequirement(ctx context.Context, projectRequir } const clearProjectRequirementStatsByStat = `-- name: ClearProjectRequirementStatsByStat :exec -DELETE FROM project_requirement_stat WHERE stat_id = ? +DELETE +FROM project_requirement_stat +WHERE stat_id = ? ` func (q *Queries) ClearProjectRequirementStatsByStat(ctx context.Context, statID int) error { @@ -31,7 +33,9 @@ func (q *Queries) ClearProjectRequirementStatsByStat(ctx context.Context, statID } const deleteAllProjectRequirementStats = `-- name: DeleteAllProjectRequirementStats :exec -DELETE FROM project_requirement_stat WHERE project_requirement_id = ? +DELETE +FROM project_requirement_stat +WHERE project_requirement_id = ? ` func (q *Queries) DeleteAllProjectRequirementStats(ctx context.Context, projectRequirementID int) error { @@ -40,7 +44,9 @@ func (q *Queries) DeleteAllProjectRequirementStats(ctx context.Context, projectR } const deleteAllProjectRequirements = `-- name: DeleteAllProjectRequirements :exec -DELETE FROM project_requirement WHERE project_id = ? +DELETE +FROM project_requirement +WHERE project_id = ? ` func (q *Queries) DeleteAllProjectRequirements(ctx context.Context, projectID int) error { @@ -49,7 +55,9 @@ func (q *Queries) DeleteAllProjectRequirements(ctx context.Context, projectID in } const deleteAllScopeProjectRequirements = `-- name: DeleteAllScopeProjectRequirements :exec -DELETE FROM project_requirement WHERE scope_id = ? +DELETE +FROM project_requirement +WHERE scope_id = ? ` func (q *Queries) DeleteAllScopeProjectRequirements(ctx context.Context, scopeID int) error { @@ -58,7 +66,9 @@ func (q *Queries) DeleteAllScopeProjectRequirements(ctx context.Context, scopeID } const deleteAllScopeProjects = `-- name: DeleteAllScopeProjects :exec -DELETE FROM project WHERE scope_id = ? +DELETE +FROM project +WHERE scope_id = ? ` func (q *Queries) DeleteAllScopeProjects(ctx context.Context, scopeID int) error { @@ -67,7 +77,10 @@ func (q *Queries) DeleteAllScopeProjects(ctx context.Context, scopeID int) error } const deleteProject = `-- name: DeleteProject :exec -DELETE FROM project WHERE id = ? AND scope_id = ? +DELETE +FROM project +WHERE id = ? + AND scope_id = ? ` type DeleteProjectParams struct { @@ -81,7 +94,10 @@ func (q *Queries) DeleteProject(ctx context.Context, arg DeleteProjectParams) er } const deleteProjectRequirement = `-- name: DeleteProjectRequirement :exec -DELETE FROM project_requirement WHERE id = ? AND scope_id = ? +DELETE +FROM project_requirement +WHERE id = ? + AND scope_id = ? ` type DeleteProjectRequirementParams struct { @@ -95,7 +111,10 @@ func (q *Queries) DeleteProjectRequirement(ctx context.Context, arg DeleteProjec } const deleteProjectRequirementStat = `-- name: DeleteProjectRequirementStat :exec -DELETE FROM project_requirement_stat WHERE project_requirement_id = ? AND stat_id = ? +DELETE +FROM project_requirement_stat +WHERE project_requirement_id = ? + AND stat_id = ? ` type DeleteProjectRequirementStatParams struct { @@ -109,7 +128,10 @@ func (q *Queries) DeleteProjectRequirementStat(ctx context.Context, arg DeletePr } const getProject = `-- name: GetProject :one -SELECT id, scope_id, owner_id, name, status, description, created_time FROM project WHERE id = ? AND scope_id = ? +SELECT id, scope_id, owner_id, name, status, description, created_time +FROM project +WHERE id = ? + AND scope_id = ? ` type GetProjectParams struct { @@ -183,7 +205,9 @@ func (q *Queries) InsertProjectRequirement(ctx context.Context, arg InsertProjec } const listProjectRequirements = `-- name: ListProjectRequirements :many -SELECT id, scope_id, project_id, name, status, description, is_coarse, aggregate_required FROM project_requirement WHERE project_id = ? +SELECT id, scope_id, project_id, name, status, description, is_coarse, aggregate_required +FROM project_requirement +WHERE project_id = ? ` func (q *Queries) ListProjectRequirements(ctx context.Context, projectID int) ([]ProjectRequirement, error) { @@ -218,9 +242,48 @@ func (q *Queries) ListProjectRequirements(ctx context.Context, projectID int) ([ return items, nil } +const listProjectRequirementsByScopeID = `-- name: ListProjectRequirementsByScopeID :many +SELECT id, scope_id, project_id, name, status, description, is_coarse, aggregate_required +FROM project_requirement +WHERE scope_id = ? +` + +func (q *Queries) ListProjectRequirementsByScopeID(ctx context.Context, scopeID int) ([]ProjectRequirement, error) { + rows, err := q.query(ctx, q.listProjectRequirementsByScopeIDStmt, listProjectRequirementsByScopeID, scopeID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ProjectRequirement{} + for rows.Next() { + var i ProjectRequirement + if err := rows.Scan( + &i.ID, + &i.ScopeID, + &i.ProjectID, + &i.Name, + &i.Status, + &i.Description, + &i.IsCoarse, + &i.AggregateRequired, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listProjectRequirementsStats = `-- name: ListProjectRequirementsStats :many -SELECT prs.project_requirement_id, prs.stat_id, prs.required FROM project_requirement pr -RIGHT JOIN project_requirement_stat prs ON pr.id = prs.project_requirement_id +SELECT prs.project_requirement_id, prs.stat_id, prs.required +FROM project_requirement pr + RIGHT JOIN project_requirement_stat prs ON pr.id = prs.project_requirement_id WHERE pr.project_id = ? ` @@ -247,8 +310,41 @@ func (q *Queries) ListProjectRequirementsStats(ctx context.Context, projectID in return items, nil } +const listProjectRequirementsStatsByScopeID = `-- name: ListProjectRequirementsStatsByScopeID :many +SELECT prs.project_requirement_id, prs.stat_id, prs.required +FROM project_requirement pr + RIGHT JOIN project_requirement_stat prs ON pr.id = prs.project_requirement_id +WHERE pr.scope_id = ? +` + +func (q *Queries) ListProjectRequirementsStatsByScopeID(ctx context.Context, scopeID int) ([]ProjectRequirementStat, error) { + rows, err := q.query(ctx, q.listProjectRequirementsStatsByScopeIDStmt, listProjectRequirementsStatsByScopeID, scopeID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ProjectRequirementStat{} + for rows.Next() { + var i ProjectRequirementStat + if err := rows.Scan(&i.ProjectRequirementID, &i.StatID, &i.Required); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listProjects = `-- name: ListProjects :many -SELECT id, scope_id, owner_id, name, status, description, created_time FROM project WHERE scope_id = ? ORDER BY status, name +SELECT id, scope_id, owner_id, name, status, description, created_time +FROM project +WHERE scope_id = ? +ORDER BY status, name ` func (q *Queries) ListProjects(ctx context.Context, scopeID int) ([]Project, error) { @@ -300,11 +396,12 @@ func (q *Queries) ReplaceProjectRequirementStat(ctx context.Context, arg Replace const updateProject = `-- name: UpdateProject :exec UPDATE project -SET owner_id = ?, - name = ?, - status = ?, +SET owner_id = ?, + name = ?, + status = ?, description = ? -WHERE id = ? AND scope_id = ? +WHERE id = ? + AND scope_id = ? ` type UpdateProjectParams struct { @@ -330,13 +427,14 @@ func (q *Queries) UpdateProject(ctx context.Context, arg UpdateProjectParams) er const updateProjectRequirement = `-- name: UpdateProjectRequirement :exec UPDATE project_requirement -SET name = ?, - status = ?, - description = ?, - is_coarse = ?, +SET name = ?, + status = ?, + description = ?, + is_coarse = ?, aggregate_required = ?, - project_id = ? -WHERE id = ? AND scope_id = ? + project_id = ? +WHERE id = ? + AND scope_id = ? ` type UpdateProjectRequirementParams struct { diff --git a/ports/mysql/projects.go b/ports/mysql/projects.go index 0b4a63a..ef3130e 100644 --- a/ports/mysql/projects.go +++ b/ports/mysql/projects.go @@ -422,6 +422,20 @@ func (r *projectRepository) FetchRequirements(ctx context.Context, scopeID int, return requirements, stats, nil } +func (r *projectRepository) ListRequirementsByScope(ctx context.Context, scopeID int) ([]entities.Requirement, []entities.RequirementStat, error) { + reqRows, err := r.q.ListProjectRequirementsByScopeID(ctx, scopeID) + if err != nil && err != sql.ErrNoRows { + return nil, nil, err + } + + statsRows, err := r.q.ListProjectRequirementsStatsByScopeID(ctx, scopeID) + if err != nil && err != sql.ErrNoRows { + return nil, nil, err + } + + return r.fillRequirements(ctx, reqRows, statsRows) +} + func (r *projectRepository) ListRequirements(ctx context.Context, projectID int) ([]entities.Requirement, []entities.RequirementStat, error) { reqRows, err := r.q.ListProjectRequirements(ctx, projectID) if err != nil && err != sql.ErrNoRows { @@ -433,6 +447,10 @@ func (r *projectRepository) ListRequirements(ctx context.Context, projectID int) return nil, nil, err } + return r.fillRequirements(ctx, reqRows, statsRows) +} + +func (r *projectRepository) fillRequirements(ctx context.Context, reqRows []mysqlcore.ProjectRequirement, statsRows []mysqlcore.ProjectRequirementStat) ([]entities.Requirement, []entities.RequirementStat, error) { requirements := make([]entities.Requirement, 0, len(reqRows)) ids := make([]int, 0, len(reqRows)) for _, row := range reqRows { @@ -451,7 +469,7 @@ func (r *projectRepository) ListRequirements(ctx context.Context, projectID int) } // Fill tags - err = fetchTags(ctx, r.db, tagObjectKindRequirement, ids, func(id int, tag string) { + err := fetchTags(ctx, r.db, tagObjectKindRequirement, ids, func(id int, tag string) { for i := range requirements { if id == requirements[i].ID { requirements[i].Tags = append(requirements[i].Tags, tag) diff --git a/ports/mysql/queries/projects.sql b/ports/mysql/queries/projects.sql index eb61f90..a05cce9 100644 --- a/ports/mysql/queries/projects.sql +++ b/ports/mysql/queries/projects.sql @@ -1,8 +1,14 @@ -- name: ListProjects :many -SELECT * FROM project WHERE scope_id = ? ORDER BY status, name; +SELECT * +FROM project +WHERE scope_id = ? +ORDER BY status, name; -- name: GetProject :one -SELECT * FROM project WHERE id = ? AND scope_id = ?; +SELECT * +FROM project +WHERE id = ? + AND scope_id = ?; -- name: InsertProject :execresult INSERT INTO project (scope_id, owner_id, name, status, description) @@ -10,17 +16,28 @@ VALUES (?, ?, ?, ?, ?); -- name: UpdateProject :exec UPDATE project -SET owner_id = ?, - name = ?, - status = ?, +SET owner_id = ?, + name = ?, + status = ?, description = ? -WHERE id = ? AND scope_id = ?; +WHERE id = ? + AND scope_id = ?; -- name: DeleteProject :exec -DELETE FROM project WHERE id = ? AND scope_id = ?; +DELETE +FROM project +WHERE id = ? + AND scope_id = ?; -- name: ListProjectRequirements :many -SELECT * FROM project_requirement WHERE project_id = ?; +SELECT * +FROM project_requirement +WHERE project_id = ?; + +-- name: ListProjectRequirementsByScopeID :many +SELECT * +FROM project_requirement +WHERE scope_id = ?; -- name: InsertProjectRequirement :execresult INSERT INTO project_requirement (scope_id, project_id, name, status, description, is_coarse, aggregate_required) @@ -28,19 +45,25 @@ VALUES (?, ?, ?, ?, ?, ?, ?); -- name: UpdateProjectRequirement :exec UPDATE project_requirement -SET name = ?, - status = ?, - description = ?, - is_coarse = ?, +SET name = ?, + status = ?, + description = ?, + is_coarse = ?, aggregate_required = ?, - project_id = ? -WHERE id = ? AND scope_id = ?; + project_id = ? +WHERE id = ? + AND scope_id = ?; -- name: DeleteProjectRequirement :exec -DELETE FROM project_requirement WHERE id = ? AND scope_id = ?; +DELETE +FROM project_requirement +WHERE id = ? + AND scope_id = ?; -- name: DeleteAllProjectRequirements :exec -DELETE FROM project_requirement WHERE project_id = ?; +DELETE +FROM project_requirement +WHERE project_id = ?; -- name: ClearItemProjectRequirement :exec UPDATE item @@ -48,25 +71,43 @@ SET project_requirement_id = NULL WHERE project_requirement_id = ?; -- name: ListProjectRequirementsStats :many -SELECT prs.* FROM project_requirement pr -RIGHT JOIN project_requirement_stat prs ON pr.id = prs.project_requirement_id +SELECT prs.* +FROM project_requirement pr + RIGHT JOIN project_requirement_stat prs ON pr.id = prs.project_requirement_id WHERE pr.project_id = ?; +-- name: ListProjectRequirementsStatsByScopeID :many +SELECT prs.* +FROM project_requirement pr + RIGHT JOIN project_requirement_stat prs ON pr.id = prs.project_requirement_id +WHERE pr.scope_id = ?; + -- name: ReplaceProjectRequirementStat :exec REPLACE INTO project_requirement_stat (project_requirement_id, stat_id, required) VALUES (?, ?, ?); -- name: DeleteProjectRequirementStat :exec -DELETE FROM project_requirement_stat WHERE project_requirement_id = ? AND stat_id = ?; +DELETE +FROM project_requirement_stat +WHERE project_requirement_id = ? + AND stat_id = ?; -- name: DeleteAllProjectRequirementStats :exec -DELETE FROM project_requirement_stat WHERE project_requirement_id = ?; +DELETE +FROM project_requirement_stat +WHERE project_requirement_id = ?; -- name: ClearProjectRequirementStatsByStat :exec -DELETE FROM project_requirement_stat WHERE stat_id = ?; +DELETE +FROM project_requirement_stat +WHERE stat_id = ?; -- name: DeleteAllScopeProjects :exec -DELETE FROM project WHERE scope_id = ?; +DELETE +FROM project +WHERE scope_id = ?; -- name: DeleteAllScopeProjectRequirements :exec -DELETE FROM project_requirement WHERE scope_id = ?; \ No newline at end of file +DELETE +FROM project_requirement +WHERE scope_id = ?; \ No newline at end of file diff --git a/usecases/projects/repository.go b/usecases/projects/repository.go index 9edca7f..310a3b3 100644 --- a/usecases/projects/repository.go +++ b/usecases/projects/repository.go @@ -15,6 +15,7 @@ type Repository interface { Update(ctx context.Context, project entities.Project, update models.ProjectUpdate) error Delete(ctx context.Context, project entities.Project) error FetchRequirements(ctx context.Context, scopeID int, requirementIDs ...int) ([]entities.Requirement, []entities.RequirementStat, error) + ListRequirementsByScope(ctx context.Context, scopeID int) ([]entities.Requirement, []entities.RequirementStat, error) ListRequirements(ctx context.Context, projectID int) ([]entities.Requirement, []entities.RequirementStat, error) CreateRequirement(ctx context.Context, requirement entities.Requirement) (*entities.Requirement, error) UpdateRequirement(ctx context.Context, requirement entities.Requirement, update models.RequirementUpdate) error diff --git a/usecases/projects/result.go b/usecases/projects/result.go index 26a6202..aa1ada7 100644 --- a/usecases/projects/result.go +++ b/usecases/projects/result.go @@ -66,7 +66,7 @@ type RequirementResult struct { IsCoarse bool `json:"isCoarse"` AggregateRequired int `json:"aggregateRequired"` Stats []RequirementResultStat `json:"stats"` - Items []items.Result `json:"items"` + Items []items.Result `json:"items,omitempty"` Tags []string `json:"tags"` Requirement entities.Requirement `json:"-"` @@ -283,5 +283,9 @@ func generateRequirementResult(req entities.Requirement, scope scopes.Result, re } } + if projectItems == nil { + resReq.Items = nil + } + return resReq } diff --git a/usecases/projects/service.go b/usecases/projects/service.go index faac14d..56907f2 100644 --- a/usecases/projects/service.go +++ b/usecases/projects/service.go @@ -67,6 +67,48 @@ func (s *Service) List(ctx context.Context) ([]Entry, error) { return entries, nil } +func (s *Service) ListRequirementAllScopes(ctx context.Context) ([]RequirementResult, error) { + allScopes, err := s.Scopes.Context(ctx).Scopes(ctx) + if err != nil { + return nil, err + } + + var res []RequirementResult + for _, scope := range allScopes { + scopedRes, err := s.listRequirements(ctx, scope) + if err != nil { + return nil, err + } + + res = append(res, scopedRes...) + } + + return res, nil +} + +func (s *Service) ListRequirements(ctx context.Context) ([]RequirementResult, error) { + return s.listRequirements(ctx, s.Scopes.Context(ctx).Scope) +} + +func (s *Service) listRequirements(ctx context.Context, sc scopes.Result) ([]RequirementResult, error) { + requirements, requirementStats, err := s.Repository.ListRequirementsByScope(ctx, sc.ID) + if err != nil { + return nil, err + } + + results := make([]RequirementResult, 0, len(requirements)) + for _, req := range requirements { + results = append(results, generateRequirementResult( + req, + sc, + requirementStats, + nil, + )) + } + + return results, nil +} + func (s *Service) ListByTags(ctx context.Context, tags []string) (interface{}, error) { projects, err := s.Repository.ListByTags(ctx, s.Scopes.Context(ctx).ID, tags) if err != nil {