From f4dcb4edf2aafca85c9af631131a77888da24bc7 Mon Sep 17 00:00:00 2001 From: Martin Kraft Date: Wed, 2 May 2018 07:31:14 -0400 Subject: MM-10182 & MM-10183: Adds channel scheme and team scheme API endpoint. (#8680) * MM-10183: Adds channel scheme API endpoint. MM-10182: Adds team scheme API endpoint. MM-10182_3: Switch from scheme_id in path to body. * MM-10182/MM-10183: Changes path from 'schemes' to 'scheme'. * MM-10182: Fix merge error. --- api4/channel.go | 51 +++++++++++++++++++++++++++++++++++++ api4/channel_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ api4/team.go | 51 +++++++++++++++++++++++++++++++++++++ api4/team_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++ app/channel.go | 17 +++++++++++++ app/channel_test.go | 18 +++++++++++++ app/team.go | 18 +++++++++++++ app/team_test.go | 18 +++++++++++++ i18n/en.json | 16 ++++++++++++ model/client4.go | 30 ++++++++++++++++++++++ model/scheme.go | 15 +++++++++++ 11 files changed, 370 insertions(+) diff --git a/api4/channel.go b/api4/channel.go index 83fa8eb18..a19a1b094 100644 --- a/api4/channel.go +++ b/api4/channel.go @@ -15,6 +15,7 @@ func (api *API) InitChannel() { api.BaseRoutes.Channels.Handle("/direct", api.ApiSessionRequired(createDirectChannel)).Methods("POST") api.BaseRoutes.Channels.Handle("/group", api.ApiSessionRequired(createGroupChannel)).Methods("POST") api.BaseRoutes.Channels.Handle("/members/{user_id:[A-Za-z0-9]+}/view", api.ApiSessionRequired(viewChannel)).Methods("POST") + api.BaseRoutes.Channels.Handle("/{channel_id:[A-Za-z0-9]+}/scheme", api.ApiSessionRequired(updateChannelScheme)).Methods("PUT") api.BaseRoutes.ChannelsForTeam.Handle("", api.ApiSessionRequired(getPublicChannelsForTeam)).Methods("GET") api.BaseRoutes.ChannelsForTeam.Handle("/deleted", api.ApiSessionRequired(getDeletedChannelsForTeam)).Methods("GET") @@ -948,3 +949,53 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { ReturnStatusOK(w) } + +func updateChannelScheme(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireChannelId() + if c.Err != nil { + return + } + + schemeID := model.SchemeIDFromJson(r.Body) + if schemeID == nil || len(*schemeID) != 26 { + c.SetInvalidParam("scheme_id") + return + } + + if c.App.License() == nil { + c.Err = model.NewAppError("Api4.UpdateChannelScheme", "api.channel.update_channel_scheme.license.error", nil, "", http.StatusNotImplemented) + return + } + + if !c.App.SessionHasPermissionToChannel(c.Session, c.Params.ChannelId, model.PERMISSION_MANAGE_SYSTEM) { + c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) + return + } + + scheme, err := c.App.GetScheme(*schemeID) + if err != nil { + c.Err = err + return + } + + if scheme.Scope != model.SCHEME_SCOPE_CHANNEL { + c.Err = model.NewAppError("Api4.UpdateChannelScheme", "api.channel.update_channel_scheme.scheme_scope.error", nil, "", http.StatusBadRequest) + return + } + + channel, err := c.App.GetChannel(c.Params.ChannelId) + if err != nil { + c.Err = err + return + } + + channel.SchemeId = &scheme.Id + + _, err = c.App.UpdateChannelScheme(channel) + if err != nil { + c.Err = err + return + } + + ReturnStatusOK(w) +} diff --git a/api4/channel_test.go b/api4/channel_test.go index c3d8c8039..7618b22d9 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -1879,3 +1879,75 @@ func TestAutocompleteChannels(t *testing.T) { } } } + +func TestUpdateChannelScheme(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + th.App.SetLicense(model.NewTestLicense("")) + + team := &model.Team{ + DisplayName: "Name", + Description: "Some description", + CompanyName: "Some company name", + AllowOpenInvite: false, + InviteId: "inviteid0", + Name: "z-z-" + model.NewId() + "a", + Email: "success+" + model.NewId() + "@simulator.amazonses.com", + Type: model.TEAM_OPEN, + } + team, _ = th.SystemAdminClient.CreateTeam(team) + + channel := &model.Channel{ + DisplayName: "Name", + Name: "z-z-" + model.NewId() + "a", + Type: model.CHANNEL_OPEN, + TeamId: team.Id, + } + channel, _ = th.SystemAdminClient.CreateChannel(channel) + + channelScheme := &model.Scheme{ + Name: "Name", + Description: "Some description", + Scope: model.SCHEME_SCOPE_CHANNEL, + } + channelScheme, _ = th.SystemAdminClient.CreateScheme(channelScheme) + teamScheme := &model.Scheme{ + Name: "Name", + Description: "Some description", + Scope: model.SCHEME_SCOPE_TEAM, + } + teamScheme, _ = th.SystemAdminClient.CreateScheme(teamScheme) + + // Test the setup/base case. + _, resp := th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id) + CheckNoError(t, resp) + + // Test various invalid channel and scheme id combinations. + _, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, "x") + CheckBadRequestStatus(t, resp) + _, resp = th.SystemAdminClient.UpdateChannelScheme("x", channelScheme.Id) + CheckBadRequestStatus(t, resp) + _, resp = th.SystemAdminClient.UpdateChannelScheme("x", "x") + CheckBadRequestStatus(t, resp) + + // Test that permissions are required. + _, resp = th.Client.UpdateChannelScheme(channel.Id, channelScheme.Id) + CheckForbiddenStatus(t, resp) + + // Test that a license is requried. + th.App.SetLicense(nil) + _, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id) + CheckNotImplementedStatus(t, resp) + th.App.SetLicense(model.NewTestLicense("")) + + // Test an invalid scheme scope. + _, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, teamScheme.Id) + fmt.Printf("resp: %+v\n", resp) + CheckBadRequestStatus(t, resp) + + // Test that an unauthenticated user gets rejected. + th.SystemAdminClient.Logout() + _, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id) + CheckUnauthorizedStatus(t, resp) +} diff --git a/api4/team.go b/api4/team.go index 023289579..1c2e9514e 100644 --- a/api4/team.go +++ b/api4/team.go @@ -20,6 +20,7 @@ const ( func (api *API) InitTeam() { api.BaseRoutes.Teams.Handle("", api.ApiSessionRequired(createTeam)).Methods("POST") api.BaseRoutes.Teams.Handle("", api.ApiSessionRequired(getAllTeams)).Methods("GET") + api.BaseRoutes.Teams.Handle("/{team_id:[A-Za-z0-9]+}/scheme", api.ApiSessionRequired(updateTeamScheme)).Methods("PUT") api.BaseRoutes.Teams.Handle("/search", api.ApiSessionRequired(searchTeams)).Methods("POST") api.BaseRoutes.TeamsForUser.Handle("", api.ApiSessionRequired(getTeamsForUser)).Methods("GET") api.BaseRoutes.TeamsForUser.Handle("/unread", api.ApiSessionRequired(getTeamsUnreadForUser)).Methods("GET") @@ -833,3 +834,53 @@ func removeTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("") ReturnStatusOK(w) } + +func updateTeamScheme(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireTeamId() + if c.Err != nil { + return + } + + schemeID := model.SchemeIDFromJson(r.Body) + if schemeID == nil || len(*schemeID) != 26 { + c.SetInvalidParam("scheme_id") + return + } + + if c.App.License() == nil { + c.Err = model.NewAppError("Api4.UpdateTeamScheme", "api.team.update_team_scheme.license.error", nil, "", http.StatusNotImplemented) + return + } + + if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_MANAGE_SYSTEM) { + c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) + return + } + + scheme, err := c.App.GetScheme(*schemeID) + if err != nil { + c.Err = err + return + } + + if scheme.Scope != model.SCHEME_SCOPE_TEAM { + c.Err = model.NewAppError("Api4.UpdateTeamScheme", "api.team.update_team_scheme.scheme_scope.error", nil, "", http.StatusBadRequest) + return + } + + team, err := c.App.GetTeam(c.Params.TeamId) + if err != nil { + c.Err = err + return + } + + team.SchemeId = &scheme.Id + + _, err = c.App.UpdateTeamScheme(team) + if err != nil { + c.Err = err + return + } + + ReturnStatusOK(w) +} diff --git a/api4/team_test.go b/api4/team_test.go index 6540457b0..6df56f754 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -2052,3 +2052,67 @@ func TestRemoveTeamIcon(t *testing.T) { _, resp = Client.RemoveTeamIcon(team.Id) CheckForbiddenStatus(t, resp) } + +func TestUpdateTeamScheme(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + th.App.SetLicense(model.NewTestLicense("")) + + team := &model.Team{ + DisplayName: "Name", + Description: "Some description", + CompanyName: "Some company name", + AllowOpenInvite: false, + InviteId: "inviteid0", + Name: "z-z-" + model.NewId() + "a", + Email: "success+" + model.NewId() + "@simulator.amazonses.com", + Type: model.TEAM_OPEN, + } + team, _ = th.SystemAdminClient.CreateTeam(team) + + teamScheme := &model.Scheme{ + Name: "Name", + Description: "Some description", + Scope: model.SCHEME_SCOPE_TEAM, + } + teamScheme, _ = th.SystemAdminClient.CreateScheme(teamScheme) + channelScheme := &model.Scheme{ + Name: "Name", + Description: "Some description", + Scope: model.SCHEME_SCOPE_CHANNEL, + } + channelScheme, _ = th.SystemAdminClient.CreateScheme(channelScheme) + + // Test the setup/base case. + _, resp := th.SystemAdminClient.UpdateTeamScheme(team.Id, teamScheme.Id) + CheckNoError(t, resp) + + // Test various invalid team and scheme id combinations. + _, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, "x") + CheckBadRequestStatus(t, resp) + _, resp = th.SystemAdminClient.UpdateTeamScheme("x", teamScheme.Id) + CheckBadRequestStatus(t, resp) + _, resp = th.SystemAdminClient.UpdateTeamScheme("x", "x") + CheckBadRequestStatus(t, resp) + + // Test that permissions are required. + _, resp = th.Client.UpdateTeamScheme(team.Id, teamScheme.Id) + CheckForbiddenStatus(t, resp) + + // Test that a license is requried. + th.App.SetLicense(nil) + _, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, teamScheme.Id) + CheckNotImplementedStatus(t, resp) + th.App.SetLicense(model.NewTestLicense("")) + + // Test an invalid scheme scope. + _, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, channelScheme.Id) + fmt.Printf("resp: %+v\n", resp) + CheckBadRequestStatus(t, resp) + + // Test that an unauthenticated user gets rejected. + th.SystemAdminClient.Logout() + _, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, teamScheme.Id) + CheckUnauthorizedStatus(t, resp) +} diff --git a/app/channel.go b/app/channel.go index 516e8d094..4b606ac27 100644 --- a/app/channel.go +++ b/app/channel.go @@ -354,6 +354,23 @@ func (a *App) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppE } } +func (a *App) UpdateChannelScheme(channel *model.Channel) (*model.Channel, *model.AppError) { + var oldChannel *model.Channel + var err *model.AppError + if oldChannel, err = a.GetChannel(channel.Id); err != nil { + return nil, err + } + + oldChannel.SchemeId = channel.SchemeId + + newChannel, err := a.UpdateChannel(oldChannel) + if err != nil { + return nil, err + } + + return newChannel, nil +} + func (a *App) UpdateChannelPrivacy(oldChannel *model.Channel, user *model.User) (*model.Channel, *model.AppError) { if channel, err := a.UpdateChannel(oldChannel); err != nil { return channel, err diff --git a/app/channel_test.go b/app/channel_test.go index de8a6a6a0..336d9b25b 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -381,3 +381,21 @@ func TestAddChannelMemberNoUserRequestor(t *testing.T) { assert.Equal(t, user.Username, post.Props["username"]) } } + +func TestAppUpdateChannelScheme(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + channel := th.BasicChannel + mockID := model.NewString("x") + channel.SchemeId = mockID + + updatedChannel, err := th.App.UpdateChannelScheme(channel) + if err != nil { + t.Fatal(err) + } + + if updatedChannel.SchemeId != mockID { + t.Fatal("Wrong Channel SchemeId") + } +} diff --git a/app/team.go b/app/team.go index 4fc410934..d8ebbab2a 100644 --- a/app/team.go +++ b/app/team.go @@ -114,6 +114,24 @@ func (a *App) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { return oldTeam, nil } +func (a *App) UpdateTeamScheme(team *model.Team) (*model.Team, *model.AppError) { + var oldTeam *model.Team + var err *model.AppError + if oldTeam, err = a.GetTeam(team.Id); err != nil { + return nil, err + } + + oldTeam.SchemeId = team.SchemeId + + if result := <-a.Srv.Store.Team().Update(oldTeam); result.Err != nil { + return nil, result.Err + } + + a.sendTeamEvent(oldTeam, model.WEBSOCKET_EVENT_UPDATE_TEAM) + + return oldTeam, nil +} + func (a *App) PatchTeam(teamId string, patch *model.TeamPatch) (*model.Team, *model.AppError) { team, err := a.GetTeam(teamId) if err != nil { diff --git a/app/team_test.go b/app/team_test.go index 7ebfb8166..6a47da58b 100644 --- a/app/team_test.go +++ b/app/team_test.go @@ -559,3 +559,21 @@ func TestJoinUserToTeam(t *testing.T) { } }) } + +func TestAppUpdateTeamScheme(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + team := th.BasicTeam + mockID := model.NewString("x") + team.SchemeId = mockID + + updatedTeam, err := th.App.UpdateTeamScheme(th.BasicTeam) + if err != nil { + t.Fatal(err) + } + + if updatedTeam.SchemeId != mockID { + t.Fatal("Wrong Team SchemeId") + } +} diff --git a/i18n/en.json b/i18n/en.json index bbbeb3302..c0c8ea184 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2506,6 +2506,14 @@ "id": "api.team.update_team.permissions.app_error", "translation": "You do not have the appropriate permissions" }, + { + "id": "api.team.update_team_scheme.license.error", + "translation": "License does not support updating a team's scheme" + }, + { + "id": "api.team.update_team_scheme.scheme_scope.error", + "translation": "Unable to set the scheme to the team because the supplied scheme is not a team scheme." + }, { "id": "api.templates.channel_name.group", "translation": "Group Message" @@ -6694,6 +6702,14 @@ "id": "api.channel.update_team_member_roles.scheme_role.app_error", "translation": "The provided role is managed by a Scheme and therefore cannot be applied directly to a Team Member" }, + { + "id": "api.channel.update_channel_scheme.license.error", + "translation": "License does not support updating a channel's scheme" + }, + { + "id": "api.channel.update_channel_scheme.scheme_scope.error", + "translation": "Unable to set the scheme to the channel because the supplied scheme is not a channel scheme." + }, { "id": "store.sql_channel.get_by_scheme.app_error", "translation": "Unable to get the channels for the provided scheme" diff --git a/model/client4.go b/model/client4.go index cf34c9fd7..f17bb089a 100644 --- a/model/client4.go +++ b/model/client4.go @@ -326,6 +326,14 @@ func (c *Client4) GetTimezonesRoute() string { return fmt.Sprintf(c.GetSystemRoute() + "/timezones") } +func (c *Client4) GetChannelSchemeRoute(channelId string) string { + return fmt.Sprintf(c.GetChannelsRoute()+"/%v/scheme", channelId) +} + +func (c *Client4) GetTeamSchemeRoute(teamId string) string { + return fmt.Sprintf(c.GetTeamsRoute()+"/%v/scheme", teamId) +} + func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) { return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag) } @@ -3505,3 +3513,25 @@ func (c *Client4) DeactivatePlugin(id string) (bool, *Response) { return CheckStatusOK(r), BuildResponse(r) } } + +// UpdateChannelScheme will update a channel's scheme. +func (c *Client4) UpdateChannelScheme(channelId, schemeId string) (bool, *Response) { + sip := &SchemeIDPatch{SchemeID: &schemeId} + if r, err := c.DoApiPut(c.GetChannelSchemeRoute(channelId), sip.ToJson()); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UpdateTeamScheme will update a team's scheme. +func (c *Client4) UpdateTeamScheme(teamId, schemeId string) (bool, *Response) { + sip := &SchemeIDPatch{SchemeID: &schemeId} + if r, err := c.DoApiPut(c.GetTeamSchemeRoute(teamId), sip.ToJson()); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} diff --git a/model/scheme.go b/model/scheme.go index 9ad153c73..c3ae7f15d 100644 --- a/model/scheme.go +++ b/model/scheme.go @@ -29,6 +29,10 @@ type Scheme struct { DefaultChannelUserRole string `json:"default_channel_user_role"` } +type SchemeIDPatch struct { + SchemeID *string `json:"scheme_id"` +} + func (scheme *Scheme) ToJson() string { b, _ := json.Marshal(scheme) return string(b) @@ -93,3 +97,14 @@ func (scheme *Scheme) IsValidForCreate() bool { return true } + +func SchemeIDFromJson(data io.Reader) *string { + var p *SchemeIDPatch + json.NewDecoder(data).Decode(&p) + return p.SchemeID +} + +func (p *SchemeIDPatch) ToJson() string { + b, _ := json.Marshal(p) + return string(b) +} -- cgit v1.2.3-1-g7c22