Unverified Commit 1f6c271b authored by Joram Wilander's avatar Joram Wilander Committed by GitHub

MM-8708 Remove api package (#8784)

* Remove api package

* Remove api dependency from cmd package

* Remove EnableAPIv3 setting

* Update web tests

* Add more websocket tests

* Move some ws and oauth tests to api4 package

* Move command tests into api4 package

* Test fixes

* Fix msg command test

* Add some app file tests
parent 02f8c18f
...@@ -496,8 +496,6 @@ clean: stop-docker ## Clean up everything except persistant server data. ...@@ -496,8 +496,6 @@ clean: stop-docker ## Clean up everything except persistant server data.
rm -f mattermost.log rm -f mattermost.log
rm -f mattermost.log.jsonl rm -f mattermost.log.jsonl
rm -f npm-debug.log rm -f npm-debug.log
rm -f api/mattermost.log
rm -f api/mattermost.log.jsonl
rm -f .prepare-go rm -f .prepare-go
rm -f enterprise rm -f enterprise
rm -f cover.out rm -f cover.out
......
This diff is collapsed.
This diff is collapsed.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"net/http"
"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/app"
"github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model"
_ "github.com/nicksnyder/go-i18n/i18n"
)
type Routes struct {
Root *mux.Router // ''
ApiRoot *mux.Router // 'api/v3'
Users *mux.Router // 'api/v3/users'
NeedUser *mux.Router // 'api/v3/users/{user_id:[A-Za-z0-9]+}'
Teams *mux.Router // 'api/v3/teams'
NeedTeam *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}'
Channels *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels'
NeedChannel *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}'
NeedChannelName *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/name/{channel_name:[A-Za-z0-9_-]+}'
Posts *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}/posts'
NeedPost *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}/posts/{post_id:[A-Za-z0-9]+}'
Commands *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/commands'
Hooks *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/hooks'
TeamFiles *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/files'
Files *mux.Router // 'api/v3/files'
NeedFile *mux.Router // 'api/v3/files/{file_id:[A-Za-z0-9]+}'
OAuth *mux.Router // 'api/v3/oauth'
Admin *mux.Router // 'api/v3/admin'
General *mux.Router // 'api/v3/general'
Preferences *mux.Router // 'api/v3/preferences'
License *mux.Router // 'api/v3/license'
Public *mux.Router // 'api/v3/public'
Emoji *mux.Router // 'api/v3/emoji'
Webrtc *mux.Router // 'api/v3/webrtc'
}
type API struct {
App *app.App
BaseRoutes *Routes
}
func Init(a *app.App, root *mux.Router) *API {
api := &API{
App: a,
BaseRoutes: &Routes{},
}
api.BaseRoutes.Root = root
api.BaseRoutes.ApiRoot = root.PathPrefix(model.API_URL_SUFFIX_V3).Subrouter()
api.BaseRoutes.Users = api.BaseRoutes.ApiRoot.PathPrefix("/users").Subrouter()
api.BaseRoutes.NeedUser = api.BaseRoutes.Users.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter()
api.BaseRoutes.Teams = api.BaseRoutes.ApiRoot.PathPrefix("/teams").Subrouter()
api.BaseRoutes.NeedTeam = api.BaseRoutes.Teams.PathPrefix("/{team_id:[A-Za-z0-9]+}").Subrouter()
api.BaseRoutes.Channels = api.BaseRoutes.NeedTeam.PathPrefix("/channels").Subrouter()
api.BaseRoutes.NeedChannel = api.BaseRoutes.Channels.PathPrefix("/{channel_id:[A-Za-z0-9]+}").Subrouter()
api.BaseRoutes.NeedChannelName = api.BaseRoutes.Channels.PathPrefix("/name/{channel_name:[A-Za-z0-9_-]+}").Subrouter()
api.BaseRoutes.Posts = api.BaseRoutes.NeedChannel.PathPrefix("/posts").Subrouter()
api.BaseRoutes.NeedPost = api.BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter()
api.BaseRoutes.Commands = api.BaseRoutes.NeedTeam.PathPrefix("/commands").Subrouter()
api.BaseRoutes.TeamFiles = api.BaseRoutes.NeedTeam.PathPrefix("/files").Subrouter()
api.BaseRoutes.Files = api.BaseRoutes.ApiRoot.PathPrefix("/files").Subrouter()
api.BaseRoutes.NeedFile = api.BaseRoutes.Files.PathPrefix("/{file_id:[A-Za-z0-9]+}").Subrouter()
api.BaseRoutes.Hooks = api.BaseRoutes.NeedTeam.PathPrefix("/hooks").Subrouter()
api.BaseRoutes.OAuth = api.BaseRoutes.ApiRoot.PathPrefix("/oauth").Subrouter()
api.BaseRoutes.Admin = api.BaseRoutes.ApiRoot.PathPrefix("/admin").Subrouter()
api.BaseRoutes.General = api.BaseRoutes.ApiRoot.PathPrefix("/general").Subrouter()
api.BaseRoutes.Preferences = api.BaseRoutes.ApiRoot.PathPrefix("/preferences").Subrouter()
api.BaseRoutes.License = api.BaseRoutes.ApiRoot.PathPrefix("/license").Subrouter()
api.BaseRoutes.Public = api.BaseRoutes.ApiRoot.PathPrefix("/public").Subrouter()
api.BaseRoutes.Emoji = api.BaseRoutes.ApiRoot.PathPrefix("/emoji").Subrouter()
api.BaseRoutes.Webrtc = api.BaseRoutes.ApiRoot.PathPrefix("/webrtc").Subrouter()
api.InitUser()
api.InitTeam()
api.InitChannel()
api.InitPost()
api.InitWebSocket()
api.InitFile()
api.InitCommand()
api.InitAdmin()
api.InitGeneral()
api.InitOAuth()
api.InitWebhook()
api.InitPreference()
api.InitLicense()
api.InitEmoji()
api.InitStatus()
api.InitWebrtc()
api.InitReaction()
// 404 on any api route before web.go has a chance to serve it
root.Handle("/api/{anything:.*}", http.HandlerFunc(api.Handle404))
a.InitEmailBatching()
if *a.Config().ServiceSettings.EnableAPIv3 {
mlog.Info("API version 3 is scheduled for deprecation. Please see https://api.mattermost.com for details.")
}
return api
}
func (api *API) Handle404(w http.ResponseWriter, r *http.Request) {
Handle404(api.App, w, r)
}
func ReturnStatusOK(w http.ResponseWriter) {
m := make(map[string]string)
m[model.STATUS] = model.STATUS_OK
w.Write([]byte(model.MapToJson(m)))
}
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"flag"
"os"
"testing"
"github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/store/storetest"
"github.com/mattermost/mattermost-server/utils"
)
func TestMain(m *testing.M) {
flag.Parse()
// Setup a global logger to catch tests logging outside of app context
// The global logger will be stomped by apps initalizing but that's fine for testing. Ideally this won't happen.
mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{
EnableConsole: true,
ConsoleJson: true,
ConsoleLevel: "error",
EnableFile: false,
}))
utils.TranslationsPreInit()
// In the case where a dev just wants to run a single test, it's faster to just use the default
// store.
if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." {
mlog.Info("-test.run used, not creating temporary containers")
os.Exit(m.Run())
}
status := 0
container, settings, err := storetest.NewMySQLContainer()
if err != nil {
panic(err)
}
UseTestStore(container, settings)
defer func() {
StopTestStore()
os.Exit(status)
}()
status = m.Run()
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"io/ioutil"
"net/http"
"strings"
"github.com/mattermost/mattermost-server/model"
)
func (api *API) InitCommand() {
api.BaseRoutes.Commands.Handle("/execute", api.ApiUserRequired(executeCommand)).Methods("POST")
api.BaseRoutes.Commands.Handle("/list", api.ApiUserRequired(listCommands)).Methods("GET")
api.BaseRoutes.Commands.Handle("/create", api.ApiUserRequired(createCommand)).Methods("POST")
api.BaseRoutes.Commands.Handle("/update", api.ApiUserRequired(updateCommand)).Methods("POST")
api.BaseRoutes.Commands.Handle("/list_team_commands", api.ApiUserRequired(listTeamCommands)).Methods("GET")
api.BaseRoutes.Commands.Handle("/regen_token", api.ApiUserRequired(regenCommandToken)).Methods("POST")
api.BaseRoutes.Commands.Handle("/delete", api.ApiUserRequired(deleteCommand)).Methods("POST")
api.BaseRoutes.Teams.Handle("/command_test", api.ApiAppHandler(testCommand)).Methods("POST")
api.BaseRoutes.Teams.Handle("/command_test", api.ApiAppHandler(testCommand)).Methods("GET")
api.BaseRoutes.Teams.Handle("/command_test_e", api.ApiAppHandler(testEphemeralCommand)).Methods("POST")
api.BaseRoutes.Teams.Handle("/command_test_e", api.ApiAppHandler(testEphemeralCommand)).Methods("GET")
}
func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
commands, err := c.App.ListAutocompleteCommands(c.TeamId, c.T)
if err != nil {
c.Err = err
return
}
w.Write([]byte(model.CommandListToJson(commands)))
}
func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
commandArgs := model.CommandArgsFromJson(r.Body)
if commandArgs == nil {
c.SetInvalidParam("executeCommand", "command_args")
return
}
if len(commandArgs.Command) <= 1 || strings.Index(commandArgs.Command, "/") != 0 {
c.Err = model.NewAppError("executeCommand", "api.command.execute_command.start.app_error", nil, "", http.StatusBadRequest)
return
}
if len(commandArgs.ChannelId) > 0 {
if !c.App.SessionHasPermissionToChannel(c.Session, commandArgs.ChannelId, model.PERMISSION_USE_SLASH_COMMANDS) {
c.SetPermissionError(model.PERMISSION_USE_SLASH_COMMANDS)
return
}
}
commandArgs.TeamId = c.TeamId
commandArgs.UserId = c.Session.UserId
commandArgs.T = c.T
commandArgs.Session = c.Session
commandArgs.SiteURL = c.GetSiteURLHeader()
response, err := c.App.ExecuteCommand(commandArgs)
if err != nil {
c.Err = err
return
}
w.Write([]byte(response.ToJson()))
}
func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
cmd := model.CommandFromJson(r.Body)
if cmd == nil {
c.SetInvalidParam("createCommand", "command")
return
}
c.LogAudit("attempt")
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
return
}
cmd.CreatorId = c.Session.UserId
cmd.TeamId = c.TeamId
rcmd, err := c.App.CreateCommand(cmd)
if err != nil {
c.Err = err
return
}
c.LogAudit("success")
w.Write([]byte(rcmd.ToJson()))
}
func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) {
cmd := model.CommandFromJson(r.Body)
if cmd == nil {
c.SetInvalidParam("updateCommand", "command")
return
}
c.LogAudit("attempt")
oldCmd, err := c.App.GetCommand(cmd.Id)
if err != nil {
c.Err = err
return
}
if c.TeamId != oldCmd.TeamId {
c.Err = model.NewAppError("updateCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest)
return
}
if !c.App.SessionHasPermissionToTeam(c.Session, oldCmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
c.LogAudit("fail - inappropriate permissions")
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
return
}
if c.Session.UserId != oldCmd.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
c.LogAudit("fail - inappropriate permissions")
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
return
}
rcmd, err := c.App.UpdateCommand(oldCmd, cmd)
if err != nil {
c.Err = err
return
}
c.LogAudit("success")
w.Write([]byte(rcmd.ToJson()))
}
func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
return
}
cmds, err := c.App.ListTeamCommands(c.TeamId)
if err != nil {
c.Err = err
return
}
w.Write([]byte(model.CommandListToJson(cmds)))
}
func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
id := props["id"]
if len(id) == 0 {
c.SetInvalidParam("regenCommandToken", "id")
return
}
c.LogAudit("attempt")
cmd, err := c.App.GetCommand(id)
if err != nil {
c.Err = err
return
}
if c.TeamId != cmd.TeamId {
c.Err = model.NewAppError("regenCommandToken", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest)
return
}
if !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
c.LogAudit("fail - inappropriate permissions")
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
return
}
if c.Session.UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
c.LogAudit("fail - inappropriate permissions")
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
return
}
rcmd, err := c.App.RegenCommandToken(cmd)
if err != nil {
c.Err = err
return
}
w.Write([]byte(rcmd.ToJson()))
}
func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
id := props["id"]
if len(id) == 0 {
c.SetInvalidParam("deleteCommand", "id")
return
}
c.LogAudit("attempt")
cmd, err := c.App.GetCommand(id)
if err != nil {
c.Err = err
return
}
if c.TeamId != cmd.TeamId {
c.Err = model.NewAppError("deleteCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest)
return
}
if !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) {
c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS)
c.LogAudit("fail - inappropriate permissions")
return
}
if c.Session.UserId != cmd.CreatorId && !c.App.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) {
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)
c.LogAudit("fail - inappropriate permissions")
return
}
err = c.App.DeleteCommand(cmd.Id)
if err != nil {
c.Err = err
return
}
c.LogAudit("success")
w.Write([]byte(model.MapToJson(props)))
}
func testCommand(c *Context, w http.ResponseWriter, r *http.Request) {
r.ParseForm()
msg := ""
if r.Method == "POST" {
msg = msg + "\ntoken=" + r.FormValue("token")
msg = msg + "\nteam_domain=" + r.FormValue("team_domain")
} else {
body, _ := ioutil.ReadAll(r.Body)
msg = string(body)
}
rc := &model.CommandResponse{
Text: "test command response " + msg,
ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
}
w.Write([]byte(rc.ToJson()))
}
func testEphemeralCommand(c *Context, w http.ResponseWriter, r *http.Request) {
r.ParseForm()
msg := ""
if r.Method == "POST" {
msg = msg + "\ntoken=" + r.FormValue("token")
msg = msg + "\nteam_domain=" + r.FormValue("team_domain")
} else {
body, _ := ioutil.ReadAll(r.Body)
msg = string(body)
}
rc := &model.CommandResponse{
Text: "test command response " + msg,
ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL,
}
w.Write([]byte(rc.ToJson()))
}
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"testing"
"time"
"github.com/mattermost/mattermost-server/model"
)
func TestEchoCommand(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
Client := th.BasicClient
channel1 := th.BasicChannel
echoTestString := "/echo test"
if r1 := Client.Must(Client.Command(channel1.Id, echoTestString)).Data.(*model.CommandResponse); r1 == nil {
t.Fatal("Echo command failed to execute")
}
if r1 := Client.Must(Client.Command(channel1.Id, "/echo ")).Data.(*model.CommandResponse); r1 == nil {
t.Fatal("Echo command failed to execute")
}
time.Sleep(100 * time.Millisecond)
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
if len(p1.Order) != 2 {
t.Fatal("Echo command failed to send")
}
}
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"testing"
"time"
"github.com/mattermost/mattermost-server/model"
)
func TestExpandCommand(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
Client := th.BasicClient
channel := th.BasicChannel
r1 := Client.Must(Client.Command(channel.Id, "/expand")).Data.(*model.CommandResponse)
if r1 == nil {
t.Fatal("Command failed to execute")
}
time.Sleep(100 * time.Millisecond)
p1 := Client.Must(Client.GetPreference(model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, model.PREFERENCE_NAME_COLLAPSE_SETTING)).Data.(*model.Preference)
if p1.Value != "false" {
t.Fatal("preference not updated correctly")
}
}
func TestCollapseCommand(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
Client := th.BasicClient
channel := th.BasicChannel
r1 := Client.Must(Client.Command(channel.Id, "/collapse")).Data.(*model.CommandResponse)
if r1 == nil {
t.Fatal("Command failed to execute")
}
time.Sleep(100 * time.Millisecond)
p1 := Client.Must(Client.GetPreference(model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, model.PREFERENCE_NAME_COLLAPSE_SETTING)).Data.(*model.Preference)
if p1.Value != "true" {
t.Fatal("preference not updated correctly")
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"testing"
)
func TestLogoutTestCommand(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
th.BasicClient.Must(th.BasicClient.Command(th.BasicChannel.Id, "/logout"))
}
This diff is collapsed.
This diff is collapsed.
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"testing"
)
func TestOpenCommands(t *testing.T) {
testJoinCommands(t, "open")
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.