diff --git a/plugin.json b/plugin.json index 598637247..9b08d9221 100644 --- a/plugin.json +++ b/plugin.json @@ -55,9 +55,9 @@ }, { "key": "GithubOrg", - "display_name": "GitHub Organization:", + "display_name": "GitHub Organizations:", "type": "text", - "help_text": "(Optional) Set to lock the plugin to a single GitHub organization." + "help_text": "(Optional) Set to lock the plugin to one or more GitHub organizations. Provide multiple orgs using a comma-separated list." }, { "key": "EnterpriseBaseURL", diff --git a/server/plugin/api.go b/server/plugin/api.go index 8dffebdd9..d8fd5ca30 100644 --- a/server/plugin/api.go +++ b/server/plugin/api.go @@ -480,7 +480,7 @@ func (p *Plugin) completeConnectUserToGitHub(c *Context, w http.ResponseWriter, } config := p.getConfiguration() - + orgList := p.configuration.getOrganizations() p.client.Frontend.PublishWebSocketEvent( wsEventConnect, map[string]interface{}{ @@ -488,7 +488,7 @@ func (p *Plugin) completeConnectUserToGitHub(c *Context, w http.ResponseWriter, "github_username": userInfo.GitHubUsername, "github_client_id": config.GitHubOAuthClientID, "enterprise_base_url": config.EnterpriseBaseURL, - "organization": config.GitHubOrg, + "organizations": orgList, "configuration": config.ClientConfiguration(), }, &model.WebsocketBroadcast{UserId: state.UserID}, @@ -565,15 +565,16 @@ func (p *Plugin) getConnected(c *Context, w http.ResponseWriter, r *http.Request GitHubUsername string `json:"github_username"` GitHubClientID string `json:"github_client_id"` EnterpriseBaseURL string `json:"enterprise_base_url,omitempty"` - Organization string `json:"organization"` + Organizations []string `json:"organizations"` UserSettings *UserSettings `json:"user_settings"` ClientConfiguration map[string]interface{} `json:"configuration"` } + orgList := p.configuration.getOrganizations() resp := &ConnectedResponse{ Connected: false, EnterpriseBaseURL: config.EnterpriseBaseURL, - Organization: config.GitHubOrg, + Organizations: orgList, ClientConfiguration: p.getConfiguration().ClientConfiguration(), } @@ -645,11 +646,10 @@ func (p *Plugin) getConnected(c *Context, w http.ResponseWriter, r *http.Request } func (p *Plugin) getMentions(c *UserContext, w http.ResponseWriter, r *http.Request) { - config := p.getConfiguration() - githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo) username := c.GHInfo.GitHubUsername - query := getMentionSearchQuery(username, config.GitHubOrg) + orgList := p.configuration.getOrganizations() + query := getMentionSearchQuery(username, orgList) result, _, err := githubClient.Search.Issues(c.Ctx, query, &github.SearchOptions{}) if err != nil { @@ -662,7 +662,6 @@ func (p *Plugin) getMentions(c *UserContext, w http.ResponseWriter, r *http.Requ func (p *Plugin) getUnreadsData(c *UserContext) []*FilteredNotification { githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo) - notifications, _, err := githubClient.Activity.ListNotifications(c.Ctx, &github.NotificationListOptions{}) if err != nil { c.Log.WithError(err).Warnf("Failed to list notifications") @@ -797,20 +796,24 @@ func getRepoOwnerAndNameFromURL(url string) (string, string) { } func (p *Plugin) searchIssues(c *UserContext, w http.ResponseWriter, r *http.Request) { - config := p.getConfiguration() - githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo) searchTerm := r.FormValue("term") - query := getIssuesSearchQuery(config.GitHubOrg, searchTerm) - result, _, err := githubClient.Search.Issues(c.Ctx, query, &github.SearchOptions{}) - if err != nil { - c.Log.WithError(err).With(logger.LogContext{"query": query}).Warnf("Failed to search for issues") - p.writeJSON(w, make([]*github.Issue, 0)) - return + orgsList := p.configuration.getOrganizations() + allIssues := []*github.Issue{} + for _, org := range orgsList { + query := getIssuesSearchQuery(org, searchTerm) + result, _, err := githubClient.Search.Issues(c.Ctx, query, &github.SearchOptions{}) + if err != nil { + c.Log.WithError(err).With(logger.LogContext{"query": query}).Warnf("Failed to search for issues") + p.writeJSON(w, make([]*github.Issue, 0)) + return + } + + allIssues = append(allIssues, result.Issues...) } - p.writeJSON(w, result.Issues) + p.writeJSON(w, allIssues) } func (p *Plugin) getPermaLink(postID string) string { @@ -1226,12 +1229,11 @@ func getRepositoryListByOrg(c context.Context, org string, githubClient *github. func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http.Request) { githubClient := p.githubConnectUser(c.Context.Ctx, c.GHInfo) - org := p.getConfiguration().GitHubOrg var allRepos []*github.Repository var err error - var statusCode int + opt := github.ListOptions{PerPage: 50} if org == "" { @@ -1242,19 +1244,26 @@ func (p *Plugin) getRepositories(c *UserContext, w http.ResponseWriter, r *http. return } } else { - allRepos, statusCode, err = getRepositoryListByOrg(c.Ctx, org, githubClient, opt) - if err != nil { - if statusCode == http.StatusNotFound { - allRepos, err = getRepositoryList(c.Ctx, org, githubClient, opt) - if err != nil { - c.Log.WithError(err).Warnf("Failed to list repositories") + orgsList := p.configuration.getOrganizations() + for _, org := range orgsList { + orgRepos, statusCode, err := getRepositoryListByOrg(c.Ctx, org, githubClient, opt) + if err != nil { + if statusCode == http.StatusNotFound { + orgRepos, err = getRepositoryList(c.Ctx, org, githubClient, opt) + if err != nil { + c.Log.WithError(err).Warnf("Failed to list repositories", "Organization", org) + p.writeAPIError(w, &APIErrorResponse{Message: "Failed to fetch repositories", StatusCode: http.StatusInternalServerError}) + return + } + } else { + c.Log.WithError(err).Warnf("Failed to list repositories", "Organization", org) p.writeAPIError(w, &APIErrorResponse{Message: "Failed to fetch repositories", StatusCode: http.StatusInternalServerError}) return } - } else { - c.Log.WithError(err).Warnf("Failed to list repositories") - p.writeAPIError(w, &APIErrorResponse{Message: "Failed to fetch repositories", StatusCode: http.StatusInternalServerError}) - return + } + + if len(orgRepos) > 0 { + allRepos = append(allRepos, orgRepos...) } } } diff --git a/server/plugin/configuration.go b/server/plugin/configuration.go index 408b4ff35..9f510a2d5 100644 --- a/server/plugin/configuration.go +++ b/server/plugin/configuration.go @@ -250,3 +250,20 @@ func generateSecret() (string, error) { return s, nil } + +func (c *Configuration) getOrganizations() []string { + if c.GitHubOrg == "" { + return []string{} + } + + list := strings.Split(c.GitHubOrg, ",") + allOrgs := []string{} + for _, org := range list { + org = strings.TrimSpace(strings.ToLower(org)) + if org != "" { + allOrgs = append(allOrgs, org) + } + } + + return allOrgs +} diff --git a/server/plugin/configuration_test.go b/server/plugin/configuration_test.go index 316733a9d..e8aea9a3b 100644 --- a/server/plugin/configuration_test.go +++ b/server/plugin/configuration_test.go @@ -171,3 +171,31 @@ func TestSetDefaults(t *testing.T) { }) } } + +func TestGetOrganizations(t *testing.T) { + tcs := []struct { + Organizations string + ExpectedOrgList []string + }{ + { + Organizations: "org-1,org-2", + ExpectedOrgList: []string{"org-1", "org-2"}, + }, + { + Organizations: "org-1,org-2,", + ExpectedOrgList: []string{"org-1", "org-2"}, + }, + { + Organizations: "org-1, org-2 ", + ExpectedOrgList: []string{"org-1", "org-2"}, + }, + } + + for _, tc := range tcs { + config := Configuration{ + GitHubOrg: tc.Organizations, + } + orgList := config.getOrganizations() + assert.Equal(t, tc.ExpectedOrgList, orgList) + } +} diff --git a/server/plugin/graphql/client.go b/server/plugin/graphql/client.go index d1e7f36b6..ef21aa522 100644 --- a/server/plugin/graphql/client.go +++ b/server/plugin/graphql/client.go @@ -14,24 +14,26 @@ import ( // Client encapsulates the third party package that communicates with Github GraphQL API type Client struct { - client *githubv4.Client - org string - username string - logger pluginapi.LogService + client *githubv4.Client + org string + username string + logger pluginapi.LogService + getOrganizations func() []string } // NewClient creates and returns Client. The third party package that queries GraphQL is initialized here. -func NewClient(logger pluginapi.LogService, token oauth2.Token, username, orgName, enterpriseBaseURL string) *Client { +func NewClient(logger pluginapi.LogService, getOrganizations func() []string, token oauth2.Token, username, orgName, enterpriseBaseURL string) *Client { ts := oauth2.StaticTokenSource(&token) httpClient := oauth2.NewClient(context.Background(), ts) var client Client if enterpriseBaseURL == "" { client = Client{ - username: username, - client: githubv4.NewClient(httpClient), - logger: logger, - org: orgName, + username: username, + client: githubv4.NewClient(httpClient), + logger: logger, + org: orgName, + getOrganizations: getOrganizations, } } else { baseURL, err := url.Parse(enterpriseBaseURL) @@ -43,10 +45,11 @@ func NewClient(logger pluginapi.LogService, token oauth2.Token, username, orgNam baseURL.Path = path.Join(baseURL.Path, "api", "graphql") client = Client{ - client: githubv4.NewEnterpriseClient(baseURL.String(), httpClient), - username: username, - org: orgName, - logger: logger, + client: githubv4.NewEnterpriseClient(baseURL.String(), httpClient), + username: username, + org: orgName, + logger: logger, + getOrganizations: getOrganizations, } } diff --git a/server/plugin/graphql/lhs_request.go b/server/plugin/graphql/lhs_request.go index 46161e1fc..f36df5921 100644 --- a/server/plugin/graphql/lhs_request.go +++ b/server/plugin/graphql/lhs_request.go @@ -20,73 +20,74 @@ const ( ) func (c *Client) GetLHSData(ctx context.Context) ([]*github.Issue, []*github.Issue, []*github.Issue, error) { - params := map[string]interface{}{ - queryParamOpenPRQueryArg: githubv4.String(fmt.Sprintf("author:%s is:pr is:%s archived:false", c.username, githubv4.PullRequestStateOpen)), - queryParamReviewPRQueryArg: githubv4.String(fmt.Sprintf("review-requested:%s is:pr is:%s archived:false", c.username, githubv4.PullRequestStateOpen)), - queryParamAssigneeQueryArg: githubv4.String(fmt.Sprintf("assignee:%s is:%s archived:false", c.username, githubv4.PullRequestStateOpen)), - queryParamReviewsCursor: (*githubv4.String)(nil), - queryParamAssignmentsCursor: (*githubv4.String)(nil), - queryParamOpenPRsCursor: (*githubv4.String)(nil), - } - - if c.org != "" { - params[queryParamOpenPRQueryArg] = githubv4.String(fmt.Sprintf("org:%s %s", c.org, params[queryParamOpenPRQueryArg])) - params[queryParamReviewPRQueryArg] = githubv4.String(fmt.Sprintf("org:%s %s", c.org, params[queryParamReviewPRQueryArg])) - params[queryParamAssigneeQueryArg] = githubv4.String(fmt.Sprintf("org:%s %s", c.org, params[queryParamAssigneeQueryArg])) - } - + orgsList := c.getOrganizations() var resultReview, resultAssignee, resultOpenPR []*github.Issue - allReviewRequestsFetched, allAssignmentsFetched, allOpenPRsFetched := false, false, false - - for { - if allReviewRequestsFetched && allAssignmentsFetched && allOpenPRsFetched { - break + for _, org := range orgsList { + params := map[string]interface{}{ + queryParamOpenPRQueryArg: githubv4.String(fmt.Sprintf("author:%s is:pr is:%s archived:false", c.username, githubv4.PullRequestStateOpen)), + queryParamReviewPRQueryArg: githubv4.String(fmt.Sprintf("review-requested:%s is:pr is:%s archived:false", c.username, githubv4.PullRequestStateOpen)), + queryParamAssigneeQueryArg: githubv4.String(fmt.Sprintf("assignee:%s is:%s archived:false", c.username, githubv4.PullRequestStateOpen)), + queryParamReviewsCursor: (*githubv4.String)(nil), + queryParamAssignmentsCursor: (*githubv4.String)(nil), + queryParamOpenPRsCursor: (*githubv4.String)(nil), } - if err := c.executeQuery(ctx, &mainQuery, params); err != nil { - return nil, nil, nil, errors.Wrap(err, "Not able to excute the query") - } + params[queryParamOpenPRQueryArg] = githubv4.String(fmt.Sprintf("org:%s %s", org, params[queryParamOpenPRQueryArg])) + params[queryParamReviewPRQueryArg] = githubv4.String(fmt.Sprintf("org:%s %s", org, params[queryParamReviewPRQueryArg])) + params[queryParamAssigneeQueryArg] = githubv4.String(fmt.Sprintf("org:%s %s", org, params[queryParamAssigneeQueryArg])) + + allReviewRequestsFetched, allAssignmentsFetched, allOpenPRsFetched := false, false, false - if !allReviewRequestsFetched { - for i := range mainQuery.ReviewRequests.Nodes { - resp := mainQuery.ReviewRequests.Nodes[i] - pr := getPR(&resp) - resultReview = append(resultReview, pr) + for { + if allReviewRequestsFetched && allAssignmentsFetched && allOpenPRsFetched { + break } - if !mainQuery.ReviewRequests.PageInfo.HasNextPage { - allReviewRequestsFetched = true + if err := c.executeQuery(ctx, &mainQuery, params); err != nil { + return nil, nil, nil, errors.Wrap(err, "Not able to excute the query") } - params[queryParamReviewsCursor] = githubv4.NewString(mainQuery.ReviewRequests.PageInfo.EndCursor) - } + if !allReviewRequestsFetched { + for i := range mainQuery.ReviewRequests.Nodes { + resp := mainQuery.ReviewRequests.Nodes[i] + pr := getPR(&resp) + resultReview = append(resultReview, pr) + } - if !allAssignmentsFetched { - for i := range mainQuery.Assignments.Nodes { - resp := mainQuery.Assignments.Nodes[i] - issue := newIssueFromAssignmentResponse(&resp) - resultAssignee = append(resultAssignee, issue) - } + if !mainQuery.ReviewRequests.PageInfo.HasNextPage { + allReviewRequestsFetched = true + } - if !mainQuery.Assignments.PageInfo.HasNextPage { - allAssignmentsFetched = true + params[queryParamReviewsCursor] = githubv4.NewString(mainQuery.ReviewRequests.PageInfo.EndCursor) } - params[queryParamAssignmentsCursor] = githubv4.NewString(mainQuery.Assignments.PageInfo.EndCursor) - } + if !allAssignmentsFetched { + for i := range mainQuery.Assignments.Nodes { + resp := mainQuery.Assignments.Nodes[i] + issue := newIssueFromAssignmentResponse(&resp) + resultAssignee = append(resultAssignee, issue) + } - if !allOpenPRsFetched { - for i := range mainQuery.OpenPullRequests.Nodes { - resp := mainQuery.OpenPullRequests.Nodes[i] - pr := getPR(&resp) - resultOpenPR = append(resultOpenPR, pr) - } + if !mainQuery.Assignments.PageInfo.HasNextPage { + allAssignmentsFetched = true + } - if !mainQuery.OpenPullRequests.PageInfo.HasNextPage { - allOpenPRsFetched = true + params[queryParamAssignmentsCursor] = githubv4.NewString(mainQuery.Assignments.PageInfo.EndCursor) } - params[queryParamOpenPRsCursor] = githubv4.NewString(mainQuery.OpenPullRequests.PageInfo.EndCursor) + if !allOpenPRsFetched { + for i := range mainQuery.OpenPullRequests.Nodes { + resp := mainQuery.OpenPullRequests.Nodes[i] + pr := getPR(&resp) + resultOpenPR = append(resultOpenPR, pr) + } + + if !mainQuery.OpenPullRequests.PageInfo.HasNextPage { + allOpenPRsFetched = true + } + + params[queryParamOpenPRsCursor] = githubv4.NewString(mainQuery.OpenPullRequests.PageInfo.EndCursor) + } } } diff --git a/server/plugin/plugin.go b/server/plugin/plugin.go index 0f092ee76..77770ec88 100644 --- a/server/plugin/plugin.go +++ b/server/plugin/plugin.go @@ -177,7 +177,7 @@ func (p *Plugin) githubConnectUser(ctx context.Context, info *GitHubUserInfo) *g func (p *Plugin) graphQLConnect(info *GitHubUserInfo) *graphql.Client { conf := p.getConfiguration() - return graphql.NewClient(p.client.Log, *info.Token, info.GitHubUsername, conf.GitHubOrg, conf.EnterpriseBaseURL) + return graphql.NewClient(p.client.Log, p.configuration.getOrganizations, *info.Token, info.GitHubUsername, conf.GitHubOrg, conf.EnterpriseBaseURL) } func (p *Plugin) githubConnectToken(token oauth2.Token) *github.Client { @@ -799,8 +799,8 @@ func (p *Plugin) PostToDo(info *GitHubUserInfo, userID string) error { func (p *Plugin) GetToDo(ctx context.Context, username string, githubClient *github.Client) (string, error) { config := p.getConfiguration() baseURL := config.getBaseURL() - - issueResults, _, err := githubClient.Search.Issues(ctx, getReviewSearchQuery(username, config.GitHubOrg), &github.SearchOptions{}) + orgList := p.configuration.getOrganizations() + issueResults, _, err := githubClient.Search.Issues(ctx, getReviewSearchQuery(username, orgList), &github.SearchOptions{}) if err != nil { return "", errors.Wrap(err, "Error occurred while searching for reviews") } @@ -810,12 +810,12 @@ func (p *Plugin) GetToDo(ctx context.Context, username string, githubClient *git return "", errors.Wrap(err, "error occurred while listing notifications") } - yourPrs, _, err := githubClient.Search.Issues(ctx, getYourPrsSearchQuery(username, config.GitHubOrg), &github.SearchOptions{}) + yourPrs, _, err := githubClient.Search.Issues(ctx, getYourPrsSearchQuery(username, orgList), &github.SearchOptions{}) if err != nil { return "", errors.Wrap(err, "error occurred while searching for PRs") } - yourAssignments, _, err := githubClient.Search.Issues(ctx, getYourAssigneeSearchQuery(username, config.GitHubOrg), &github.SearchOptions{}) + yourAssignments, _, err := githubClient.Search.Issues(ctx, getYourAssigneeSearchQuery(username, orgList), &github.SearchOptions{}) if err != nil { return "", errors.Wrap(err, "error occurred while searching for assignments") } @@ -911,23 +911,22 @@ func (p *Plugin) HasUnreads(info *GitHubUserInfo) bool { username := info.GitHubUsername ctx := context.Background() githubClient := p.githubConnectUser(ctx, info) - config := p.getConfiguration() - - query := getReviewSearchQuery(username, config.GitHubOrg) + orgList := p.configuration.getOrganizations() + query := getReviewSearchQuery(username, orgList) issues, _, err := githubClient.Search.Issues(ctx, query, &github.SearchOptions{}) if err != nil { p.client.Log.Warn("Failed to search for review", "query", query, "error", err.Error()) return false } - query = getYourPrsSearchQuery(username, config.GitHubOrg) + query = getYourPrsSearchQuery(username, orgList) yourPrs, _, err := githubClient.Search.Issues(ctx, query, &github.SearchOptions{}) if err != nil { p.client.Log.Warn("Failed to search for PRs", "query", query, "error", "error", err.Error()) return false } - query = getYourAssigneeSearchQuery(username, config.GitHubOrg) + query = getYourAssigneeSearchQuery(username, orgList) yourAssignments, _, err := githubClient.Search.Issues(ctx, query, &github.SearchOptions{}) if err != nil { p.client.Log.Warn("Failed to search for assignments", "query", query, "error", "error", err.Error()) @@ -969,12 +968,18 @@ func (p *Plugin) HasUnreads(info *GitHubUserInfo) bool { func (p *Plugin) checkOrg(org string) error { config := p.getConfiguration() - configOrg := strings.TrimSpace(config.GitHubOrg) - if configOrg != "" && configOrg != org && strings.ToLower(configOrg) != org { - return errors.Errorf("only repositories in the %v organization are supported", configOrg) + orgList := config.getOrganizations() + if len(orgList) == 0 { + return nil } - return nil + for _, configOrg := range orgList { + if configOrg == strings.ToLower(org) { + return nil + } + } + + return errors.Errorf("only repositories in the %v organization(s) are supported", config.GitHubOrg) } func (p *Plugin) isUserOrganizationMember(githubClient *github.Client, user *github.User, organization string) bool { diff --git a/server/plugin/utils.go b/server/plugin/utils.go index 08006f24e..f2d434b65 100644 --- a/server/plugin/utils.go +++ b/server/plugin/utils.go @@ -19,20 +19,20 @@ import ( "github.com/pkg/errors" ) -func getMentionSearchQuery(username, org string) string { - return buildSearchQuery("is:open mentions:%v archived:false %v", username, org) +func getMentionSearchQuery(username string, orgs []string) string { + return buildSearchQuery("is:open mentions:%v archived:false %v", username, orgs) } -func getReviewSearchQuery(username, org string) string { - return buildSearchQuery("is:pr is:open review-requested:%v archived:false %v", username, org) +func getReviewSearchQuery(username string, orgs []string) string { + return buildSearchQuery("is:pr is:open review-requested:%v archived:false %v", username, orgs) } -func getYourPrsSearchQuery(username, org string) string { - return buildSearchQuery("is:pr is:open author:%v archived:false %v", username, org) +func getYourPrsSearchQuery(username string, orgs []string) string { + return buildSearchQuery("is:pr is:open author:%v archived:false %v", username, orgs) } -func getYourAssigneeSearchQuery(username, org string) string { - return buildSearchQuery("is:open assignee:%v archived:false %v", username, org) +func getYourAssigneeSearchQuery(username string, orgs []string) string { + return buildSearchQuery("is:open assignee:%v archived:false %v", username, orgs) } func getIssuesSearchQuery(org, searchTerm string) string { @@ -45,10 +45,12 @@ func getIssuesSearchQuery(org, searchTerm string) string { return fmt.Sprintf(query, orgField, searchTerm) } -func buildSearchQuery(query, username, org string) string { +func buildSearchQuery(query, username string, orgs []string) string { orgField := "" - if len(org) != 0 { - orgField = fmt.Sprintf("org:%v", org) + for _, org := range orgs { + if len(org) != 0 { + orgField = fmt.Sprintf("%s org:%s", orgField, org) + } } return fmt.Sprintf(query, username, orgField) diff --git a/server/plugin/webhook.go b/server/plugin/webhook.go index b39c4fa69..27366199d 100644 --- a/server/plugin/webhook.go +++ b/server/plugin/webhook.go @@ -312,6 +312,7 @@ func (p *Plugin) permissionToRepo(userID string, ownerAndRepo string) bool { if owner == "" { return false } + if err := p.checkOrg(owner); err != nil { return false } diff --git a/webapp/src/components/sidebar_right/index.jsx b/webapp/src/components/sidebar_right/index.jsx index f96a08a02..25717d359 100644 --- a/webapp/src/components/sidebar_right/index.jsx +++ b/webapp/src/components/sidebar_right/index.jsx @@ -11,7 +11,7 @@ import {getSidebarData} from 'src/selectors'; import SidebarRight from './sidebar_right.jsx'; function mapStateToProps(state) { - const {username, reviews, yourPrs, yourAssignments, unreads, enterpriseURL, org, rhsState} = getSidebarData(state); + const {username, reviews, yourPrs, yourAssignments, unreads, enterpriseURL, orgs, rhsState} = getSidebarData(state); return { username, reviews, @@ -19,7 +19,7 @@ function mapStateToProps(state) { yourAssignments, unreads, enterpriseURL, - org, + orgs, rhsState, }; } diff --git a/webapp/src/components/sidebar_right/sidebar_right.jsx b/webapp/src/components/sidebar_right/sidebar_right.jsx index 71bb12191..4da5fc9cd 100644 --- a/webapp/src/components/sidebar_right/sidebar_right.jsx +++ b/webapp/src/components/sidebar_right/sidebar_right.jsx @@ -66,7 +66,7 @@ function shouldUpdateDetails(prs, prevPrs, targetState, currentState, prevState) export default class SidebarRight extends React.PureComponent { static propTypes = { username: PropTypes.string, - org: PropTypes.string, + orgs: PropTypes.array.isRequired, enterpriseURL: PropTypes.string, reviews: PropTypes.arrayOf(PropTypes.object), unreads: PropTypes.arrayOf(PropTypes.object), @@ -102,7 +102,11 @@ export default class SidebarRight extends React.PureComponent { render() { const baseURL = this.props.enterpriseURL ? this.props.enterpriseURL : 'https://github.com'; - const orgQuery = this.props.org ? '+org%3A' + this.props.org : ''; + let orgQuery = ''; + this.props.orgs.map((org) => { + orgQuery += ('+org%3A' + org); + return orgQuery; + }); const {yourPrs, reviews, unreads, yourAssignments, username, rhsState} = this.props; let title = ''; diff --git a/webapp/src/reducers/index.ts b/webapp/src/reducers/index.ts index 6dd440f27..7c2b49d24 100644 --- a/webapp/src/reducers/index.ts +++ b/webapp/src/reducers/index.ts @@ -29,13 +29,13 @@ function enterpriseURL(state = '', action: {type: string, data: ConnectedData}) } } -function organization(state = '', action: {type: string, data: ConnectedData}) { +function organizations(state: string[] = [], action: {type: string, data: ConnectedData}) { switch (action.type) { case ActionTypes.RECEIVED_CONNECTED: - if (action.data && action.data.organization) { - return action.data.organization; + if (action.data && action.data.organizations) { + return action.data.organizations; } - return ''; + return []; default: return state; } @@ -217,7 +217,7 @@ const attachCommentToIssueModalForPostId = (state = '', action: {type: string, d export default combineReducers({ connected, enterpriseURL, - organization, + organizations, username, userSettings, configuration, diff --git a/webapp/src/selectors.ts b/webapp/src/selectors.ts index 6cbeb3ba0..47b710182 100644 --- a/webapp/src/selectors.ts +++ b/webapp/src/selectors.ts @@ -51,14 +51,14 @@ function mapPrsToDetails(prs: GithubIssueData[], details: PrsDetailsData[]) { export const getSidebarData = createSelector( getPluginState, (pluginState): SidebarData => { - const {username, sidebarContent, reviewDetails, yourPrDetails, organization, rhsState} = pluginState; + const {username, sidebarContent, reviewDetails, yourPrDetails, organizations, rhsState} = pluginState; return { username, reviews: mapPrsToDetails(sidebarContent.reviews || emptyArray, reviewDetails), yourPrs: mapPrsToDetails(sidebarContent.prs || emptyArray, yourPrDetails), yourAssignments: sidebarContent.assignments || emptyArray, unreads: sidebarContent.unreads || emptyArray, - org: organization, + orgs: organizations, rhsState, }; }, diff --git a/webapp/src/types/github_types.ts b/webapp/src/types/github_types.ts index 27aeb4941..3bccd581e 100644 --- a/webapp/src/types/github_types.ts +++ b/webapp/src/types/github_types.ts @@ -60,7 +60,7 @@ export type ConnectedData = { github_username: string; github_client_id: string; enterprise_base_url: string; - organization: string; + organizations: string[]; user_settings: UserSettingsData; configuration: Record; } @@ -136,6 +136,6 @@ export type SidebarData = { yourPrs: GithubIssueData[]; yourAssignments: GithubIssueData[], unreads: UnreadsData[] - org: string, + orgs: string[], rhsState?: string | null }