Skip to content

Commit

Permalink
[MI-2814] Done the review fixes of github PR mattermost#636 (#22)
Browse files Browse the repository at this point in the history
* [MI-2736]: Review fixes done
1. Improved code readability

* [MI-2736]: Review fixes done
1. Fixed linting errors

* [MI-2736]: Review fixes done
1. Fixed linting error

* [MI-2736]: Review fixes done
1. Improved code readability

* [MI-2736]: Review fixes done
1. Changed the case of few endpoints to snake case

* [MI-2814]: Review fixes done of github PR mattermost#636

* [MI-2814]: Review fixes done
1. Made comment field editable when trying to add a comment from message action.

* [MI-2814]: Review fixes done
1. Improved code readability

* [MI-2814]: Review fixes done
1. Used GET method on getting the issue info
2. Improved code readability

* [MI-2814]: Improved linting error
  • Loading branch information
Nityanand13 authored Mar 13, 2023
1 parent 3cecd5d commit 779f10f
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 227 deletions.
132 changes: 32 additions & 100 deletions server/plugin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ func (p *Plugin) initializeAPI() {
apiRouter.HandleFunc("/create_issue", p.checkAuth(p.attachUserContext(p.createIssue), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/close_or_reopen_issue", p.checkAuth(p.attachUserContext(p.closeOrReopenIssue), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/update_issue", p.checkAuth(p.attachUserContext(p.updateIssue), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/edit_issue_modal", p.checkAuth(p.attachUserContext(p.openIssueEditModal), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/close_reopen_issue_modal", p.checkAuth(p.attachUserContext(p.openCloseOrReopenIssueModal), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/attach_comment_issue_modal", p.checkAuth(p.attachUserContext(p.openAttachCommentIssueModal), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/issue_info", p.checkAuth(p.attachUserContext(p.getIssueInfo), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/create_issue_comment", p.checkAuth(p.attachUserContext(p.createIssueComment), ResponseTypePlain)).Methods(http.MethodPost)
apiRouter.HandleFunc("/mentions", p.checkAuth(p.attachUserContext(p.getMentions), ResponseTypePlain)).Methods(http.MethodGet)
apiRouter.HandleFunc("/unreads", p.checkAuth(p.attachUserContext(p.getUnreads), ResponseTypePlain)).Methods(http.MethodGet)
Expand Down Expand Up @@ -959,99 +957,38 @@ func (p *Plugin) updateSettings(c *serializer.UserContext, w http.ResponseWriter
p.writeJSON(w, info.Settings)
}

func (p *Plugin) openAttachCommentIssueModal(c *serializer.UserContext, w http.ResponseWriter, r *http.Request) {
req := &serializer.OpenCreateCommentOrEditIssueModalRequestBody{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.Log.WithError(err).Warnf("Error decoding the JSON body")
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: "Please provide a valid JSON object.", StatusCode: http.StatusBadRequest})
return
}

userID := r.Header.Get(constants.HeaderMattermostUserID)
post, appErr := p.API.GetPost(req.PostID)
if appErr != nil {
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s", req.PostID), StatusCode: http.StatusInternalServerError})
return
}
if post == nil {
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s : not found", req.PostID), StatusCode: http.StatusNotFound})
return
}

p.API.PublishWebSocketEvent(
wsEventAttachCommentToIssue,
map[string]interface{}{
"postId": post.Id,
"owner": req.RepoOwner,
"repo": req.RepoName,
"number": req.IssueNumber,
},
&model.WebsocketBroadcast{UserId: userID},
)
}

func (p *Plugin) openCloseOrReopenIssueModal(c *serializer.UserContext, w http.ResponseWriter, r *http.Request) {
req := &serializer.OpenCreateCommentOrEditIssueModalRequestBody{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.Log.WithError(err).Warnf("Error decoding the JSON body")
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: "Please provide a valid JSON object.", StatusCode: http.StatusBadRequest})
return
}

userID := r.Header.Get(constants.HeaderMattermostUserID)

post, appErr := p.API.GetPost(req.PostID)
if appErr != nil {
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s", req.PostID), StatusCode: http.StatusInternalServerError})
return
}
if post == nil {
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s : not found", req.PostID), StatusCode: http.StatusNotFound})
return
}

p.API.PublishWebSocketEvent(
wsEventCloseOrReopenIssue,
map[string]interface{}{
"channel_id": post.ChannelId,
"owner": req.RepoOwner,
"repo": req.RepoName,
"number": req.IssueNumber,
"status": req.Status,
"postId": req.PostID,
},
&model.WebsocketBroadcast{UserId: userID},
)
}
func (p *Plugin) getIssueInfo(c *serializer.UserContext, w http.ResponseWriter, r *http.Request) {
owner := r.FormValue(constants.OwnerQueryParam)
repo := r.FormValue(constants.RepoQueryParam)
number := r.FormValue(constants.NumberQueryParam)
postID := r.FormValue(constants.PostIDQueryParam)

func (p *Plugin) openIssueEditModal(c *serializer.UserContext, w http.ResponseWriter, r *http.Request) {
req := &serializer.OpenCreateCommentOrEditIssueModalRequestBody{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.Log.WithError(err).Warnf("Error decoding the JSON body")
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: "Please provide a valid JSON object.", StatusCode: http.StatusBadRequest})
issueNumber, err := strconv.Atoi(number)
if err != nil {
p.writeAPIError(w, &serializer.APIErrorResponse{Message: "Invalid param 'number'.", StatusCode: http.StatusBadRequest})
return
}

githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo)
issue, _, err := githubClient.Issues.Get(c.Ctx, req.RepoOwner, req.RepoName, req.IssueNumber)
issue, _, err := githubClient.Issues.Get(c.Ctx, owner, repo, issueNumber)
if err != nil {
// If the issue is not found, it probably belongs to a private repo.
// Return an empty response in that case.
var gerr *github.ErrorResponse
if errors.As(err, &gerr) && gerr.Response.StatusCode == http.StatusNotFound {
c.Log.WithError(err).With(logger.LogContext{
"owner": req.RepoOwner,
"repo": req.RepoName,
"number": req.IssueNumber,
"owner": owner,
"repo": repo,
"number": issueNumber,
}).Debugf("Issue not found")
p.writeJSON(w, nil)
return
}

c.Log.WithError(err).With(logger.LogContext{
"owner": req.RepoOwner,
"repo": req.RepoName,
"number": req.IssueNumber,
"owner": owner,
"repo": repo,
"number": issueNumber,
}).Debugf("Could not get the issue")
p.writeAPIError(w, &serializer.APIErrorResponse{Message: "Could not get the issue", StatusCode: http.StatusInternalServerError})
return
Expand Down Expand Up @@ -1080,35 +1017,30 @@ func (p *Plugin) openIssueEditModal(c *serializer.UserContext, w http.ResponseWr
milestoneNumber = *issue.Milestone.Number
}

userID := r.Header.Get(constants.HeaderMattermostUserID)
post, appErr := p.API.GetPost(req.PostID)
post, appErr := p.API.GetPost(postID)
if appErr != nil {
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s", req.PostID), StatusCode: http.StatusInternalServerError})
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s", postID), StatusCode: http.StatusInternalServerError})
return
}
if post == nil {
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s : not found", req.PostID), StatusCode: http.StatusNotFound})
p.writeAPIError(w, &serializer.APIErrorResponse{ID: "", Message: fmt.Sprintf("failed to load the post %s : not found", postID), StatusCode: http.StatusNotFound})
return
}

p.API.PublishWebSocketEvent(
wsEventCreateOrUpdateIssue,
map[string]interface{}{
"title": *issue.Title,
"channel_id": post.ChannelId,
"postId": req.PostID,
"milestone_title": milestoneTitle,
"milestone_number": milestoneNumber,
"assignees": assignees,
"labels": labels,
"description": description,
"repo_full_name": fmt.Sprintf("%s/%s", req.RepoOwner, req.RepoName),
"issue_number": *issue.Number,
},
&model.WebsocketBroadcast{UserId: userID},
)
issueInfo := map[string]interface{}{
"title": *issue.Title,
"channel_id": post.ChannelId,
"postId": postID,
"milestone_title": milestoneTitle,
"milestone_number": milestoneNumber,
"assignees": assignees,
"labels": labels,
"description": description,
"repo_full_name": fmt.Sprintf("%s/%s", owner, repo),
"issue_number": *issue.Number,
}

p.writeJSON(w, issue)
p.writeJSON(w, issueInfo)
}

func (p *Plugin) getIssueByNumber(c *serializer.UserContext, w http.ResponseWriter, r *http.Request) {
Expand Down
10 changes: 4 additions & 6 deletions server/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,9 @@ const (
wsEventConnect = "connect"
wsEventDisconnect = "disconnect"
// WSEventConfigUpdate is the WebSocket event to update the configurations on webapp.
WSEventConfigUpdate = "config_update"
wsEventRefresh = "refresh"
wsEventCreateOrUpdateIssue = "createOrUpdateIssue"
wsEventCloseOrReopenIssue = "closeOrReopenIssue"
wsEventAttachCommentToIssue = "attachCommentToIssue"
WSEventConfigUpdate = "config_update"
wsEventRefresh = "refresh"
wsEventCreateIssue = "createIssue"

WSEventRefresh = "refresh"

Expand Down Expand Up @@ -482,7 +480,7 @@ func (p *Plugin) disconnectGitHubAccount(userID string) {

func (p *Plugin) openIssueCreateModal(userID string, channelID string, title string) {
p.API.PublishWebSocketEvent(
wsEventCreateOrUpdateIssue,
wsEventCreateIssue,
map[string]interface{}{
"title": title,
"channel_id": channelID,
Expand Down
8 changes: 0 additions & 8 deletions server/serializer/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,3 @@ type CommentAndCloseRequest struct {
Status string `json:"status"`
PostID string `json:"postId"`
}

type OpenCreateCommentOrEditIssueModalRequestBody struct {
RepoOwner string `json:"repo_owner"`
RepoName string `json:"repo_name"`
IssueNumber int `json:"issue_number"`
PostID string `json:"postId"`
Status string `json:"status"`
}
40 changes: 2 additions & 38 deletions webapp/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,47 +211,11 @@ export function getMilestoneOptions(repo) {
};
}

export function attachCommentIssueModal(payload) {
export function getIssueInfo(owner, repo, issueNumber, postID) {
return async (dispatch, getState) => {
let data;
try {
data = await Client.attachCommentIssueModal(payload);
} catch (error) {
return {error};
}

const connected = await checkAndHandleNotConnected(data)(dispatch, getState);
if (!connected) {
return {error: data};
}

return {data};
};
}

export function editIssueModal(payload) {
return async (dispatch, getState) => {
let data;
try {
data = await Client.editIssueModal(payload);
} catch (error) {
return {error};
}

const connected = await checkAndHandleNotConnected(data)(dispatch, getState);
if (!connected) {
return {error: data};
}

return {data};
};
}

export function closeOrReopenIssueModal(payload) {
return async (dispatch, getState) => {
let data;
try {
data = await Client.closeOrReopenIssueModal(payload);
data = await Client.getIssueInfo(owner, repo, issueNumber, postID);
} catch (error) {
return {error};
}
Expand Down
12 changes: 2 additions & 10 deletions webapp/src/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,8 @@ import {ClientError} from 'mattermost-redux/client/client4';
import {id as pluginId} from '../manifest';

export default class Client {
editIssueModal = async (payload) => {
return this.doPost(`${this.url}/edit_issue_modal`, payload);
}

closeOrReopenIssueModal = async (payload) => {
return this.doPost(`${this.url}/close_reopen_issue_modal`, payload);
}

attachCommentIssueModal = async (payload) => {
return this.doPost(`${this.url}/attach_comment_issue_modal`, payload);
getIssueInfo = async (owner, repo, issueNumber, postID) => {
return this.doGet(`${this.url}/issue_info?owner=${owner}&repo=${repo}&number=${issueNumber}&postId=${postID}`);
}

setServerRoute(url) {
Expand Down
9 changes: 5 additions & 4 deletions webapp/src/components/github_issue/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Theme} from 'mattermost-redux/types/preferences';
import {Post} from 'mattermost-redux/types/posts';
import {useDispatch} from 'react-redux';

import {attachCommentIssueModal, editIssueModal, closeOrReopenIssueModal} from '../../actions';
import {openCreateCommentOnIssueModal, openCreateOrUpdateIssueModal, openCloseOrReopenIssueModal} from '../../actions';

type GithubIssueProps = {
theme: Theme,
Expand All @@ -24,24 +24,25 @@ const GithubIssue = ({theme, post}: GithubIssueProps) => {
issue_number: postProps.issue_number,
postId: post.id,
status: postProps.status,
channel_id: post.channel_id,
};

const content = (
<div>
<button
style={{...style.button, ...style.other_buttons}}
className='btn btn-primary'
onClick={() => dispatch(attachCommentIssueModal(issue))}
onClick={() => dispatch(openCreateCommentOnIssueModal(issue))}
>{'Comment'}</button>
<button
style={{...style.button, ...style.other_buttons}}
className='btn btn-primary'
onClick={() => dispatch(editIssueModal(issue))}
onClick={() => dispatch(openCreateOrUpdateIssueModal(issue))}
>{'Edit'}</button>
<button
style={{...style.button, ...{...postProps.status === 'Close' ? style.close_or_reopen_button : style.other_buttons}}}
className='btn btn-primary'
onClick={() => dispatch(closeOrReopenIssueModal(issue))}
onClick={() => dispatch(openCloseOrReopenIssueModal(issue))}
>{postProps.status}</button>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ export default class AttachIssueModal extends PureComponent {
}

if (!this.state.issueValue) {
const {owner, repo, number} = this.props.messageData ?? {};
const {repo_owner, repo_name, issue_number} = this.props.messageData ?? {};
const issue = {
owner,
repo,
number,
owner: repo_owner,
repo: repo_name,
number: issue_number,
comment: this.state.comment,
post_id: this.props.post.id,
show_attached_message: false,
Expand Down Expand Up @@ -72,7 +72,7 @@ export default class AttachIssueModal extends PureComponent {
owner,
repo,
number,
comment: this.props.post.message,
comment: this.state.comment,
post_id: this.props.post.id,
show_attached_message: true,
};
Expand Down Expand Up @@ -106,17 +106,23 @@ export default class AttachIssueModal extends PureComponent {
});
};

componentDidUpdate(prevProps) {
if (this.props.post && !this.props.messageData && !prevProps.post) {
this.setState({comment: this.props.post.message}); // eslint-disable-line react/no-did-update-set-state
}
}

render() {
const {error, submitting, comment, issueValue} = this.state;
const {visible, theme, messageData, post} = this.props;
const {visible, theme, messageData} = this.props;
const style = getStyle(theme);
if (!visible) {
return null;
}

const {number} = messageData ?? {};
const modalTitle = number ? 'Create a comment to GitHub Issue' : 'Attach Message to GitHub Issue';
const component = number ? (
const {issue_number} = messageData ?? {};
const modalTitle = issue_number ? 'Create a comment to GitHub Issue' : 'Attach Message to GitHub Issue';
const component = issue_number ? (
<div>
<Input
label='Create a comment'
Expand All @@ -138,10 +144,9 @@ export default class AttachIssueModal extends PureComponent {
<Input
label='Message Attached to GitHub Issue'
type='textarea'
isDisabled={true}
value={post?.message}
value={comment}
disabled={false}
readOnly={true}
onChange={this.handleIssueCommentChange}
/>
</div>
);
Expand Down
6 changes: 3 additions & 3 deletions webapp/src/components/modals/close_reopen_issue/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ const CloseOrReopenIssueModal = ({theme}: {theme: Theme}) => {
channel_id: messageData.channel_id,
issue_comment: comment,
status_reason: currentStatus,
repo: messageData.repo,
number: messageData.number,
owner: messageData.owner,
repo: messageData.repo_name,
number: messageData.issue_number,
owner: messageData.repo_owner,
status: messageData.status,
postId: messageData.postId,
};
Expand Down
Loading

0 comments on commit 779f10f

Please sign in to comment.