From ff8ba9bc9841867f370a4aaf8ef02fb8b271b152 Mon Sep 17 00:00:00 2001 From: kshitij Date: Wed, 17 Jul 2024 19:07:34 +0530 Subject: [PATCH 1/9] [MM-611]: Added the feature to select the default repository for the channel per User --- server/plugin/api.go | 62 ++++++++- server/plugin/command.go | 119 +++++++++++++++++- server/plugin/plugin.go | 1 + webapp/src/actions/index.ts | 4 +- webapp/src/client/client.js | 4 +- .../github_repo_selector.jsx | 17 ++- .../components/github_repo_selector/index.js | 3 + webapp/src/reducers/index.ts | 4 +- webapp/src/types/github_types.ts | 10 ++ 9 files changed, 207 insertions(+), 17 deletions(-) diff --git a/server/plugin/api.go b/server/plugin/api.go index 8dffebdd9..67bf5430d 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -30,6 +30,8 @@ const ( requestTimeout = 30 * time.Second oauthCompleteTimeout = 2 * time.Minute + + channelIDParam = "channelId" ) type OAuthState struct { @@ -1224,11 +1226,32 @@ func getRepositoryListByOrg(c context.Context, org string, githubClient *github. return allRepos, http.StatusOK, nil } +func getRepository(c context.Context, org string, repo string, githubClient *github.Client) (*github.Repository, int, error) { + repository, resp, err := githubClient.Repositories.Get(c, org, repo) + if err != nil { + return nil, resp.StatusCode, err + } + + return repository, http.StatusOK, nil +} + func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http.Request) { githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo) org := p.getConfiguration().GitHubOrg + channelID := r.URL.Query().Get(channelIDParam) + + if channelID == "" { + p.writeAPIError(w, &APIErrorResponse{Message: "Bad request: missing channelId", StatusCode: http.StatusBadRequest}) + return + } + + defaultRepo, dErr := p.GetDefaultRepo(c.GHInfo.UserID, channelID) + if dErr != nil { + c.Log.Warnf("Failed to get the default repo for the channel") + } + var allRepos []*github.Repository var err error var statusCode int @@ -1259,18 +1282,45 @@ func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http. } } - // Only send down fields to client that are needed - type RepositoryResponse struct { + type RepoResponse struct { Name string `json:"name,omitempty"` FullName string `json:"full_name,omitempty"` Permissions map[string]bool `json:"permissions,omitempty"` } - resp := make([]RepositoryResponse, len(allRepos)) + // Only send down fields to client that are needed + type RepositoryResponse struct { + DefaultRepo RepoResponse `json:"defaultRepo,omitempty"` + Repo []RepoResponse `json:"repo,omitempty"` + } + + repoResp := make([]RepoResponse, len(allRepos)) for i, r := range allRepos { - resp[i].Name = r.GetName() - resp[i].FullName = r.GetFullName() - resp[i].Permissions = r.GetPermissions() + repoResp[i].Name = r.GetName() + repoResp[i].FullName = r.GetFullName() + repoResp[i].Permissions = r.GetPermissions() + } + + resp := RepositoryResponse{ + Repo: repoResp, + } + + if defaultRepo != "" { + config := p.getConfiguration() + baseURL := config.getBaseURL() + owner, repo := parseOwnerAndRepo(defaultRepo, baseURL) + defaultRepository, _, err := getRepository(c.Ctx, owner, repo, githubClient) + if err != nil { + c.Log.Warnf("Failed to get the default repo %s/%s", owner, repo) + } + + if defaultRepository != nil { + resp.DefaultRepo = RepoResponse{ + Name: *defaultRepository.Name, + FullName: *defaultRepository.FullName, + Permissions: defaultRepository.Permissions, + } + } } p.writeJSON(w, resp) diff --git a/server/plugin/command.go b/server/plugin/command.go index 14071d9db..d08bedf11 100644 --- a/server/plugin/command.go +++ b/server/plugin/command.go @@ -115,7 +115,7 @@ func (p *Plugin) getCommand(config *Configuration) (*model.Command, error) { return &model.Command{ Trigger: "github", AutoComplete: true, - AutoCompleteDesc: "Available commands: connect, disconnect, todo, subscriptions, issue, me, mute, settings, help, about", + AutoCompleteDesc: "Available commands: connect, disconnect, todo, subscriptions, issue, default-repo, me, mute, settings, help, about", AutoCompleteHint: "[command]", AutocompleteData: getAutocompleteData(config), AutocompleteIconData: iconData, @@ -702,6 +702,107 @@ func (p *Plugin) handleIssue(_ *plugin.Context, args *model.CommandArgs, paramet } } +func (p *Plugin) handleDefaultRepo(c *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { + if len(parameters) == 0 { + return "Invalid issue command. Available command is 'set', 'get' and 'unset'." + } + + command := parameters[0] + parameters = parameters[1:] + + switch { + case command == "set": + return p.handleSetDefaultRepo(c, args, parameters, userInfo) + case command == "get": + return p.handleGetDefaultRepo(c, args, parameters, userInfo) + case command == "unset": + return p.handleUnSetDefaultRepo(c, args, parameters, userInfo) + default: + return fmt.Sprintf("Unknown subcommand %v", command) + } +} + +func (p *Plugin) handleSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { + if len(parameters) == 0 { + return "Please specify a repository." + } + + repo := parameters[0] + config := p.getConfiguration() + baseURL := config.getBaseURL() + owner, repo := parseOwnerAndRepo(repo, baseURL) + if owner == "" && repo == "" { + return "invalid repository" + } + + owner = strings.ToLower(owner) + repo = strings.ToLower(repo) + + if config.GitHubOrg != "" && strings.ToLower(config.GitHubOrg) != owner { + return "repository is not part of the locked github organization" + } + + ctx := context.Background() + githubClient := p.githubConnectUser(ctx, userInfo) + + ghRepo, _, _ := githubClient.Repositories.Get(ctx, owner, repo) + if ghRepo == nil { + return fmt.Sprintf("unknown repository %s", fullNameFromOwnerAndRepo(owner, repo)) + } + + if _, err := p.store.Set(args.ChannelId+"_"+userInfo.UserID+"-default-repo", []byte(owner+"/"+repo)); err != nil { + return "error occurred saving the default repo" + } + + repoLink := baseURL + owner + "/" + repo + successMsg := fmt.Sprintf("The default repo has been set to [%s/%s](%s)", owner, repo, repoLink) + + return successMsg +} + +func (p *Plugin) GetDefaultRepo(userID string, channelID string) (string, error) { + var defaultRepoBytes []byte + if err := p.store.Get(channelID+"_"+userID+"-default-repo", &defaultRepoBytes); err != nil { + return "", err + } + + return string(defaultRepoBytes), nil +} + +func (p *Plugin) handleGetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { + defaultRepo, err := p.GetDefaultRepo(userInfo.UserID, args.ChannelId) + if err != nil { + p.client.Log.Warn("Not able to get the default repo", "error", err.Error()) + return "error occurred while getting the default repo" + } + + if defaultRepo == "" { + return "you have not set a default repository for this channel" + } + + config := p.getConfiguration() + repoLink := config.getBaseURL() + defaultRepo + return fmt.Sprintf("The default repository is [%s](%s)", defaultRepo, repoLink) +} + +func (p *Plugin) handleUnSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { + defaultRepo, err := p.GetDefaultRepo(userInfo.UserID, args.ChannelId) + if err != nil { + p.client.Log.Warn("Not able to get the default repo", "error", err.Error()) + return "error occurred while getting the default repo" + } + + if defaultRepo == "" { + return "you have not set a default repository for this channel" + } + + if err := p.store.Delete(args.ChannelId + "_" + userInfo.UserID + "-default-repo"); err != nil { + return "error occurred while unsetting the repo for this channel" + } + + return "The default repository has been unset successfully" +} + func (p *Plugin) handleSetup(c *plugin.Context, args *model.CommandArgs, parameters []string) string { userID := args.UserId isSysAdmin, err := p.isAuthorizedSysAdmin(userID) @@ -871,7 +972,7 @@ func getAutocompleteData(config *Configuration) *model.AutocompleteData { return github } - github := model.NewAutocompleteData("github", "[command]", "Available commands: connect, disconnect, todo, subscriptions, issue, me, mute, settings, help, about") + github := model.NewAutocompleteData("github", "[command]", "Available commands: connect, disconnect, todo, subscriptions, issue, default-repo, me, mute, settings, help, about") connect := model.NewAutocompleteData("connect", "", "Connect your Mattermost account to your GitHub account") if config.EnablePrivateRepo { @@ -944,6 +1045,20 @@ func getAutocompleteData(config *Configuration) *model.AutocompleteData { github.AddCommand(issue) + defaultRepo := model.NewAutocompleteData("default-repo", "[command]", "Available commands: set, get, unset") + defaultRepoSet := model.NewAutocompleteData("set", "[owner/repo]", "Set the default repository for the channel") + defaultRepoSet.AddTextArgument("Owner/repo to set as a default repository", "[owner/repo]", "") + + defaultRepoGet := model.NewAutocompleteData("get", "", "Get the default repository already set for the channel") + + defaultRepoDelete := model.NewAutocompleteData("unset", "", "Unset the default repository set for the channel") + + defaultRepo.AddCommand(defaultRepoSet) + defaultRepo.AddCommand(defaultRepoGet) + defaultRepo.AddCommand(defaultRepoDelete) + + github.AddCommand(defaultRepo) + me := model.NewAutocompleteData("me", "", "Display the connected GitHub account") github.AddCommand(me) diff --git a/server/plugin/plugin.go b/server/plugin/plugin.go index d360be6e4..fda94d0cc 100644 --- a/server/plugin/plugin.go +++ b/server/plugin/plugin.go @@ -124,6 +124,7 @@ func NewPlugin() *Plugin { "": p.handleHelp, "settings": p.handleSettings, "issue": p.handleIssue, + "default-repo": p.handleDefaultRepo, } p.createGithubEmojiMap() diff --git a/webapp/src/actions/index.ts b/webapp/src/actions/index.ts index af1f7ee22..bd050a2d9 100644 --- a/webapp/src/actions/index.ts +++ b/webapp/src/actions/index.ts @@ -70,11 +70,11 @@ export function getReviewsDetails(prList: PrsDetailsData[]) { }; } -export function getRepos() { +export function getRepos(channelId: string) { return async (dispatch: DispatchFunc) => { let data; try { - data = await Client.getRepositories(); + data = await Client.getRepositories(channelId); } catch (error) { dispatch({ type: ActionTypes.RECEIVED_REPOSITORIES, diff --git a/webapp/src/client/client.js b/webapp/src/client/client.js index e381dbf5d..82d472dbe 100644 --- a/webapp/src/client/client.js +++ b/webapp/src/client/client.js @@ -31,8 +31,8 @@ export default class Client { return this.doPost(`${this.url}/user`, {user_id: userID}); } - getRepositories = async () => { - return this.doGet(`${this.url}/repositories`); + getRepositories = async (channelId) => { + return this.doGet(`${this.url}/repositories?channelId=${channelId}`); } getLabels = async (repo) => { diff --git a/webapp/src/components/github_repo_selector/github_repo_selector.jsx b/webapp/src/components/github_repo_selector/github_repo_selector.jsx index c6352203a..631fba885 100644 --- a/webapp/src/components/github_repo_selector/github_repo_selector.jsx +++ b/webapp/src/components/github_repo_selector/github_repo_selector.jsx @@ -13,10 +13,11 @@ const initialState = { export default class GithubRepoSelector extends PureComponent { static propTypes = { - yourRepos: PropTypes.array.isRequired, + yourRepos: PropTypes.object.isRequired, theme: PropTypes.object.isRequired, onChange: PropTypes.func.isRequired, value: PropTypes.string, + currentChannelId: PropTypes.string, addValidate: PropTypes.func, removeValidate: PropTypes.func, actions: PropTypes.shape({ @@ -30,16 +31,24 @@ export default class GithubRepoSelector extends PureComponent { } componentDidMount() { - this.props.actions.getRepos(); + this.props.actions.getRepos(this.props.currentChannelId); + } + + componentDidUpdate() { + const defaultRepo = this.props.yourRepos.defaultRepo; + + if (!(this.props.value) && this.props.yourRepos.defaultRepo?.full_name) { + this.onChange(defaultRepo.name, defaultRepo.full_name); + } } onChange = (_, name) => { - const repo = this.props.yourRepos.find((r) => r.full_name === name); + const repo = this.props.yourRepos.repo.find((r) => r.full_name === name); this.props.onChange({name, permissions: repo.permissions}); } render() { - const repoOptions = this.props.yourRepos.map((item) => ({value: item.full_name, label: item.full_name})); + const repoOptions = this.props.yourRepos.repo.map((item) => ({value: item.full_name, label: item.full_name})); return (
diff --git a/webapp/src/components/github_repo_selector/index.js b/webapp/src/components/github_repo_selector/index.js index 43616df85..64ebc9d8e 100644 --- a/webapp/src/components/github_repo_selector/index.js +++ b/webapp/src/components/github_repo_selector/index.js @@ -4,6 +4,8 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; +import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; + import manifest from 'manifest'; import {getRepos} from '../../actions'; @@ -12,6 +14,7 @@ import GithubRepoSelector from './github_repo_selector.jsx'; function mapStateToProps(state) { return { yourRepos: state[`plugins-${manifest.id}`].yourRepos, + currentChannelId: getCurrentChannelId(state), }; } diff --git a/webapp/src/reducers/index.ts b/webapp/src/reducers/index.ts index 6dd440f27..ae3d9ffaa 100644 --- a/webapp/src/reducers/index.ts +++ b/webapp/src/reducers/index.ts @@ -106,7 +106,9 @@ function sidebarContent(state = { } } -function yourRepos(state: YourReposData[] = [], action: {type: string, data: YourReposData[]}) { +function yourRepos(state: YourReposData = { + repo: [], +}, action: {type: string, data: YourReposData}) { switch (action.type) { case ActionTypes.RECEIVED_REPOSITORIES: return action.data; diff --git a/webapp/src/types/github_types.ts b/webapp/src/types/github_types.ts index 27aeb4941..44d2a4094 100644 --- a/webapp/src/types/github_types.ts +++ b/webapp/src/types/github_types.ts @@ -83,7 +83,17 @@ export type GithubIssueData = { repository_url: string; } +export type DefaultRepo = { + name: string; + full_name: string; +} + export type YourReposData = { + defaultRepo?: DefaultRepo; + repo: ReposData[]; +} + +export type ReposData = { name: string; full_name: string; } From bea8ce527b3cc6b44c45e5dc5c820453f3759ece Mon Sep 17 00:00:00 2001 From: kshitij Date: Wed, 17 Jul 2024 19:28:09 +0530 Subject: [PATCH 2/9] [MM-611]: Updated the help text --- server/plugin/template.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/plugin/template.go b/server/plugin/template.go index 753abc183..00b82ab42 100644 --- a/server/plugin/template.go +++ b/server/plugin/template.go @@ -426,7 +426,11 @@ Assignees: {{range $i, $el := .Assignees -}} {{- if $i}}, {{end}}{{template "use " * `/github mute list` - list your muted GitHub users\n" + " * `/github mute add [username]` - add a GitHub user to your muted list\n" + " * `/github mute delete [username]` - remove a GitHub user from your muted list\n" + - " * `/github mute delete-all` - unmute all GitHub users\n")) + " * `/github mute delete-all` - unmute all GitHub users\n" + + "* `/github default-repo` - Manage the default repository per channel for the user. The default repository will be auto selected for creating the issues\n" + + " * `/github default-repo set owner[/repo]` - set the default repo for the channel\n" + + " * `/github default-repo get` - get the default repo for the channel\n" + + " * `/github default-repo unset` - unset the default repo for the channel\n")) template.Must(masterTemplate.New("newRepoStar").Funcs(funcMap).Parse(` {{template "repo" .GetRepo}} From b72f9798275c984c01d782ab8159cf21ab07dd89 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Thu, 1 Aug 2024 17:17:43 +0530 Subject: [PATCH 3/9] [MM-611]: review fixes --- server/plugin/api.go | 49 ++++++++++++++++++++-------------------- server/plugin/command.go | 22 ++++++++++-------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/server/plugin/api.go b/server/plugin/api.go index 67bf5430d..3cd680bf1 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -50,6 +50,18 @@ func (e *APIErrorResponse) Error() string { return e.Message } +type RepoResponse struct { + Name string `json:"name,omitempty"` + FullName string `json:"full_name,omitempty"` + Permissions map[string]bool `json:"permissions,omitempty"` +} + +// Only send down fields to client that are needed +type RepositoryResponse struct { + DefaultRepo RepoResponse `json:"defaultRepo,omitempty"` + Repos []RepoResponse `json:"repo,omitempty"` +} + type PRDetails struct { URL string `json:"url"` Number int `json:"number"` @@ -1226,13 +1238,13 @@ func getRepositoryListByOrg(c context.Context, org string, githubClient *github. return allRepos, http.StatusOK, nil } -func getRepository(c context.Context, org string, repo string, githubClient *github.Client) (*github.Repository, int, error) { - repository, resp, err := githubClient.Repositories.Get(c, org, repo) +func getRepository(c context.Context, org string, repo string, githubClient *github.Client) (*github.Repository, error) { + repository, _, err := githubClient.Repositories.Get(c, org, repo) if err != nil { - return nil, resp.StatusCode, err + return nil, err } - return repository, http.StatusOK, nil + return repository, nil } func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http.Request) { @@ -1243,15 +1255,11 @@ func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http. channelID := r.URL.Query().Get(channelIDParam) if channelID == "" { + c.Log.Warnf("Bad request: missing channelId") p.writeAPIError(w, &APIErrorResponse{Message: "Bad request: missing channelId", StatusCode: http.StatusBadRequest}) return } - defaultRepo, dErr := p.GetDefaultRepo(c.GHInfo.UserID, channelID) - if dErr != nil { - c.Log.Warnf("Failed to get the default repo for the channel") - } - var allRepos []*github.Repository var err error var statusCode int @@ -1282,18 +1290,6 @@ func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http. } } - type RepoResponse struct { - Name string `json:"name,omitempty"` - FullName string `json:"full_name,omitempty"` - Permissions map[string]bool `json:"permissions,omitempty"` - } - - // Only send down fields to client that are needed - type RepositoryResponse struct { - DefaultRepo RepoResponse `json:"defaultRepo,omitempty"` - Repo []RepoResponse `json:"repo,omitempty"` - } - repoResp := make([]RepoResponse, len(allRepos)) for i, r := range allRepos { repoResp[i].Name = r.GetName() @@ -1302,16 +1298,21 @@ func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http. } resp := RepositoryResponse{ - Repo: repoResp, + Repos: repoResp, + } + + defaultRepo, dErr := p.GetDefaultRepo(c.GHInfo.UserID, channelID) + if dErr != nil { + c.Log.WithError(dErr).Warnf("Failed to get the default repo for the channel. UserID: %s. ChannelID: %s", c.GHInfo.UserID, channelID) } if defaultRepo != "" { config := p.getConfiguration() baseURL := config.getBaseURL() owner, repo := parseOwnerAndRepo(defaultRepo, baseURL) - defaultRepository, _, err := getRepository(c.Ctx, owner, repo, githubClient) + defaultRepository, err := getRepository(c.Ctx, owner, repo, githubClient) if err != nil { - c.Log.Warnf("Failed to get the default repo %s/%s", owner, repo) + c.Log.WithError(err).Warnf("Failed to get the default repo %s/%s", owner, repo) } if defaultRepository != nil { diff --git a/server/plugin/command.go b/server/plugin/command.go index d08bedf11..bab1911bf 100644 --- a/server/plugin/command.go +++ b/server/plugin/command.go @@ -33,6 +33,8 @@ const ( PerPageValue = 50 ) +const DefaultRepoKey string = "%s_%s-default-repo" + var validFeatures = map[string]bool{ featureIssueCreation: true, featureIssues: true, @@ -704,7 +706,7 @@ func (p *Plugin) handleIssue(_ *plugin.Context, args *model.CommandArgs, paramet func (p *Plugin) handleDefaultRepo(c *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { if len(parameters) == 0 { - return "Invalid issue command. Available command is 'set', 'get' and 'unset'." + return "Invalid action. Available actions are 'set', 'get' and 'unset'." } command := parameters[0] @@ -731,15 +733,15 @@ func (p *Plugin) handleSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs config := p.getConfiguration() baseURL := config.getBaseURL() owner, repo := parseOwnerAndRepo(repo, baseURL) - if owner == "" && repo == "" { - return "invalid repository" + if owner == "" || repo == "" { + return "Please provide a valid repository" } owner = strings.ToLower(owner) repo = strings.ToLower(repo) if config.GitHubOrg != "" && strings.ToLower(config.GitHubOrg) != owner { - return "repository is not part of the locked github organization" + return fmt.Sprintf("Repository is not part of the locked Github organization. Locked Github organization: %s", config.GitHubOrg) } ctx := context.Background() @@ -750,26 +752,26 @@ func (p *Plugin) handleSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs return fmt.Sprintf("unknown repository %s", fullNameFromOwnerAndRepo(owner, repo)) } - if _, err := p.store.Set(args.ChannelId+"_"+userInfo.UserID+"-default-repo", []byte(owner+"/"+repo)); err != nil { + if _, err := p.store.Set(fmt.Sprintf(DefaultRepoKey, args.ChannelId, userInfo.UserID), []byte(owner+"/"+repo)); err != nil { return "error occurred saving the default repo" } repoLink := baseURL + owner + "/" + repo - successMsg := fmt.Sprintf("The default repo has been set to [%s/%s](%s)", owner, repo, repoLink) + successMsg := fmt.Sprintf("The default repo has been set to [%s/%s](%s) for this channel", owner, repo, repoLink) return successMsg } func (p *Plugin) GetDefaultRepo(userID string, channelID string) (string, error) { var defaultRepoBytes []byte - if err := p.store.Get(channelID+"_"+userID+"-default-repo", &defaultRepoBytes); err != nil { + if err := p.store.Get(fmt.Sprintf(DefaultRepoKey, channelID, userID), &defaultRepoBytes); err != nil { return "", err } return string(defaultRepoBytes), nil } -func (p *Plugin) handleGetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { +func (p *Plugin) handleGetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, _ []string, userInfo *GitHubUserInfo) string { defaultRepo, err := p.GetDefaultRepo(userInfo.UserID, args.ChannelId) if err != nil { p.client.Log.Warn("Not able to get the default repo", "error", err.Error()) @@ -785,7 +787,7 @@ func (p *Plugin) handleGetDefaultRepo(_ *plugin.Context, args *model.CommandArgs return fmt.Sprintf("The default repository is [%s](%s)", defaultRepo, repoLink) } -func (p *Plugin) handleUnSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { +func (p *Plugin) handleUnSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, _ []string, userInfo *GitHubUserInfo) string { defaultRepo, err := p.GetDefaultRepo(userInfo.UserID, args.ChannelId) if err != nil { p.client.Log.Warn("Not able to get the default repo", "error", err.Error()) @@ -796,7 +798,7 @@ func (p *Plugin) handleUnSetDefaultRepo(_ *plugin.Context, args *model.CommandAr return "you have not set a default repository for this channel" } - if err := p.store.Delete(args.ChannelId + "_" + userInfo.UserID + "-default-repo"); err != nil { + if err := p.store.Delete(fmt.Sprintf(DefaultRepoKey, args.ChannelId, userInfo.UserID)); err != nil { return "error occurred while unsetting the repo for this channel" } From f84d7ddb54eed91a4e09f2fbc83356ca6b0a45c3 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Thu, 1 Aug 2024 17:37:06 +0530 Subject: [PATCH 4/9] [MM-611]: review fixes --- server/plugin/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/plugin/api.go b/server/plugin/api.go index 3cd680bf1..6573490a5 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -59,7 +59,7 @@ type RepoResponse struct { // Only send down fields to client that are needed type RepositoryResponse struct { DefaultRepo RepoResponse `json:"defaultRepo,omitempty"` - Repos []RepoResponse `json:"repo,omitempty"` + Repos []RepoResponse `json:"repo,omitempty"` } type PRDetails struct { From fe7d3f20e63d693a56ef987ded120a724ea53c15 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Tue, 6 Aug 2024 17:01:00 +0530 Subject: [PATCH 5/9] [MM-611]: review fixes --- server/plugin/api.go | 5 +-- server/plugin/command.go | 41 ++++++++++--------- .../github_repo_selector.jsx | 4 +- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/server/plugin/api.go b/server/plugin/api.go index 6573490a5..aa42d98c7 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -59,7 +59,7 @@ type RepoResponse struct { // Only send down fields to client that are needed type RepositoryResponse struct { DefaultRepo RepoResponse `json:"defaultRepo,omitempty"` - Repos []RepoResponse `json:"repo,omitempty"` + Repos []RepoResponse `json:"repos,omitempty"` } type PRDetails struct { @@ -1253,9 +1253,8 @@ func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http. org := p.getConfiguration().GitHubOrg channelID := r.URL.Query().Get(channelIDParam) - if channelID == "" { - c.Log.Warnf("Bad request: missing channelId") + p.client.Log.Warn("Bad request: missing channelId") p.writeAPIError(w, &APIErrorResponse{Message: "Bad request: missing channelId", StatusCode: http.StatusBadRequest}) return } diff --git a/server/plugin/command.go b/server/plugin/command.go index bab1911bf..8b36f5605 100644 --- a/server/plugin/command.go +++ b/server/plugin/command.go @@ -714,17 +714,17 @@ func (p *Plugin) handleDefaultRepo(c *plugin.Context, args *model.CommandArgs, p switch { case command == "set": - return p.handleSetDefaultRepo(c, args, parameters, userInfo) + return p.handleSetDefaultRepo(args, parameters, userInfo) case command == "get": - return p.handleGetDefaultRepo(c, args, parameters, userInfo) + return p.handleGetDefaultRepo(args, userInfo) case command == "unset": - return p.handleUnSetDefaultRepo(c, args, parameters, userInfo) + return p.handleUnSetDefaultRepo(args, userInfo) default: return fmt.Sprintf("Unknown subcommand %v", command) } } -func (p *Plugin) handleSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { +func (p *Plugin) handleSetDefaultRepo(args *model.CommandArgs, parameters []string, userInfo *GitHubUserInfo) string { if len(parameters) == 0 { return "Please specify a repository." } @@ -747,22 +747,25 @@ func (p *Plugin) handleSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs ctx := context.Background() githubClient := p.githubConnectUser(ctx, userInfo) - ghRepo, _, _ := githubClient.Repositories.Get(ctx, owner, repo) + ghRepo, _, err := githubClient.Repositories.Get(ctx, owner, repo) + if err != nil { + return "Error occured while getting github repository details" + } if ghRepo == nil { - return fmt.Sprintf("unknown repository %s", fullNameFromOwnerAndRepo(owner, repo)) + return fmt.Sprintf("Unknown repository %s", fullNameFromOwnerAndRepo(owner, repo)) } - if _, err := p.store.Set(fmt.Sprintf(DefaultRepoKey, args.ChannelId, userInfo.UserID), []byte(owner+"/"+repo)); err != nil { - return "error occurred saving the default repo" + if _, err := p.store.Set(fmt.Sprintf(DefaultRepoKey, args.ChannelId, userInfo.UserID), []byte(fmt.Sprintf("%s/%s", owner, repo))); err != nil { + return "Error occurred saving the default repo" } - repoLink := baseURL + owner + "/" + repo + repoLink := fmt.Sprintf("%s%s/%s", baseURL, owner, repo) successMsg := fmt.Sprintf("The default repo has been set to [%s/%s](%s) for this channel", owner, repo, repoLink) return successMsg } -func (p *Plugin) GetDefaultRepo(userID string, channelID string) (string, error) { +func (p *Plugin) GetDefaultRepo(userID, channelID string) (string, error) { var defaultRepoBytes []byte if err := p.store.Get(fmt.Sprintf(DefaultRepoKey, channelID, userID), &defaultRepoBytes); err != nil { return "", err @@ -771,15 +774,15 @@ func (p *Plugin) GetDefaultRepo(userID string, channelID string) (string, error) return string(defaultRepoBytes), nil } -func (p *Plugin) handleGetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, _ []string, userInfo *GitHubUserInfo) string { +func (p *Plugin) handleGetDefaultRepo(args *model.CommandArgs, userInfo *GitHubUserInfo) string { defaultRepo, err := p.GetDefaultRepo(userInfo.UserID, args.ChannelId) if err != nil { - p.client.Log.Warn("Not able to get the default repo", "error", err.Error()) - return "error occurred while getting the default repo" + p.client.Log.Warn("Not able to get the default repo", "UserID", userInfo.UserID, "ChannelID", args.ChannelId, "Error", err.Error()) + return "Error occurred while getting the default repo" } if defaultRepo == "" { - return "you have not set a default repository for this channel" + return "You have not set a default repository for this channel" } config := p.getConfiguration() @@ -787,19 +790,19 @@ func (p *Plugin) handleGetDefaultRepo(_ *plugin.Context, args *model.CommandArgs return fmt.Sprintf("The default repository is [%s](%s)", defaultRepo, repoLink) } -func (p *Plugin) handleUnSetDefaultRepo(_ *plugin.Context, args *model.CommandArgs, _ []string, userInfo *GitHubUserInfo) string { +func (p *Plugin) handleUnSetDefaultRepo(args *model.CommandArgs, userInfo *GitHubUserInfo) string { defaultRepo, err := p.GetDefaultRepo(userInfo.UserID, args.ChannelId) if err != nil { - p.client.Log.Warn("Not able to get the default repo", "error", err.Error()) - return "error occurred while getting the default repo" + p.client.Log.Warn("Not able to get the default repo", "UserID", userInfo.UserID, "ChannelID", args.ChannelId, "Error", err.Error()) + return "Error occurred while getting the default repo" } if defaultRepo == "" { - return "you have not set a default repository for this channel" + return "You have not set a default repository for this channel" } if err := p.store.Delete(fmt.Sprintf(DefaultRepoKey, args.ChannelId, userInfo.UserID)); err != nil { - return "error occurred while unsetting the repo for this channel" + return "Error occurred while unsetting the repo for this channel" } return "The default repository has been unset successfully" diff --git a/webapp/src/components/github_repo_selector/github_repo_selector.jsx b/webapp/src/components/github_repo_selector/github_repo_selector.jsx index 631fba885..7dab06d0d 100644 --- a/webapp/src/components/github_repo_selector/github_repo_selector.jsx +++ b/webapp/src/components/github_repo_selector/github_repo_selector.jsx @@ -43,12 +43,12 @@ export default class GithubRepoSelector extends PureComponent { } onChange = (_, name) => { - const repo = this.props.yourRepos.repo.find((r) => r.full_name === name); + const repo = this.props.yourRepos.repos.find((r) => r.full_name === name); this.props.onChange({name, permissions: repo.permissions}); } render() { - const repoOptions = this.props.yourRepos.repo.map((item) => ({value: item.full_name, label: item.full_name})); + const repoOptions = this.props.yourRepos.repos.map((item) => ({value: item.full_name, label: item.full_name})); return (
From 5df533e4fd106def9d125cd51aba1e6e207f79a0 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Tue, 6 Aug 2024 17:26:07 +0530 Subject: [PATCH 6/9] [MM-611]: Fixed the lint issue --- server/plugin/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/plugin/command.go b/server/plugin/command.go index 8b36f5605..265059bca 100644 --- a/server/plugin/command.go +++ b/server/plugin/command.go @@ -749,7 +749,7 @@ func (p *Plugin) handleSetDefaultRepo(args *model.CommandArgs, parameters []stri ghRepo, _, err := githubClient.Repositories.Get(ctx, owner, repo) if err != nil { - return "Error occured while getting github repository details" + return "Error occurred while getting github repository details" } if ghRepo == nil { return fmt.Sprintf("Unknown repository %s", fullNameFromOwnerAndRepo(owner, repo)) From 704af498b43f1f53151a5393368ef4c5b68a974c Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Thu, 16 Jan 2025 16:40:56 +0530 Subject: [PATCH 7/9] [MM-611]: resolved map accessing empty array error --- server/plugin/api.go | 2 +- .../components/github_repo_selector/github_repo_selector.jsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/plugin/api.go b/server/plugin/api.go index 49ffd8296..bc40d8bb1 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -62,7 +62,7 @@ type RepoResponse struct { // Only send down fields to client that are needed type RepositoryResponse struct { DefaultRepo RepoResponse `json:"defaultRepo,omitempty"` - Repos []RepoResponse `json:"repos,omitempty"` + Repos []RepoResponse `json:"repo,omitempty"` } type PRDetails struct { diff --git a/webapp/src/components/github_repo_selector/github_repo_selector.jsx b/webapp/src/components/github_repo_selector/github_repo_selector.jsx index 73680914b..700baf2c7 100644 --- a/webapp/src/components/github_repo_selector/github_repo_selector.jsx +++ b/webapp/src/components/github_repo_selector/github_repo_selector.jsx @@ -43,12 +43,12 @@ export default class GithubRepoSelector extends PureComponent { } onChange = (_, name) => { - const repo = this.props.yourRepos.repos.find((r) => r.full_name === name); + const repo = this.props.yourRepos.repo.find((r) => r.full_name === name); this.props.onChange({name, permissions: repo.permissions}); } render() { - const repoOptions = this.props.yourRepos.repos.map((item) => ({value: item.full_name, label: item.full_name})); + const repoOptions = this.props.yourRepos.repo.map((item) => ({value: item.full_name, label: item.full_name})); return (
From a3488a0057d1c0f9ffb9d6bc25d71e7158d1ec1d Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Thu, 16 Jan 2025 16:42:57 +0530 Subject: [PATCH 8/9] [MM-611]: review fixes --- .../components/github_repo_selector/github_repo_selector.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/github_repo_selector/github_repo_selector.jsx b/webapp/src/components/github_repo_selector/github_repo_selector.jsx index 700baf2c7..0d4c83ac6 100644 --- a/webapp/src/components/github_repo_selector/github_repo_selector.jsx +++ b/webapp/src/components/github_repo_selector/github_repo_selector.jsx @@ -37,7 +37,7 @@ export default class GithubRepoSelector extends PureComponent { componentDidUpdate() { const defaultRepo = this.props.yourRepos.defaultRepo; - if (!(this.props.value) && this.props.yourRepos.defaultRepo?.full_name) { + if (!(this.props.value) && defaultRepo?.full_name) { this.onChange(defaultRepo.name, defaultRepo.full_name); } } From dc5832ee456b7640a5bbfdbf27f1240de2271f74 Mon Sep 17 00:00:00 2001 From: kshitij katiyar Date: Fri, 17 Jan 2025 13:45:35 +0530 Subject: [PATCH 9/9] updated json for repo data --- server/plugin/api.go | 2 +- .../components/github_repo_selector/github_repo_selector.jsx | 4 ++-- webapp/src/reducers/index.ts | 2 +- webapp/src/types/github_types.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/plugin/api.go b/server/plugin/api.go index bc40d8bb1..49ffd8296 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -62,7 +62,7 @@ type RepoResponse struct { // Only send down fields to client that are needed type RepositoryResponse struct { DefaultRepo RepoResponse `json:"defaultRepo,omitempty"` - Repos []RepoResponse `json:"repo,omitempty"` + Repos []RepoResponse `json:"repos,omitempty"` } type PRDetails struct { diff --git a/webapp/src/components/github_repo_selector/github_repo_selector.jsx b/webapp/src/components/github_repo_selector/github_repo_selector.jsx index 0d4c83ac6..f6b72a8b6 100644 --- a/webapp/src/components/github_repo_selector/github_repo_selector.jsx +++ b/webapp/src/components/github_repo_selector/github_repo_selector.jsx @@ -43,12 +43,12 @@ export default class GithubRepoSelector extends PureComponent { } onChange = (_, name) => { - const repo = this.props.yourRepos.repo.find((r) => r.full_name === name); + const repo = this.props.yourRepos.repos.find((r) => r.full_name === name); this.props.onChange({name, permissions: repo.permissions}); } render() { - const repoOptions = this.props.yourRepos.repo.map((item) => ({value: item.full_name, label: item.full_name})); + const repoOptions = this.props.yourRepos.repos.map((item) => ({value: item.full_name, label: item.full_name})); return (
diff --git a/webapp/src/reducers/index.ts b/webapp/src/reducers/index.ts index 81cab1c12..4a59927f6 100644 --- a/webapp/src/reducers/index.ts +++ b/webapp/src/reducers/index.ts @@ -107,7 +107,7 @@ function sidebarContent(state = { } function yourRepos(state: YourReposData = { - repo: [], + repos: [], }, action: {type: string, data: YourReposData}) { switch (action.type) { case ActionTypes.RECEIVED_REPOSITORIES: diff --git a/webapp/src/types/github_types.ts b/webapp/src/types/github_types.ts index d0caddc71..cd1c6ab08 100644 --- a/webapp/src/types/github_types.ts +++ b/webapp/src/types/github_types.ts @@ -93,7 +93,7 @@ export type DefaultRepo = { export type YourReposData = { defaultRepo?: DefaultRepo; - repo: ReposData[]; + repos: ReposData[]; } export type ReposData = {