diff options
author | Unknwon <u@gogs.io> | 2015-08-10 14:42:50 +0800 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2015-08-10 14:42:50 +0800 |
commit | 17de3ab0a313819bdeb73f3985b61a791ae84696 (patch) | |
tree | 55b8faa356fe7c79e6910d0b7cfe1fae606bb503 /models | |
parent | 48f12d4eaddf08282086199d94a306b47483521f (diff) | |
download | gitea-17de3ab0a313819bdeb73f3985b61a791ae84696.tar.gz gitea-17de3ab0a313819bdeb73f3985b61a791ae84696.zip |
add migrate from issue.label_ids to issue_label
Diffstat (limited to 'models')
-rw-r--r-- | models/error.go | 20 | ||||
-rw-r--r-- | models/issue.go | 186 | ||||
-rw-r--r-- | models/migrations/migrations.go | 61 | ||||
-rw-r--r-- | models/models.go | 3 |
4 files changed, 220 insertions, 50 deletions
diff --git a/models/error.go b/models/error.go index 0e554e5273..70285b3a34 100644 --- a/models/error.go +++ b/models/error.go @@ -239,6 +239,26 @@ func (err ErrRepoAlreadyExist) Error() string { return fmt.Sprintf("repository already exists [uname: %d, name: %s]", err.Uname, err.Name) } +// .____ ___. .__ +// | | _____ \_ |__ ____ | | +// | | \__ \ | __ \_/ __ \| | +// | |___ / __ \| \_\ \ ___/| |__ +// |_______ (____ /___ /\___ >____/ +// \/ \/ \/ \/ + +type ErrLabelNotExist struct { + ID int64 +} + +func IsErrLabelNotExist(err error) bool { + _, ok := err.(ErrLabelNotExist) + return ok +} + +func (err ErrLabelNotExist) Error() string { + return fmt.Sprintf("label does not exist [id: %d]", err.ID) +} + // _____ .__.__ __ // / \ |__| | ____ _______/ |_ ____ ____ ____ // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ diff --git a/models/issue.go b/models/issue.go index 39969c7194..892b9928c0 100644 --- a/models/issue.go +++ b/models/issue.go @@ -7,6 +7,7 @@ package models import ( "bytes" "errors" + "fmt" "html/template" "os" "strconv" @@ -22,7 +23,6 @@ import ( var ( ErrIssueNotExist = errors.New("Issue does not exist") - ErrLabelNotExist = errors.New("Label does not exist") ErrWrongIssueCounter = errors.New("Invalid number of issues for this milestone") ErrAttachmentNotExist = errors.New("Attachment does not exist") ErrAttachmentNotLinked = errors.New("Attachment does not belong to this issue") @@ -38,7 +38,6 @@ type Issue struct { Repo *Repository `xorm:"-"` PosterID int64 Poster *User `xorm:"-"` - LabelIDs string `xorm:"label_ids TEXT"` Labels []*Label `xorm:"-"` MilestoneID int64 Milestone *Milestone `xorm:"-"` @@ -76,29 +75,50 @@ func (i *Issue) GetPoster() (err error) { return err } -func (i *Issue) GetLabels() error { - if len(i.LabelIDs) < 3 { +func (i *Issue) hasLabel(e Engine, labelID int64) bool { + return hasIssueLabel(e, i.ID, labelID) +} + +// HasLabel returns true if issue has been labeled by given ID. +func (i *Issue) HasLabel(labelID int64) bool { + return i.hasLabel(x, labelID) +} + +func (i *Issue) addLabel(e Engine, labelID int64) error { + return newIssueLabel(e, i.ID, labelID) +} + +// AddLabel adds new label to issue by given ID. +func (i *Issue) AddLabel(labelID int64) error { + return i.addLabel(x, labelID) +} + +func (i *Issue) getLabels(e Engine) (err error) { + if len(i.Labels) > 0 { return nil } - strIds := strings.Split(strings.TrimSuffix(i.LabelIDs[1:], "|"), "|$") - i.Labels = make([]*Label, 0, len(strIds)) - for _, strId := range strIds { - id := com.StrTo(strId).MustInt64() - if id > 0 { - l, err := GetLabelById(id) - if err != nil { - if err == ErrLabelNotExist { - continue - } - return err - } - i.Labels = append(i.Labels, l) - } + i.Labels, err = getLabelsByIssueID(e, i.ID) + if err != nil { + return fmt.Errorf("getLabelsByIssueID: %v", err) } return nil } +// GetLabels retrieves all labels of issue and assign to corresponding field. +func (i *Issue) GetLabels() error { + return i.getLabels(x) +} + +func (i *Issue) removeLabel(e Engine, labelID int64) error { + return deleteIssueLabel(e, i.ID, labelID) +} + +// RemoveLabel removes a label from issue by given ID. +func (i *Issue) RemoveLabel(labelID int64) error { + return i.removeLabel(x, labelID) +} + func (i *Issue) GetAssignee() (err error) { if i.AssigneeID == 0 { return nil @@ -261,12 +281,6 @@ const ( IS_CLOSE ) -// GetIssuesByLabel returns a list of issues by given label and repository. -func GetIssuesByLabel(repoID, labelID int64) ([]*Issue, error) { - issues := make([]*Issue, 0, 10) - return issues, x.Where("repo_id=?", repoID).And("label_ids like '%$" + com.ToStr(labelID) + "|%'").Find(&issues) -} - // GetIssueCountByPoster returns number of issues of repository by poster. func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 { count, _ := x.Where("repo_id=?", rid).And("poster_id=?", uid).And("is_closed=?", isClosed).Count(new(Issue)) @@ -550,7 +564,7 @@ func UpdateIssueUserPairsByMentions(uids []int64, iid int64) error { // Label represents a label of repository for issues. type Label struct { ID int64 `xorm:"pk autoincr"` - RepoId int64 `xorm:"INDEX"` + RepoID int64 `xorm:"INDEX"` Name string Color string `xorm:"VARCHAR(7)"` NumIssues int @@ -570,10 +584,9 @@ func NewLabel(l *Label) error { return err } -// GetLabelById returns a label by given ID. -func GetLabelById(id int64) (*Label, error) { +func getLabelByID(e Engine, id int64) (*Label, error) { if id <= 0 { - return nil, ErrLabelNotExist + return nil, ErrLabelNotExist{id} } l := &Label{ID: id} @@ -581,16 +594,43 @@ func GetLabelById(id int64) (*Label, error) { if err != nil { return nil, err } else if !has { - return nil, ErrLabelNotExist + return nil, ErrLabelNotExist{l.ID} } return l, nil } -// GetLabels returns a list of labels of given repository ID. -func GetLabels(repoId int64) ([]*Label, error) { +// GetLabelByID returns a label by given ID. +func GetLabelByID(id int64) (*Label, error) { + return getLabelByID(x, id) +} + +// GetLabelsByRepoID returns all labels that belong to given repository by ID. +func GetLabelsByRepoID(repoID int64) ([]*Label, error) { labels := make([]*Label, 0, 10) - err := x.Where("repo_id=?", repoId).Find(&labels) - return labels, err + return labels, x.Where("repo_id=?", repoID).Find(&labels) +} + +func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) { + issueLabels, err := getIssueLabels(e, issueID) + if err != nil { + return nil, fmt.Errorf("getIssueLabels: %v", err) + } + + var label *Label + labels := make([]*Label, 0, len(issueLabels)) + for idx := range issueLabels { + label, err = getLabelByID(e, issueLabels[idx].LabelID) + if err != nil && !IsErrLabelNotExist(err) { + return nil, fmt.Errorf("getLabelByID: %v", err) + } + labels = append(labels, label) + } + return labels, nil +} + +// GetLabelsByIssueID returns all labels that belong to given issue by ID. +func GetLabelsByIssueID(issueID int64) ([]*Label, error) { + return getLabelsByIssueID(x, issueID) } // UpdateLabel updates label information. @@ -601,38 +641,88 @@ func UpdateLabel(l *Label) error { // DeleteLabel delete a label of given repository. func DeleteLabel(repoID, labelID int64) error { - l, err := GetLabelById(labelID) + l, err := GetLabelByID(labelID) if err != nil { - if err == ErrLabelNotExist { + if IsErrLabelNotExist(err) { return nil } return err } - issues, err := GetIssuesByLabel(repoID, labelID) - if err != nil { - return err - } - sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } - for _, issue := range issues { - issue.LabelIDs = strings.Replace(issue.LabelIDs, "$"+com.ToStr(labelID)+"|", "", -1) - if _, err = sess.Id(issue.ID).AllCols().Update(issue); err != nil { - return err - } - } - - if _, err = sess.Delete(l); err != nil { + if _, err = x.Where("label_id=?", labelID).Delete(new(IssueLabel)); err != nil { + return err + } else if _, err = sess.Delete(l); err != nil { return err } return sess.Commit() } +// .___ .____ ___. .__ +// | | ______ ________ __ ____ | | _____ \_ |__ ____ | | +// | |/ ___// ___/ | \_/ __ \| | \__ \ | __ \_/ __ \| | +// | |\___ \ \___ \| | /\ ___/| |___ / __ \| \_\ \ ___/| |__ +// |___/____ >____ >____/ \___ >_______ (____ /___ /\___ >____/ +// \/ \/ \/ \/ \/ \/ \/ + +// IssueLabel represetns an issue-lable relation. +type IssueLabel struct { + ID int64 `xorm:"pk autoincr"` + IssueID int64 `xorm:"UNIQUE(s)"` + LabelID int64 `xorm:"UNIQUE(s)"` +} + +func hasIssueLabel(e Engine, issueID, labelID int64) bool { + has, _ := e.Where("issue_id=? AND label_id=?", issueID, labelID).Get(new(IssueLabel)) + return has +} + +// HasIssueLabel returns true if issue has been labeled. +func HasIssueLabel(issueID, labelID int64) bool { + return hasIssueLabel(x, issueID, labelID) +} + +func newIssueLabel(e Engine, issueID, labelID int64) error { + _, err := e.Insert(&IssueLabel{ + IssueID: issueID, + LabelID: labelID, + }) + return err +} + +// NewIssueLabel creates a new issue-label relation. +func NewIssueLabel(issueID, labelID int64) error { + return newIssueLabel(x, issueID, labelID) +} + +func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) { + issueLabels := make([]*IssueLabel, 0, 10) + return issueLabels, e.Where("issue_id=?", issueID).Find(&issueLabels) +} + +// GetIssueLabels returns all issue-label relations of given issue by ID. +func GetIssueLabels(issueID int64) ([]*IssueLabel, error) { + return getIssueLabels(x, issueID) +} + +func deleteIssueLabel(e Engine, issueID, labelID int64) error { + _, err := e.Delete(&IssueLabel{ + IssueID: issueID, + LabelID: labelID, + }) + return err +} + +// DeleteIssueLabel deletes issue-label relation. +func DeleteIssueLabel(issueID, labelID int64) error { + return deleteIssueLabel(x, issueID, labelID) +} + // _____ .__.__ __ // / \ |__| | ____ _______/ |_ ____ ____ ____ // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index c7900743f1..30c69fa301 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -58,6 +58,7 @@ var migrations = []Migration{ NewMigration("generate team-repo from team", teamToTeamRepo), // V3 -> V4:v0.5.13 NewMigration("fix locale file load panic", fixLocaleFileLoadPanic), // V4 -> V5:v0.6.0 NewMigration("trim action compare URL prefix", trimCommitActionAppUrlPrefix), // V5 -> V6:v0.6.3 + NewMigration("generate issue-label from issue", issueToIssueLabel), // V6 -> V7:v0.6.4 } // Migrate database to current version @@ -130,6 +131,9 @@ func accessToCollaboration(x *xorm.Engine) (err error) { results, err := x.Query("SELECT u.id AS `uid`, a.repo_name AS `repo`, a.mode AS `mode`, a.created as `created` FROM `access` a JOIN `user` u ON a.user_name=u.lower_name") if err != nil { + if strings.Contains(err.Error(), "no such column") { + return nil + } return err } @@ -278,6 +282,9 @@ func accessRefactor(x *xorm.Engine) (err error) { results, err = x.Query("SELECT `id`,`authorize`,`repo_ids` FROM `team` WHERE org_id=? AND authorize>? ORDER BY `authorize` ASC", ownerID, int(minAccessLevel)) if err != nil { + if strings.Contains(err.Error(), "no such column") { + return nil + } return fmt.Errorf("select teams from org: %v", err) } @@ -339,6 +346,9 @@ func teamToTeamRepo(x *xorm.Engine) error { results, err := x.Query("SELECT `id`,`org_id`,`repo_ids` FROM `team`") if err != nil { + if strings.Contains(err.Error(), "no such column") { + return nil + } return fmt.Errorf("select teams: %v", err) } for _, team := range results { @@ -369,7 +379,7 @@ func teamToTeamRepo(x *xorm.Engine) error { } if err = sess.Sync2(new(TeamRepo)); err != nil { - return fmt.Errorf("sync: %v", err) + return fmt.Errorf("sync2: %v", err) } else if _, err = sess.Insert(teamRepos); err != nil { return fmt.Errorf("insert team-repos: %v", err) } @@ -453,3 +463,52 @@ func trimCommitActionAppUrlPrefix(x *xorm.Engine) error { } return sess.Commit() } + +func issueToIssueLabel(x *xorm.Engine) error { + type IssueLabel struct { + ID int64 `xorm:"pk autoincr"` + IssueID int64 `xorm:"UNIQUE(s)"` + LabelID int64 `xorm:"UNIQUE(s)"` + } + + issueLabels := make([]*IssueLabel, 0, 50) + results, err := x.Query("SELECT `id`,`label_ids` FROM `issue`") + if err != nil { + if strings.Contains(err.Error(), "no such column") { + return nil + } + return fmt.Errorf("select issues: %v", err) + } + for _, issue := range results { + issueID := com.StrTo(issue["id"]).MustInt64() + + // Just in case legacy code can have duplicated IDs for same label. + mark := make(map[int64]bool) + for _, idStr := range strings.Split(string(issue["label_ids"]), "|") { + labelID := com.StrTo(strings.TrimPrefix(idStr, "$")).MustInt64() + if labelID == 0 || mark[labelID] { + continue + } + + mark[labelID] = true + issueLabels = append(issueLabels, &IssueLabel{ + IssueID: issueID, + LabelID: labelID, + }) + } + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = sess.Sync2(new(IssueLabel)); err != nil { + return fmt.Errorf("sync2: %v", err) + } else if _, err = sess.Insert(issueLabels); err != nil { + return fmt.Errorf("insert issue-labels: %v", err) + } + + return sess.Commit() +} diff --git a/models/models.go b/models/models.go index 01b96c0f54..e06d5cf88e 100644 --- a/models/models.go +++ b/models/models.go @@ -57,7 +57,8 @@ func init() { new(User), new(PublicKey), new(Oauth2), new(AccessToken), new(Repository), new(DeployKey), new(Collaboration), new(Access), new(Watch), new(Star), new(Follow), new(Action), - new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone), + new(Issue), new(Comment), new(Attachment), new(IssueUser), + new(Label), new(IssueLabel), new(Milestone), new(Mirror), new(Release), new(LoginSource), new(Webhook), new(UpdateTask), new(HookTask), new(Team), new(OrgUser), new(TeamUser), new(TeamRepo), |