Exemplo n.º 1
0
func deletePublicKey(e *xorm.Session, keyID int64) error {
	sshOpLocker.Lock()
	defer sshOpLocker.Unlock()

	key := &PublicKey{ID: keyID}
	has, err := e.Get(key)
	if err != nil {
		return err
	} else if !has {
		return nil
	}

	if _, err = e.Id(key.ID).Delete(new(PublicKey)); err != nil {
		return err
	}

	// Don't need to rewrite this file if builtin SSH server is enabled.
	if setting.StartSSHServer {
		return nil
	}

	fpath := filepath.Join(setting.SSHRootPath, "authorized_keys")
	tmpPath := filepath.Join(setting.SSHRootPath, "authorized_keys.tmp")
	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
		return err
	} else if err = os.Remove(fpath); err != nil {
		return err
	}
	return os.Rename(tmpPath, fpath)
}
Exemplo n.º 2
0
func deletePublicKey(e *xorm.Session, keyID int64) error {
	sshOpLocker.Lock()
	defer sshOpLocker.Unlock()

	key := &PublicKey{ID: keyID}
	has, err := e.Get(key)
	if err != nil {
		return err
	} else if !has {
		return nil
	}

	if _, err = e.Id(key.ID).Delete(new(PublicKey)); err != nil {
		return err
	}

	fpath := filepath.Join(SSHPath, "authorized_keys")
	tmpPath := filepath.Join(SSHPath, "authorized_keys.tmp")
	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
		return err
	} else if err = os.Remove(fpath); err != nil {
		return err
	}
	return os.Rename(tmpPath, fpath)
}
Exemplo n.º 3
0
// UpdateAccess updates access information with session for rolling back.
func UpdateAccessWithSession(sess *xorm.Session, access *Access) error {
	if _, err := sess.Id(access.Id).Update(access); err != nil {
		sess.Rollback()
		return err
	}
	return nil
}
Exemplo n.º 4
0
//the id should be a uuid
func getRecord(out interface{}, id string, session *xorm.Session) (statusCode int, err error) {
	if _, err := uuid.FromString(id); err != nil {
		return http.StatusBadRequest, errUuidNotValid
	}

	found, err := session.Id(id).Get(out)

	if err != nil {
		return http.StatusInternalServerError, err
	}
	if found == false {
		return http.StatusNotFound, errNotFound
	}

	return http.StatusOK, nil
}
Exemplo n.º 5
0
// addAccessWithAuthorize inserts or updates access with given mode.
func addAccessWithAuthorize(sess *xorm.Session, access *Access, mode AccessType) error {
	has, err := x.Get(access)
	if err != nil {
		return fmt.Errorf("fail to get access: %v", err)
	}
	access.Mode = mode
	if has {
		if _, err = sess.Id(access.Id).Update(access); err != nil {
			return fmt.Errorf("fail to update access: %v", err)
		}
	} else {
		if _, err = sess.Insert(access); err != nil {
			return fmt.Errorf("fail to insert access: %v", err)
		}
	}
	return nil
}
Exemplo n.º 6
0
func upsertAlerts(existingAlerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xorm.Session) error {
	for _, alert := range cmd.Alerts {
		update := false
		var alertToUpdate *m.Alert

		for _, k := range existingAlerts {
			if alert.PanelId == k.PanelId {
				update = true
				alert.Id = k.Id
				alertToUpdate = k
				break
			}
		}

		if update {
			if alertToUpdate.ContainsUpdates(alert) {
				alert.Updated = time.Now()
				alert.State = alertToUpdate.State
				_, err := sess.Id(alert.Id).Update(alert)
				if err != nil {
					return err
				}

				sqlog.Debug("Alert updated", "name", alert.Name, "id", alert.Id)
			}
		} else {
			alert.Updated = time.Now()
			alert.Created = time.Now()
			alert.State = m.AlertStateNoData
			alert.NewStateDate = time.Now()

			_, err := sess.Insert(alert)
			if err != nil {
				return err
			}

			sqlog.Debug("Alert inserted", "name", alert.Name, "id", alert.Id)
		}
	}

	return nil
}
Exemplo n.º 7
0
func deletePublicKey(e *xorm.Session, key *PublicKey) error {
	has, err := e.Get(key)
	if err != nil {
		return err
	} else if !has {
		return nil
	}

	if _, err = e.Id(key.ID).Delete(key); err != nil {
		return err
	}

	fpath := filepath.Join(SSHPath, "authorized_keys")
	tmpPath := filepath.Join(SSHPath, "authorized_keys.tmp")
	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
		return err
	} else if err = os.Remove(fpath); err != nil {
		return err
	}
	return os.Rename(tmpPath, fpath)
}
Exemplo n.º 8
0
Arquivo: issue.go Projeto: chadoe/gogs
// It's caller's responsibility to create action.
func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64, uuids []string, isPull bool) (err error) {
	if _, err = e.Insert(issue); err != nil {
		return err
	}

	if isPull {
		_, err = e.Exec("UPDATE `repository` SET num_pulls=num_pulls+1 WHERE id=?", issue.RepoID)
	} else {
		_, err = e.Exec("UPDATE `repository` SET num_issues=num_issues+1 WHERE id=?", issue.RepoID)
	}
	if err != nil {
		return err
	}

	var label *Label
	for _, id := range labelIDs {
		if id == 0 {
			continue
		}

		label, err = getLabelByID(e, id)
		if err != nil {
			return err
		}
		if err = issue.addLabel(e, label); err != nil {
			return fmt.Errorf("addLabel: %v", err)
		}

	}

	if issue.MilestoneID > 0 {
		if err = changeMilestoneAssign(e, 0, issue); err != nil {
			return err
		}
	}

	if err = newIssueUsers(e, repo, issue); err != nil {
		return err
	}

	// Check attachments.
	attachments := make([]*Attachment, 0, len(uuids))
	for _, uuid := range uuids {
		attach, err := getAttachmentByUUID(e, uuid)
		if err != nil {
			if IsErrAttachmentNotExist(err) {
				continue
			}
			return fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err)
		}
		attachments = append(attachments, attach)
	}

	for i := range attachments {
		attachments[i].IssueID = issue.ID
		// No assign value could be 0, so ignore AllCols().
		if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
			return fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err)
		}
	}

	return nil
}
Exemplo n.º 9
0
Arquivo: issue.go Projeto: chadoe/gogs
func createComment(e *xorm.Session, u *User, repo *Repository, issue *Issue, commitID, line int64, cmtType CommentType, content, commitSHA string, uuids []string) (_ *Comment, err error) {
	comment := &Comment{
		PosterID:  u.Id,
		Type:      cmtType,
		IssueID:   issue.ID,
		CommitID:  commitID,
		Line:      line,
		Content:   content,
		CommitSHA: commitSHA,
	}
	if _, err = e.Insert(comment); err != nil {
		return nil, err
	}

	// Check comment type.
	switch cmtType {
	case COMMENT_TYPE_COMMENT:
		if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", issue.ID); err != nil {
			return nil, err
		}

		// Check attachments.
		attachments := make([]*Attachment, 0, len(uuids))
		for _, uuid := range uuids {
			attach, err := getAttachmentByUUID(e, uuid)
			if err != nil {
				if IsErrAttachmentNotExist(err) {
					continue
				}
				return nil, fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err)
			}
			attachments = append(attachments, attach)
		}

		for i := range attachments {
			attachments[i].IssueID = issue.ID
			attachments[i].CommentID = comment.ID
			// No assign value could be 0, so ignore AllCols().
			if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
				return nil, fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err)
			}
		}

		// Notify watchers.
		act := &Action{
			ActUserID:    u.Id,
			ActUserName:  u.Name,
			ActEmail:     u.Email,
			OpType:       COMMENT_ISSUE,
			Content:      fmt.Sprintf("%d|%s", issue.Index, strings.Split(content, "\n")[0]),
			RepoID:       repo.ID,
			RepoUserName: repo.Owner.Name,
			RepoName:     repo.Name,
			IsPrivate:    repo.IsPrivate,
		}
		if err = notifyWatchers(e, act); err != nil {
			return nil, err
		}

	case COMMENT_TYPE_REOPEN:
		if issue.IsPull {
			_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", repo.ID)
		} else {
			_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", repo.ID)
		}
		if err != nil {
			return nil, err
		}
	case COMMENT_TYPE_CLOSE:
		if issue.IsPull {
			_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", repo.ID)
		} else {
			_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", repo.ID)
		}
		if err != nil {
			return nil, err
		}
	}

	return comment, nil
}
Exemplo n.º 10
0
// FIXME: need some kind of mechanism to record failure. HINT: system notice
func deleteUser(e *xorm.Session, u *User) error {
	// Note: A user owns any repository or belongs to any organization
	//	cannot perform delete operation.

	// Check ownership of repository.
	count, err := getRepositoryCount(e, u)
	if err != nil {
		return fmt.Errorf("GetRepositoryCount: %v", err)
	} else if count > 0 {
		return ErrUserOwnRepos{UID: u.Id}
	}

	// Check membership of organization.
	count, err = u.getOrganizationCount(e)
	if err != nil {
		return fmt.Errorf("GetOrganizationCount: %v", err)
	} else if count > 0 {
		return ErrUserHasOrgs{UID: u.Id}
	}

	// ***** START: Watch *****
	watches := make([]*Watch, 0, 10)
	if err = e.Find(&watches, &Watch{UserID: u.Id}); err != nil {
		return fmt.Errorf("get all watches: %v", err)
	}
	for i := range watches {
		if _, err = e.Exec("UPDATE `repository` SET num_watches=num_watches-1 WHERE id=?", watches[i].RepoID); err != nil {
			return fmt.Errorf("decrease repository watch number[%d]: %v", watches[i].RepoID, err)
		}
	}
	// ***** END: Watch *****

	// ***** START: Star *****
	stars := make([]*Star, 0, 10)
	if err = e.Find(&stars, &Star{UID: u.Id}); err != nil {
		return fmt.Errorf("get all stars: %v", err)
	}
	for i := range stars {
		if _, err = e.Exec("UPDATE `repository` SET num_stars=num_stars-1 WHERE id=?", stars[i].RepoID); err != nil {
			return fmt.Errorf("decrease repository star number[%d]: %v", stars[i].RepoID, err)
		}
	}
	// ***** END: Star *****

	// ***** START: Follow *****
	followers := make([]*Follow, 0, 10)
	if err = e.Find(&followers, &Follow{UserID: u.Id}); err != nil {
		return fmt.Errorf("get all followers: %v", err)
	}
	for i := range followers {
		if _, err = e.Exec("UPDATE `user` SET num_followers=num_followers-1 WHERE id=?", followers[i].UserID); err != nil {
			return fmt.Errorf("decrease user follower number[%d]: %v", followers[i].UserID, err)
		}
	}
	// ***** END: Follow *****

	if err = deleteBeans(e,
		&AccessToken{UID: u.Id},
		&Collaboration{UserID: u.Id},
		&Access{UserID: u.Id},
		&Watch{UserID: u.Id},
		&Star{UID: u.Id},
		&Follow{FollowID: u.Id},
		&Action{UserID: u.Id},
		&IssueUser{UID: u.Id},
		&EmailAddress{UID: u.Id},
	); err != nil {
		return fmt.Errorf("deleteBeans: %v", err)
	}

	// ***** START: PublicKey *****
	keys := make([]*PublicKey, 0, 10)
	if err = e.Find(&keys, &PublicKey{OwnerID: u.Id}); err != nil {
		return fmt.Errorf("get all public keys: %v", err)
	}
	for _, key := range keys {
		if err = deletePublicKey(e, key.ID); err != nil {
			return fmt.Errorf("deletePublicKey: %v", err)
		}
	}
	// ***** END: PublicKey *****

	// Clear assignee.
	if _, err = e.Exec("UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?", u.Id); err != nil {
		return fmt.Errorf("clear assignee: %v", err)
	}

	if _, err = e.Id(u.Id).Delete(new(User)); err != nil {
		return fmt.Errorf("Delete: %v", err)
	}

	// FIXME: system notice
	// Note: There are something just cannot be roll back,
	//	so just keep error logs of those operations.

	RewriteAllPublicKeys()
	os.RemoveAll(UserPath(u.Name))
	os.Remove(u.CustomAvatarPath())

	return nil
}
Exemplo n.º 11
0
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
	comment := &Comment{
		Type:      opts.Type,
		PosterID:  opts.Doer.ID,
		Poster:    opts.Doer,
		IssueID:   opts.Issue.ID,
		CommitID:  opts.CommitID,
		CommitSHA: opts.CommitSHA,
		Line:      opts.LineNum,
		Content:   opts.Content,
	}
	if _, err = e.Insert(comment); err != nil {
		return nil, err
	}

	// Compose comment action, could be plain comment, close or reopen issue/pull request.
	// This object will be used to notify watchers in the end of function.
	act := &Action{
		ActUserID:    opts.Doer.ID,
		ActUserName:  opts.Doer.Name,
		Content:      fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]),
		RepoID:       opts.Repo.ID,
		RepoUserName: opts.Repo.Owner.Name,
		RepoName:     opts.Repo.Name,
		IsPrivate:    opts.Repo.IsPrivate,
	}

	// Check comment type.
	switch opts.Type {
	case COMMENT_TYPE_COMMENT:
		act.OpType = ACTION_COMMENT_ISSUE

		if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
			return nil, err
		}

		// Check attachments
		attachments := make([]*Attachment, 0, len(opts.Attachments))
		for _, uuid := range opts.Attachments {
			attach, err := getAttachmentByUUID(e, uuid)
			if err != nil {
				if IsErrAttachmentNotExist(err) {
					continue
				}
				return nil, fmt.Errorf("getAttachmentByUUID [%s]: %v", uuid, err)
			}
			attachments = append(attachments, attach)
		}

		for i := range attachments {
			attachments[i].IssueID = opts.Issue.ID
			attachments[i].CommentID = comment.ID
			// No assign value could be 0, so ignore AllCols().
			if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
				return nil, fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err)
			}
		}

	case COMMENT_TYPE_REOPEN:
		act.OpType = ACTION_REOPEN_ISSUE
		if opts.Issue.IsPull {
			act.OpType = ACTION_REOPEN_PULL_REQUEST
		}

		if opts.Issue.IsPull {
			_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID)
		} else {
			_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID)
		}
		if err != nil {
			return nil, err
		}

	case COMMENT_TYPE_CLOSE:
		act.OpType = ACTION_CLOSE_ISSUE
		if opts.Issue.IsPull {
			act.OpType = ACTION_CLOSE_PULL_REQUEST
		}

		if opts.Issue.IsPull {
			_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID)
		} else {
			_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID)
		}
		if err != nil {
			return nil, err
		}

	}

	// Notify watchers for whatever action comes in, ignore if no action type.
	if act.OpType > 0 {
		if err = notifyWatchers(e, act); err != nil {
			log.Error(4, "notifyWatchers: %v", err)
		}
		comment.MailParticipants(act.OpType, opts.Issue)
	}

	return comment, nil
}
Exemplo n.º 12
0
Arquivo: issue.go Projeto: yweber/gogs
// It's caller's responsibility to create action.
func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64, uuids []string, isPull bool) (err error) {
	issue.Name = strings.TrimSpace(issue.Name)
	issue.Index = repo.NextIssueIndex()

	if issue.AssigneeID > 0 {
		// Silently drop invalid assignee
		valid, err := hasAccess(e, &User{ID: issue.AssigneeID}, repo, ACCESS_MODE_WRITE)
		if err != nil {
			return fmt.Errorf("hasAccess: %v", err)
		} else if !valid {
			issue.AssigneeID = 0
		}
	}

	if _, err = e.Insert(issue); err != nil {
		return err
	}

	if isPull {
		_, err = e.Exec("UPDATE `repository` SET num_pulls=num_pulls+1 WHERE id=?", issue.RepoID)
	} else {
		_, err = e.Exec("UPDATE `repository` SET num_issues=num_issues+1 WHERE id=?", issue.RepoID)
	}
	if err != nil {
		return err
	}

	if len(labelIDs) > 0 {
		// During the session, SQLite3 dirver cannot handle retrieve objects after update something.
		// So we have to get all needed labels first.
		labels := make([]*Label, 0, len(labelIDs))
		if err = e.In("id", labelIDs).Find(&labels); err != nil {
			return fmt.Errorf("find all labels: %v", err)
		}

		for _, label := range labels {
			if label.RepoID != repo.ID {
				continue
			}

			if err = issue.addLabel(e, label); err != nil {
				return fmt.Errorf("addLabel: %v", err)
			}
		}
	}

	if issue.MilestoneID > 0 {
		if err = changeMilestoneAssign(e, 0, issue); err != nil {
			return err
		}
	}

	if err = newIssueUsers(e, repo, issue); err != nil {
		return err
	}

	// Check attachments.
	attachments := make([]*Attachment, 0, len(uuids))
	for _, uuid := range uuids {
		attach, err := getAttachmentByUUID(e, uuid)
		if err != nil {
			if IsErrAttachmentNotExist(err) {
				continue
			}
			return fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err)
		}
		attachments = append(attachments, attach)
	}

	for i := range attachments {
		attachments[i].IssueID = issue.ID
		// No assign value could be 0, so ignore AllCols().
		if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
			return fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err)
		}
	}

	return issue.loadAttributes(e)
}
Exemplo n.º 13
0
func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) error {
	if !IsTeamMember(orgId, teamId, uid) {
		return nil
	}

	// Get team and its repositories.
	t, err := GetTeamById(teamId)
	if err != nil {
		return err
	}

	// Check if the user to delete is the last member in owner team.
	if t.IsOwnerTeam() && t.NumMembers == 1 {
		return ErrLastOrgOwner
	}

	t.NumMembers--

	if err = t.GetRepositories(); err != nil {
		return err
	}

	// Get organization.
	org, err := GetUserById(orgId)
	if err != nil {
		return err
	}

	// Get user.
	u, err := GetUserById(uid)
	if err != nil {
		return err
	}

	tu := &TeamUser{
		Uid:    uid,
		OrgId:  orgId,
		TeamId: teamId,
	}

	if _, err := sess.Delete(tu); err != nil {
		sess.Rollback()
		return err
	} else if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
		sess.Rollback()
		return err
	}

	// Delete access to team repositories.
	for _, repo := range t.Repos {
		auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, teamId)
		if err != nil {
			sess.Rollback()
			return err
		}

		access := &Access{
			UserName: u.LowerName,
			RepoName: path.Join(org.LowerName, repo.LowerName),
		}
		// Delete access if this is the last team user belongs to.
		if auth == 0 {
			if _, err = sess.Delete(access); err != nil {
				sess.Rollback()
				return fmt.Errorf("fail to delete access: %v", err)
			} else if err = WatchRepo(u.Id, repo.Id, false); err != nil {
				sess.Rollback()
				return err
			}
		} else if auth < t.Authorize {
			// Downgrade authorize level.
			if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil {
				sess.Rollback()
				return err
			}
		}
	}

	// This must exist.
	ou := new(OrgUser)
	_, err = sess.Where("uid=?", uid).And("org_id=?", org.Id).Get(ou)
	if err != nil {
		sess.Rollback()
		return err
	}
	ou.NumTeams--
	if t.IsOwnerTeam() {
		ou.IsOwner = false
	}
	if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil {
		sess.Rollback()
		return err
	}
	return nil
}
Exemplo n.º 14
0
func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
	opts.Issue.Title = strings.TrimSpace(opts.Issue.Title)
	opts.Issue.Index = opts.Repo.NextIssueIndex()

	if opts.Issue.MilestoneID > 0 {
		milestone, err := getMilestoneByRepoID(e, opts.Issue.RepoID, opts.Issue.MilestoneID)
		if err != nil && !IsErrMilestoneNotExist(err) {
			return fmt.Errorf("getMilestoneByID: %v", err)
		}

		// Assume milestone is invalid and drop silently.
		opts.Issue.MilestoneID = 0
		if milestone != nil {
			opts.Issue.MilestoneID = milestone.ID
			opts.Issue.Milestone = milestone
			if err = changeMilestoneAssign(e, opts.Issue, -1); err != nil {
				return err
			}
		}
	}

	if opts.Issue.AssigneeID > 0 {
		assignee, err := getUserByID(e, opts.Issue.AssigneeID)
		if err != nil && !IsErrUserNotExist(err) {
			return fmt.Errorf("getUserByID: %v", err)
		}

		// Assume assignee is invalid and drop silently.
		opts.Issue.AssigneeID = 0
		if assignee != nil {
			valid, err := hasAccess(e, assignee, opts.Repo, ACCESS_MODE_WRITE)
			if err != nil {
				return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assignee.ID, opts.Repo.ID, err)
			}
			if valid {
				opts.Issue.AssigneeID = assignee.ID
				opts.Issue.Assignee = assignee
			}
		}
	}

	// Milestone and assignee validation should happen before insert actual object.
	if _, err = e.Insert(opts.Issue); err != nil {
		return err
	}

	if opts.IsPull {
		_, err = e.Exec("UPDATE `repository` SET num_pulls = num_pulls + 1 WHERE id = ?", opts.Issue.RepoID)
	} else {
		_, err = e.Exec("UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?", opts.Issue.RepoID)
	}
	if err != nil {
		return err
	}

	if len(opts.LableIDs) > 0 {
		// During the session, SQLite3 dirver cannot handle retrieve objects after update something.
		// So we have to get all needed labels first.
		labels := make([]*Label, 0, len(opts.LableIDs))
		if err = e.In("id", opts.LableIDs).Find(&labels); err != nil {
			return fmt.Errorf("find all labels [label_ids: %v]: %v", opts.LableIDs, err)
		}

		for _, label := range labels {
			// Silently drop invalid labels.
			if label.RepoID != opts.Repo.ID {
				continue
			}

			if err = opts.Issue.addLabel(e, label); err != nil {
				return fmt.Errorf("addLabel [id: %d]: %v", label.ID, err)
			}
		}
	}

	if err = newIssueUsers(e, opts.Repo, opts.Issue); err != nil {
		return err
	}

	if len(opts.Attachments) > 0 {
		attachments, err := getAttachmentsByUUIDs(e, opts.Attachments)
		if err != nil {
			return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %v", opts.Attachments, err)
		}

		for i := 0; i < len(attachments); i++ {
			attachments[i].IssueID = opts.Issue.ID
			if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
				return fmt.Errorf("update attachment [id: %d]: %v", attachments[i].ID, err)
			}
		}
	}

	return opts.Issue.loadAttributes(e)
}