add issue item stuff.
This commit is contained in:
@@ -12,6 +12,7 @@ type Database interface {
|
|||||||
Activities() repositories.ActivityRepository
|
Activities() repositories.ActivityRepository
|
||||||
Issues() repositories.IssueRepository
|
Issues() repositories.IssueRepository
|
||||||
IssueTasks() repositories.IssueTaskRepository
|
IssueTasks() repositories.IssueTaskRepository
|
||||||
|
IssueItems() repositories.IssueItemRepository
|
||||||
Items() repositories.ItemRepository
|
Items() repositories.ItemRepository
|
||||||
Projects() repositories.ProjectRepository
|
Projects() repositories.ProjectRepository
|
||||||
Session() repositories.SessionRepository
|
Session() repositories.SessionRepository
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type DB struct {
|
|||||||
activities *activityRepository
|
activities *activityRepository
|
||||||
issues *issueRepository
|
issues *issueRepository
|
||||||
issueTasks *issueTaskRepository
|
issueTasks *issueTaskRepository
|
||||||
|
issueItems *issueItemRepository
|
||||||
items *itemRepository
|
items *itemRepository
|
||||||
projects *projectRepository
|
projects *projectRepository
|
||||||
sessions *sessionRepository
|
sessions *sessionRepository
|
||||||
@@ -36,6 +37,10 @@ func (db *DB) IssueTasks() repositories.IssueTaskRepository {
|
|||||||
return db.issueTasks
|
return db.issueTasks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) IssueItems() repositories.IssueItemRepository {
|
||||||
|
return db.issueItems
|
||||||
|
}
|
||||||
|
|
||||||
func (db *DB) Items() repositories.ItemRepository {
|
func (db *DB) Items() repositories.ItemRepository {
|
||||||
return db.items
|
return db.items
|
||||||
}
|
}
|
||||||
@@ -95,12 +100,14 @@ func Open(connectionString string) (*DB, error) {
|
|||||||
sessions := &sessionRepository{db: db}
|
sessions := &sessionRepository{db: db}
|
||||||
projectStatuses := &projectStatusRepository{db: db}
|
projectStatuses := &projectStatusRepository{db: db}
|
||||||
issueTasks := &issueTaskRepository{db: db}
|
issueTasks := &issueTaskRepository{db: db}
|
||||||
|
issueItems := &issueItemRepository{db: db}
|
||||||
|
|
||||||
return &DB{
|
return &DB{
|
||||||
db: db,
|
db: db,
|
||||||
activities: activities,
|
activities: activities,
|
||||||
issues: issues,
|
issues: issues,
|
||||||
issueTasks: issueTasks,
|
issueTasks: issueTasks,
|
||||||
|
issueItems: issueItems,
|
||||||
items: items,
|
items: items,
|
||||||
projects: projects,
|
projects: projects,
|
||||||
users: users,
|
users: users,
|
||||||
|
|||||||
@@ -90,15 +90,15 @@ func (r *issueTaskRepository) Insert(ctx context.Context, task models.IssueTask)
|
|||||||
|
|
||||||
_, err = tx.NamedExecContext(ctx, `
|
_, err = tx.NamedExecContext(ctx, `
|
||||||
INSERT INTO issue_task (
|
INSERT INTO issue_task (
|
||||||
issue_task_id, issue_id, activity_id, created_time,
|
issue_task_id, issue_id, activity_id, created_time,
|
||||||
updated_time, due_time, status_stage, status_name,
|
updated_time, due_time, status_stage, status_name,
|
||||||
name, description, estimated_time, estimated_units,
|
name, description, estimated_time, estimated_units,
|
||||||
points_multiplier
|
points_multiplier
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:issue_task_id, :issue_id, :activity_id, :created_time,
|
:issue_task_id, :issue_id, :activity_id, :created_time,
|
||||||
:updated_time, :due_time, :status_stage, :status_name,
|
:updated_time, :due_time, :status_stage, :status_name,
|
||||||
:name, :description, :estimated_time, :estimated_units,
|
:name, :description, :estimated_time, :estimated_units,
|
||||||
:points_multiplier
|
:points_multiplier
|
||||||
)
|
)
|
||||||
`, task)
|
`, task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package mysqldriver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"git.aiterp.net/stufflog/server/internal/slerrors"
|
||||||
|
"git.aiterp.net/stufflog/server/models"
|
||||||
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type issueItemRepository struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *issueItemRepository) Find(ctx context.Context, id string) (*models.IssueItem, error) {
|
||||||
|
issueItem := models.IssueItem{}
|
||||||
|
err := r.db.GetContext(ctx, &issueItem, "SELECT * FROM issue_item WHERE issue_item_id=?", id)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, slerrors.NotFound("Issue item")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &issueItem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *issueItemRepository) List(ctx context.Context, filter models.IssueItemFilter) ([]*models.IssueItem, error) {
|
||||||
|
q := sq.Select("issue_item.*").From("issue_item").GroupBy("issue_item.issue_item_id")
|
||||||
|
if len(filter.IssueItemIDs) > 0 {
|
||||||
|
q = q.Where(sq.Eq{"issue_item_id": filter.IssueIDs})
|
||||||
|
}
|
||||||
|
if len(filter.IssueIDs) > 0 {
|
||||||
|
q = q.Where(sq.Eq{"issue_id": filter.IssueIDs})
|
||||||
|
}
|
||||||
|
if len(filter.IssueAssignees) > 0 || len(filter.IssueOwners) > 0 || filter.IssueMinStage != nil || filter.IssueMaxStage != nil {
|
||||||
|
q = q.Join("issue ON issue.issue_id = issue_item.issue_id")
|
||||||
|
}
|
||||||
|
if len(filter.IssueAssignees) > 0 {
|
||||||
|
q = q.Where(sq.Eq{"issue.assignee_id": filter.IssueAssignees})
|
||||||
|
}
|
||||||
|
if len(filter.IssueOwners) > 0 {
|
||||||
|
q = q.Where(sq.Eq{"issue.owner_id": filter.IssueOwners})
|
||||||
|
}
|
||||||
|
if filter.IssueMinStage != nil && filter.IssueMaxStage != nil && *filter.IssueMinStage == *filter.IssueMaxStage {
|
||||||
|
q = q.Where(sq.Eq{"issue.status_stage": *filter.IssueMinStage})
|
||||||
|
} else {
|
||||||
|
if filter.IssueMinStage != nil {
|
||||||
|
q = q.Where(sq.GtOrEq{"issue.status_stage": *filter.IssueMinStage})
|
||||||
|
}
|
||||||
|
if filter.IssueMaxStage != nil {
|
||||||
|
q = q.Where(sq.LtOrEq{"issue.status_stage": *filter.IssueMaxStage})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(filter.ItemIDs) > 0 {
|
||||||
|
q = q.Where(sq.Eq{"item_id": filter.IssueIDs})
|
||||||
|
}
|
||||||
|
if len(filter.ItemTags) > 0 {
|
||||||
|
q = q.Join("item_tag ON item_tag.item_id = issue_item.item_id").Where(
|
||||||
|
sq.Eq{"item_tag.tag": filter.ItemTags},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if filter.Acquired != nil {
|
||||||
|
q = q.Where(sq.Eq{"acquired": *filter.Acquired})
|
||||||
|
}
|
||||||
|
|
||||||
|
query, args, err := q.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]*models.IssueItem, 0, 16)
|
||||||
|
err = r.db.SelectContext(ctx, &results, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return []*models.IssueItem{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *issueItemRepository) Insert(ctx context.Context, item models.IssueItem) (*models.IssueItem, error) {
|
||||||
|
if item.IssueID == "" {
|
||||||
|
return nil, errors.New("missing issue id")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := r.db.BeginTxx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nextID, err := incCounter(ctx, tx, counterKindIssueSubID, item.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
item.ID = fmt.Sprintf("%s-%d", item.IssueID, nextID)
|
||||||
|
|
||||||
|
_, err = tx.NamedExecContext(ctx, `
|
||||||
|
INSERT INTO issue_item (
|
||||||
|
issue_item_id, issue_id, item_id, quantity, acquired
|
||||||
|
) VALUES (
|
||||||
|
:issue_item_id, :issue_id, :item_id, :quantity, :acquired
|
||||||
|
);
|
||||||
|
`, item)
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *issueItemRepository) Save(ctx context.Context, item models.IssueItem) error {
|
||||||
|
_, err := r.db.NamedExecContext(ctx, `
|
||||||
|
UPDATE issue_item SET
|
||||||
|
acquired=:acquired,
|
||||||
|
quantity=:quantity
|
||||||
|
WHERE issue_item_id=:issue_item_id
|
||||||
|
`, item)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *issueItemRepository) Delete(ctx context.Context, item models.IssueItem) error {
|
||||||
|
_, err := r.db.ExecContext(ctx, "DELETE FROM issue_item WHERE issue_item_id=? LIMIT 1;", item.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"git.aiterp.net/stufflog/server/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IssueItemRepository interface {
|
||||||
|
Find(ctx context.Context, id string) (*models.IssueItem, error)
|
||||||
|
List(ctx context.Context, filter models.IssueItemFilter) ([]*models.IssueItem, error)
|
||||||
|
Insert(ctx context.Context, item models.IssueItem) (*models.IssueItem, error)
|
||||||
|
Save(ctx context.Context, item models.IssueItem) error
|
||||||
|
Delete(ctx context.Context, item models.IssueItem) error
|
||||||
|
}
|
||||||
@@ -20,6 +20,8 @@ models:
|
|||||||
resolver: true
|
resolver: true
|
||||||
Duration:
|
Duration:
|
||||||
model: git.aiterp.net/stufflog/server/graph/scalars.Duration
|
model: git.aiterp.net/stufflog/server/graph/scalars.Duration
|
||||||
|
IssueIssueItemFilter:
|
||||||
|
model: git.aiterp.net/stufflog/server/models.IssueItemFilter
|
||||||
|
|
||||||
resolver:
|
resolver:
|
||||||
layout: follow-schema
|
layout: follow-schema
|
||||||
|
|||||||
@@ -85,6 +85,15 @@ func (r *issueResolver) Tasks(ctx context.Context, obj *models.Issue, filter *mo
|
|||||||
return r.Database.IssueTasks().List(ctx, *filter)
|
return r.Database.IssueTasks().List(ctx, *filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *issueResolver) Items(ctx context.Context, obj *models.Issue, filter *models.IssueItemFilter) ([]*models.IssueItem, error) {
|
||||||
|
if filter == nil {
|
||||||
|
filter = &models.IssueItemFilter{}
|
||||||
|
}
|
||||||
|
filter.IssueIDs = []string{obj.ID}
|
||||||
|
|
||||||
|
return r.Database.IssueItems().List(ctx, *filter)
|
||||||
|
}
|
||||||
|
|
||||||
// Issue returns graphcore.IssueResolver implementation.
|
// Issue returns graphcore.IssueResolver implementation.
|
||||||
func (r *Resolver) Issue() graphcore.IssueResolver { return &issueResolver{r} }
|
func (r *Resolver) Issue() graphcore.IssueResolver { return &issueResolver{r} }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package resolvers
|
||||||
|
|
||||||
|
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||||
|
// will be copied through when generating and any unknown code will be moved to the end.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.aiterp.net/stufflog/server/graph/graphcore"
|
||||||
|
"git.aiterp.net/stufflog/server/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *issueItemResolver) Issue(ctx context.Context, obj *models.IssueItem) (*models.Issue, error) {
|
||||||
|
return r.Database.Issues().Find(ctx, obj.IssueID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *issueItemResolver) Item(ctx context.Context, obj *models.IssueItem) (*models.Item, error) {
|
||||||
|
return r.Database.Items().Find(ctx, obj.ItemID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *issueItemResolver) Remaining(ctx context.Context, obj *models.IssueItem) (int, error) {
|
||||||
|
if obj.Acquired {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use logs
|
||||||
|
return obj.Quantity, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssueItem returns graphcore.IssueItemResolver implementation.
|
||||||
|
func (r *Resolver) IssueItem() graphcore.IssueItemResolver { return &issueItemResolver{r} }
|
||||||
|
|
||||||
|
type issueItemResolver struct{ *Resolver }
|
||||||
|
|
||||||
|
// !!! WARNING !!!
|
||||||
|
// The code below was going to be deleted when updating resolvers. It has been copied here so you have
|
||||||
|
// one last chance to move it out of harms way if you want. There are two reasons this happens:
|
||||||
|
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
|
||||||
|
// it when you're done.
|
||||||
|
// - You have helper methods in this file. Move them out to keep these resolver files clean.
|
||||||
|
func (r *issueItemResolver) Quanity(ctx context.Context, obj *models.IssueItem) (int, error) {
|
||||||
|
panic(fmt.Errorf("not implemented"))
|
||||||
|
}
|
||||||
@@ -412,6 +412,69 @@ func (r *mutationResolver) EditIssueTask(ctx context.Context, input graphcore.Is
|
|||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) CreateIssueItem(ctx context.Context, input graphcore.IssueItemCreateInput) (*models.IssueItem, error) {
|
||||||
|
user := r.Auth.UserFromContext(ctx)
|
||||||
|
if user == nil {
|
||||||
|
return nil, slerrors.PermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
issue, err := r.Database.Issues().Find(ctx, input.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() {
|
||||||
|
return nil, slerrors.PermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := r.Database.Items().Find(ctx, input.ItemID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
issueItem := &models.IssueItem{
|
||||||
|
IssueID: issue.ID,
|
||||||
|
ItemID: item.ID,
|
||||||
|
Quantity: input.Quanitty,
|
||||||
|
Acquired: input.Acquired != nil && *input.Acquired,
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Database.IssueItems().Insert(ctx, *issueItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) EditIssueItem(ctx context.Context, input graphcore.IssueItemEditInput) (*models.IssueItem, error) {
|
||||||
|
user := r.Auth.UserFromContext(ctx)
|
||||||
|
if user == nil {
|
||||||
|
return nil, slerrors.PermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := r.Database.IssueItems().Find(ctx, input.IssueItemID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
issue, err := r.Database.Issues().Find(ctx, item.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if perm, err := r.Auth.IssuePermission(ctx, *issue); err != nil || !perm.CanManageOwnIssue() {
|
||||||
|
return nil, slerrors.PermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.SetAcquired != nil {
|
||||||
|
item.Acquired = *input.SetAcquired
|
||||||
|
}
|
||||||
|
if input.SetQuanitty != nil {
|
||||||
|
item.Quantity = *input.SetQuanitty
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.Database.IssueItems().Save(ctx, *item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) LoginUser(ctx context.Context, input graphcore.UserLoginInput) (*models.User, error) {
|
func (r *mutationResolver) LoginUser(ctx context.Context, input graphcore.UserLoginInput) (*models.User, error) {
|
||||||
return r.Auth.Login(ctx, input.Username, input.Password)
|
return r.Auth.Login(ctx, input.Username, input.Password)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,76 @@ func (r *queryResolver) ItemTags(ctx context.Context) ([]string, error) {
|
|||||||
return r.Database.Items().GetTags(ctx)
|
return r.Database.Items().GetTags(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *queryResolver) IssueItem(ctx context.Context, id string) (*models.IssueItem, error) {
|
||||||
|
user := r.Auth.UserFromContext(ctx)
|
||||||
|
if user == nil {
|
||||||
|
return nil, slerrors.PermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := r.Database.IssueItems().Find(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
issue, err := r.Database.Issues().Find(ctx, item.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = r.Auth.IssuePermission(ctx, *issue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *queryResolver) IssueItems(ctx context.Context, filter *models.IssueItemFilter) ([]*models.IssueItem, error) {
|
||||||
|
user := r.Auth.UserFromContext(ctx)
|
||||||
|
if user == nil {
|
||||||
|
return nil, slerrors.PermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter == nil {
|
||||||
|
filter = &models.IssueItemFilter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
items, err := r.Database.IssueItems().List(ctx, *filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accessMap := make(map[string]bool)
|
||||||
|
deleteList := make([]int, 0, len(items))
|
||||||
|
for i, item := range items {
|
||||||
|
if access, ok := accessMap[item.IssueID]; ok && access {
|
||||||
|
continue
|
||||||
|
} else if ok && !access {
|
||||||
|
deleteList = append(deleteList, i-len(deleteList))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
issue, err := r.Database.Issues().Find(ctx, item.IssueID)
|
||||||
|
if err != nil {
|
||||||
|
deleteList = append(deleteList, i-len(deleteList))
|
||||||
|
accessMap[item.IssueID] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = r.Auth.IssuePermission(ctx, *issue)
|
||||||
|
if err != nil {
|
||||||
|
deleteList = append(deleteList, i-len(deleteList))
|
||||||
|
}
|
||||||
|
|
||||||
|
accessMap[issue.ID] = err != nil
|
||||||
|
}
|
||||||
|
for _, index := range deleteList {
|
||||||
|
items = append(items[:index], items[index+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Project(ctx context.Context, id string) (*models.Project, error) {
|
func (r *queryResolver) Project(ctx context.Context, id string) (*models.Project, error) {
|
||||||
user := r.Auth.UserFromContext(ctx)
|
user := r.Auth.UserFromContext(ctx)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ type Issue {
|
|||||||
status: ProjectStatus!
|
status: ProjectStatus!
|
||||||
"Issue tasks."
|
"Issue tasks."
|
||||||
tasks(filter: IssueTaskFilter): [IssueTask!]!
|
tasks(filter: IssueTaskFilter): [IssueTask!]!
|
||||||
|
"Issue items."
|
||||||
|
items(filter: IssueIssueItemFilter): [IssueItem!]!
|
||||||
#"Logs related to this issue."
|
#"Logs related to this issue."
|
||||||
#logs: [Log!]!
|
#logs: [Log!]!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
"""
|
||||||
|
An issue item is a requirement of an item under an issue.
|
||||||
|
"""
|
||||||
|
type IssueItem {
|
||||||
|
"ID of the issue item listing."
|
||||||
|
id: String!
|
||||||
|
"The amount of the item associated with an issue."
|
||||||
|
quantity: Int!
|
||||||
|
"Whether the full quantity of item has been acquired."
|
||||||
|
acquired: Boolean!
|
||||||
|
|
||||||
|
"Parent issue of the issue item."
|
||||||
|
issue: Issue!
|
||||||
|
"The item associated with the issue."
|
||||||
|
item: Item!
|
||||||
|
"The amount of items remaining."
|
||||||
|
remaining: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
"Input for the items query."
|
||||||
|
input IssueItemFilter {
|
||||||
|
"Filter to only these IDs, used primarily by IDs."
|
||||||
|
issueItemIds: [String!]
|
||||||
|
"Filter to only these issues."
|
||||||
|
issueIds: [String!]
|
||||||
|
"Filter to only issues where these are the asignees."
|
||||||
|
issueAssignees: [String!]
|
||||||
|
"Filter to only issues where these are the owners."
|
||||||
|
issueOwners: [String!]
|
||||||
|
"Filter by issue minimum stage (inclusive)."
|
||||||
|
issueMinStage: Int
|
||||||
|
"Filter by issue maximum stage (inclusive)."
|
||||||
|
issueMaxStage: Int
|
||||||
|
"Filter to only list issue items with these items."
|
||||||
|
itemIds: [String!]
|
||||||
|
"Filter to only list issue items where the item has these tags."
|
||||||
|
itemTags: [String!]
|
||||||
|
"Only listed acquired or non-acquired items."
|
||||||
|
acquired: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
"Input for the items query."
|
||||||
|
input IssueIssueItemFilter {
|
||||||
|
"Filter to only these IDs, used primarily by IDs."
|
||||||
|
issueItemIds: [String!]
|
||||||
|
"Filter to only list issue items with these items."
|
||||||
|
itemIds: [String!]
|
||||||
|
"Filter to only list issue items where the item has these tags."
|
||||||
|
itemTags: [String!]
|
||||||
|
"Only listed acquired or non-acquired items."
|
||||||
|
acquired: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
"Input for the createIssueItem mutation."
|
||||||
|
input IssueItemCreateInput {
|
||||||
|
"Parent issue."
|
||||||
|
issueId: String!
|
||||||
|
"Item to associate with."
|
||||||
|
itemId: String!
|
||||||
|
"Quantity of the item."
|
||||||
|
quanitty: Int!
|
||||||
|
"Whether the item has already been acquired."
|
||||||
|
acquired: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
"Input for the editIssueItem mutation."
|
||||||
|
input IssueItemEditInput {
|
||||||
|
"The ID of the issue item to edit."
|
||||||
|
issueItemId: String!
|
||||||
|
"Update the quantity of the item."
|
||||||
|
setQuanitty: Int
|
||||||
|
"Update whether the item has been acquired."
|
||||||
|
setAcquired: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
@@ -24,6 +24,10 @@ type Mutation {
|
|||||||
"Edit an issue task."
|
"Edit an issue task."
|
||||||
editIssueTask(input: IssueTaskEditInput!): IssueTask!
|
editIssueTask(input: IssueTaskEditInput!): IssueTask!
|
||||||
# ISSUE ITEM
|
# ISSUE ITEM
|
||||||
|
"Create an issue item."
|
||||||
|
createIssueItem(input: IssueItemCreateInput!): IssueItem!
|
||||||
|
"Edit an issue item."
|
||||||
|
editIssueItem(input: IssueItemEditInput!): IssueItem!
|
||||||
|
|
||||||
# LOG
|
# LOG
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ type Query {
|
|||||||
"List item tags."
|
"List item tags."
|
||||||
itemTags: [String!]!
|
itemTags: [String!]!
|
||||||
|
|
||||||
|
"Find issue item."
|
||||||
|
issueItem(id: String!): IssueItem!
|
||||||
|
"List issue items."
|
||||||
|
issueItems(filter: IssueItemFilter): [IssueItem!]!
|
||||||
|
|
||||||
"Find project."
|
"Find project."
|
||||||
project(id: String!): Project!
|
project(id: String!): Project!
|
||||||
"List projects."
|
"List projects."
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
CREATE TABLE issue_item (
|
||||||
|
issue_item_id CHAR(48) NOT NULL PRIMARY KEY,
|
||||||
|
issue_id CHAR(32) NOT NULL,
|
||||||
|
item_id CHAR(32) NOT NULL,
|
||||||
|
quantity INTEGER NOT NULL,
|
||||||
|
acquired BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
INDEX (acquired),
|
||||||
|
INDEX (issue_id),
|
||||||
|
INDEX (item_id)
|
||||||
|
);
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
DROP TABLE issue_item;
|
||||||
|
-- +goose StatementEnd
|
||||||
+13
-1
@@ -6,5 +6,17 @@ type IssueItem struct {
|
|||||||
IssueID string `db:"issue_id"`
|
IssueID string `db:"issue_id"`
|
||||||
ItemID string `db:"item_id"`
|
ItemID string `db:"item_id"`
|
||||||
Quantity int `db:"quantity"`
|
Quantity int `db:"quantity"`
|
||||||
Acquired bool `db:"resolved"`
|
Acquired bool `db:"acquired"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IssueItemFilter struct {
|
||||||
|
IssueItemIDs []string
|
||||||
|
IssueIDs []string
|
||||||
|
IssueAssignees []string
|
||||||
|
IssueOwners []string
|
||||||
|
IssueMinStage *int
|
||||||
|
IssueMaxStage *int
|
||||||
|
ItemIDs []string
|
||||||
|
ItemTags []string
|
||||||
|
Acquired *bool
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user