|
|
@ -4,6 +4,7 @@ import ( |
|
|
|
"context" |
|
|
|
"database/sql" |
|
|
|
"git.aiterp.net/stufflog3/stufflog3/entities" |
|
|
|
"git.aiterp.net/stufflog3/stufflog3/internal/genutils" |
|
|
|
"git.aiterp.net/stufflog3/stufflog3/models" |
|
|
|
"git.aiterp.net/stufflog3/stufflog3/ports/mysql/mysqlcore" |
|
|
|
"github.com/Masterminds/squirrel" |
|
|
@ -24,6 +25,11 @@ func (r *projectRepository) Find(ctx context.Context, scopeID, projectID int) (* |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
tags, err := r.q.ListTagsByObject(ctx, mysqlcore.ListTagsByObjectParams{ |
|
|
|
ObjectKind: tagObjectKindProject, |
|
|
|
ObjectID: row.ID, |
|
|
|
}) |
|
|
|
|
|
|
|
return &entities.Project{ |
|
|
|
ID: row.ID, |
|
|
|
ScopeID: row.ScopeID, |
|
|
@ -32,6 +38,7 @@ func (r *projectRepository) Find(ctx context.Context, scopeID, projectID int) (* |
|
|
|
Name: row.Name, |
|
|
|
Description: row.Description, |
|
|
|
Status: models.Status(row.Status), |
|
|
|
Tags: tags, |
|
|
|
}, nil |
|
|
|
} |
|
|
|
|
|
|
@ -78,6 +85,8 @@ func (r *projectRepository) FetchProjects(ctx context.Context, scopeID int, ids |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
project.Tags = []string{} |
|
|
|
|
|
|
|
projects = append(projects, project) |
|
|
|
} |
|
|
|
if err := rows.Close(); err != nil { |
|
|
@ -87,6 +96,18 @@ func (r *projectRepository) FetchProjects(ctx context.Context, scopeID int, ids |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
// Fill tags
|
|
|
|
err = fetchTags(ctx, r.db, tagObjectKindProject, ids, func(id int, tag string) { |
|
|
|
for i := range projects { |
|
|
|
if projects[i].ID == id { |
|
|
|
projects[i].Tags = append(projects[i].Tags, tag) |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
return projects, nil |
|
|
|
} |
|
|
|
|
|
|
@ -101,6 +122,7 @@ func (r *projectRepository) List(ctx context.Context, scopeID int) ([]entities.P |
|
|
|
} |
|
|
|
|
|
|
|
res := make([]entities.Project, 0, len(rows)) |
|
|
|
ids := make([]int, 0, len(rows)) |
|
|
|
for _, row := range rows { |
|
|
|
res = append(res, entities.Project{ |
|
|
|
ID: row.ID, |
|
|
@ -110,14 +132,74 @@ func (r *projectRepository) List(ctx context.Context, scopeID int) ([]entities.P |
|
|
|
Name: row.Name, |
|
|
|
Description: row.Description, |
|
|
|
Status: models.Status(row.Status), |
|
|
|
Tags: []string{}, |
|
|
|
}) |
|
|
|
ids = append(ids, row.ID) |
|
|
|
} |
|
|
|
|
|
|
|
// Fill tags
|
|
|
|
err = fetchTags(ctx, r.db, tagObjectKindProject, ids, func(id int, tag string) { |
|
|
|
for i := range res { |
|
|
|
if id == res[i].ID { |
|
|
|
res[i].Tags = append(res[i].Tags, tag) |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
return res, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (r *projectRepository) ListByTags(ctx context.Context, scopeID int, tags []string) ([]entities.Project, error) { |
|
|
|
query, args, err := squirrel.Select("object_id, tag_name"). |
|
|
|
From("tag"). |
|
|
|
Where(squirrel.Eq{"tag_name": tags, "object_kind": tagObjectKindProject}). |
|
|
|
ToSql() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
rows, err := r.db.QueryContext(ctx, query, args...) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
ids := make([]int, 0, 16) |
|
|
|
matches := make(map[int]int, 64) |
|
|
|
for rows.Next() { |
|
|
|
var objectID int |
|
|
|
var tagName string |
|
|
|
err := rows.Scan(&objectID, &tagName) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
if matches[objectID] == 0 { |
|
|
|
ids = append(ids, objectID) |
|
|
|
} |
|
|
|
matches[objectID] += 1 |
|
|
|
} |
|
|
|
|
|
|
|
err = rows.Close() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
ids = genutils.RetainInPlace(ids, func(id int) bool { |
|
|
|
return matches[id] == len(tags) |
|
|
|
}) |
|
|
|
|
|
|
|
return r.FetchProjects(ctx, scopeID, ids...) |
|
|
|
} |
|
|
|
|
|
|
|
func (r *projectRepository) Insert(ctx context.Context, project entities.Project) (*entities.Project, error) { |
|
|
|
res, err := r.q.InsertProject(ctx, mysqlcore.InsertProjectParams{ |
|
|
|
tx, err := r.db.BeginTx(ctx, nil) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
defer tx.Rollback() |
|
|
|
q := r.q.WithTx(tx) |
|
|
|
|
|
|
|
res, err := q.InsertProject(ctx, mysqlcore.InsertProjectParams{ |
|
|
|
ScopeID: project.ScopeID, |
|
|
|
OwnerID: project.OwnerID, |
|
|
|
Name: project.Name, |
|
|
@ -134,6 +216,23 @@ func (r *projectRepository) Insert(ctx context.Context, project entities.Project |
|
|
|
} |
|
|
|
|
|
|
|
project.ID = int(id) |
|
|
|
|
|
|
|
for _, tag := range project.Tags { |
|
|
|
err := q.InsertTag(ctx, mysqlcore.InsertTagParams{ |
|
|
|
ObjectKind: tagObjectKindProject, |
|
|
|
ObjectID: project.ID, |
|
|
|
TagName: tag, |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
err = tx.Commit() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
return &project, nil |
|
|
|
} |
|
|
|
|
|
|
@ -174,6 +273,22 @@ func (r *projectRepository) Delete(ctx context.Context, project entities.Project |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
err = q.DeleteTagByObject(ctx, mysqlcore.DeleteTagByObjectParams{ |
|
|
|
ObjectKind: tagObjectKindRequirement, |
|
|
|
ObjectID: req.ID, |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
err = q.DeleteTagByObject(ctx, mysqlcore.DeleteTagByObjectParams{ |
|
|
|
ObjectKind: tagObjectKindProject, |
|
|
|
ObjectID: project.ID, |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
err = q.DeleteAllProjectRequirements(ctx, project.ID) |
|
|
@ -226,10 +341,25 @@ func (r *projectRepository) FetchRequirements(ctx context.Context, scopeID int, |
|
|
|
return nil, nil, err |
|
|
|
} |
|
|
|
|
|
|
|
requirement.Tags = []string{} |
|
|
|
|
|
|
|
requirements = append(requirements, requirement) |
|
|
|
ids = append(ids, requirement.ID) |
|
|
|
} |
|
|
|
|
|
|
|
// Fill tags
|
|
|
|
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) |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, nil, err |
|
|
|
} |
|
|
|
|
|
|
|
query, args, err = squirrel.Select("project_requirement_id, stat_id, required"). |
|
|
|
From("project_requirement_stat"). |
|
|
|
Where(squirrel.Eq{"project_requirement_id": requirementIDs}). |
|
|
@ -270,6 +400,7 @@ func (r *projectRepository) ListRequirements(ctx context.Context, projectID int) |
|
|
|
} |
|
|
|
|
|
|
|
requirements := make([]entities.Requirement, 0, len(reqRows)) |
|
|
|
ids := make([]int, 0, len(reqRows)) |
|
|
|
for _, row := range reqRows { |
|
|
|
requirements = append(requirements, entities.Requirement{ |
|
|
|
ID: row.ID, |
|
|
@ -280,7 +411,22 @@ func (r *projectRepository) ListRequirements(ctx context.Context, projectID int) |
|
|
|
IsCoarse: row.IsCoarse, |
|
|
|
AggregateRequired: row.AggregateRequired, |
|
|
|
Status: models.Status(row.Status), |
|
|
|
Tags: []string{}, |
|
|
|
}) |
|
|
|
ids = append(ids, row.ID) |
|
|
|
} |
|
|
|
|
|
|
|
// Fill tags
|
|
|
|
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) |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, nil, err |
|
|
|
} |
|
|
|
|
|
|
|
stats := make([]entities.RequirementStat, 0, len(statsRows)) |
|
|
@ -296,7 +442,14 @@ func (r *projectRepository) ListRequirements(ctx context.Context, projectID int) |
|
|
|
} |
|
|
|
|
|
|
|
func (r *projectRepository) CreateRequirement(ctx context.Context, requirement entities.Requirement) (*entities.Requirement, error) { |
|
|
|
res, err := r.q.InsertProjectRequirement(ctx, mysqlcore.InsertProjectRequirementParams{ |
|
|
|
tx, err := r.db.BeginTx(ctx, nil) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
defer tx.Rollback() |
|
|
|
q := r.q.WithTx(tx) |
|
|
|
|
|
|
|
res, err := q.InsertProjectRequirement(ctx, mysqlcore.InsertProjectRequirementParams{ |
|
|
|
ScopeID: requirement.ScopeID, |
|
|
|
ProjectID: requirement.ProjectID, |
|
|
|
Name: requirement.Name, |
|
|
@ -314,6 +467,22 @@ func (r *projectRepository) CreateRequirement(ctx context.Context, requirement e |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
for _, tag := range requirement.Tags { |
|
|
|
err := q.InsertTag(ctx, mysqlcore.InsertTagParams{ |
|
|
|
ObjectKind: tagObjectKindRequirement, |
|
|
|
ObjectID: int(id), |
|
|
|
TagName: tag, |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
err = tx.Commit() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
requirement.ID = int(id) |
|
|
|
return &requirement, nil |
|
|
|
} |
|
|
@ -358,6 +527,14 @@ func (r *projectRepository) DeleteRequirement(ctx context.Context, requirement e |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
err = q.DeleteTagByObject(ctx, mysqlcore.DeleteTagByObjectParams{ |
|
|
|
ObjectKind: tagObjectKindRequirement, |
|
|
|
ObjectID: requirement.ID, |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
return tx.Commit() |
|
|
|
} |
|
|
|
|
|
|
|