Move cmd execution to REST endpoint, remove /echo, add /me
This commit is contained in:
@@ -372,6 +372,36 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "executeCommand",
|
||||
"path": "/command/{command}/exec",
|
||||
"method": "POST",
|
||||
"title": "Execute command",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
"name": "command",
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"title": "Command to be executed"
|
||||
}
|
||||
],
|
||||
"post": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "input",
|
||||
"required": false,
|
||||
"title": "Arbitrary command input"
|
||||
},
|
||||
{
|
||||
"type": "[]string",
|
||||
"name": "params",
|
||||
"required": false,
|
||||
"title": "Command parameters"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "history",
|
||||
"path": "/",
|
||||
|
||||
@@ -35,6 +35,36 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "executeCommand",
|
||||
"Method": "POST",
|
||||
"Title": "Execute command",
|
||||
"Path": "/command/{command}/exec",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
"name": "command",
|
||||
"required": true,
|
||||
"title": "Command to be executed",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"post": [
|
||||
{
|
||||
"name": "input",
|
||||
"required": false,
|
||||
"title": "Arbitrary command input",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"required": false,
|
||||
"title": "Command parameters",
|
||||
"type": "[]string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "history",
|
||||
"Method": "GET",
|
||||
|
||||
@@ -300,6 +300,7 @@ The following event types may be sent with a message event:
|
||||
| Method | Endpoint | Purpose |
|
||||
| ------ | -------- | ------- |
|
||||
| `POST` | `/channels/{channelID}/messages/` | Post new message to the channel |
|
||||
| `POST` | `/channels/{channelID}/messages/command/{command}/exec` | Execute command |
|
||||
| `GET` | `/channels/{channelID}/messages/` | All messages (channel history) |
|
||||
| `GET` | `/channels/{channelID}/messages/mark-as-read` | Manages read/unread messages in a channel or a thread |
|
||||
| `PUT` | `/channels/{channelID}/messages/{messageID}` | Edit existing message |
|
||||
@@ -328,6 +329,23 @@ The following event types may be sent with a message event:
|
||||
| message | string | POST | Message contents (markdown) | N/A | YES |
|
||||
| channelID | uint64 | PATH | Channel ID | N/A | YES |
|
||||
|
||||
## Execute command
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/channels/{channelID}/messages/command/{command}/exec` | HTTP/S | POST | Client ID, Session ID |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| command | string | PATH | Command to be executed | N/A | YES |
|
||||
| channelID | uint64 | PATH | Channel ID | N/A | YES |
|
||||
| input | string | POST | Arbitrary command input | N/A | NO |
|
||||
| params | []string | POST | Command parameters | N/A | NO |
|
||||
|
||||
## All messages (channel history)
|
||||
|
||||
#### Method
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package incoming
|
||||
|
||||
type (
|
||||
ExecCommand struct {
|
||||
ChannelID string `json:"channelId"`
|
||||
Command string `json:"command"`
|
||||
Params map[string]string `json:"params"`
|
||||
Input string `json:"input"`
|
||||
}
|
||||
)
|
||||
@@ -25,6 +25,4 @@ type Payload struct {
|
||||
*MessageDelete `json:"deleteMessage"`
|
||||
|
||||
*Users `json:"getUsers"`
|
||||
|
||||
*ExecCommand `json:"exec"`
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ func (Commands) New() *Commands {
|
||||
func (ctrl *Commands) List(ctx context.Context, r *request.CommandsList) (interface{}, error) {
|
||||
return types.CommandSet{
|
||||
&types.Command{
|
||||
Name: "echo",
|
||||
Description: "It does exactly what it says on the tin"},
|
||||
Name: "me",
|
||||
Description: "Illeism"},
|
||||
&types.Command{
|
||||
Name: "shrug",
|
||||
Description: "It does exactly what it says on the tin"},
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
// Internal API interface
|
||||
type MessageAPI interface {
|
||||
Create(context.Context, *request.MessageCreate) (interface{}, error)
|
||||
ExecuteCommand(context.Context, *request.MessageExecuteCommand) (interface{}, error)
|
||||
History(context.Context, *request.MessageHistory) (interface{}, error)
|
||||
MarkAsRead(context.Context, *request.MessageMarkAsRead) (interface{}, error)
|
||||
Edit(context.Context, *request.MessageEdit) (interface{}, error)
|
||||
@@ -46,6 +47,7 @@ type MessageAPI interface {
|
||||
// HTTP API interface
|
||||
type Message struct {
|
||||
Create func(http.ResponseWriter, *http.Request)
|
||||
ExecuteCommand func(http.ResponseWriter, *http.Request)
|
||||
History func(http.ResponseWriter, *http.Request)
|
||||
MarkAsRead func(http.ResponseWriter, *http.Request)
|
||||
Edit func(http.ResponseWriter, *http.Request)
|
||||
@@ -69,6 +71,13 @@ func NewMessage(mh MessageAPI) *Message {
|
||||
return mh.Create(r.Context(), params)
|
||||
})
|
||||
},
|
||||
ExecuteCommand: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewMessageExecuteCommand()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) {
|
||||
return mh.ExecuteCommand(r.Context(), params)
|
||||
})
|
||||
},
|
||||
History: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewMessageHistory()
|
||||
@@ -160,6 +169,7 @@ func (mh *Message) MountRoutes(r chi.Router, middlewares ...func(http.Handler) h
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middlewares...)
|
||||
r.Post("/channels/{channelID}/messages/", mh.Create)
|
||||
r.Post("/channels/{channelID}/messages/command/{command}/exec", mh.ExecuteCommand)
|
||||
r.Get("/channels/{channelID}/messages/", mh.History)
|
||||
r.Get("/channels/{channelID}/messages/mark-as-read", mh.MarkAsRead)
|
||||
r.Put("/channels/{channelID}/messages/{messageID}", mh.Edit)
|
||||
|
||||
@@ -65,6 +65,35 @@ func (ctrl *Message) Edit(ctx context.Context, r *request.MessageEdit) (interfac
|
||||
}))
|
||||
}
|
||||
|
||||
func (ctrl Message) ExecuteCommand(ctx context.Context, r *request.MessageExecuteCommand) (interface{}, error) {
|
||||
switch r.Command {
|
||||
case "me":
|
||||
if r.Input != "" {
|
||||
return ctrl.svc.msg.With(ctx).Create(&types.Message{
|
||||
Type: types.MessageTypeIlleism,
|
||||
ChannelID: r.ChannelID,
|
||||
Message: r.Input,
|
||||
})
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
||||
case "shrug":
|
||||
msg := &types.Message{
|
||||
ChannelID: r.ChannelID,
|
||||
Message: `¯\\_(ツ)_/¯`,
|
||||
}
|
||||
|
||||
if r.Input != "" {
|
||||
msg.Message = r.Input + " " + msg.Message
|
||||
}
|
||||
|
||||
return ctrl.svc.msg.With(ctx).Create(msg)
|
||||
}
|
||||
|
||||
return nil, errors.New("unknown command")
|
||||
}
|
||||
|
||||
func (ctrl *Message) Delete(ctx context.Context, r *request.MessageDelete) (interface{}, error) {
|
||||
return nil, ctrl.svc.msg.With(ctx).Delete(r.MessageID)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,57 @@ func (mReq *MessageCreate) Fill(r *http.Request) (err error) {
|
||||
|
||||
var _ RequestFiller = NewMessageCreate()
|
||||
|
||||
// Message executeCommand request parameters
|
||||
type MessageExecuteCommand struct {
|
||||
Command string
|
||||
ChannelID uint64 `json:",string"`
|
||||
Input string
|
||||
Params []string
|
||||
}
|
||||
|
||||
func NewMessageExecuteCommand() *MessageExecuteCommand {
|
||||
return &MessageExecuteCommand{}
|
||||
}
|
||||
|
||||
func (mReq *MessageExecuteCommand) Fill(r *http.Request) (err error) {
|
||||
if strings.ToLower(r.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(r.Body).Decode(mReq)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
if err = r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := r.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := r.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
mReq.Command = chi.URLParam(r, "command")
|
||||
mReq.ChannelID = parseUInt64(chi.URLParam(r, "channelID"))
|
||||
if val, ok := post["input"]; ok {
|
||||
|
||||
mReq.Input = val
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewMessageExecuteCommand()
|
||||
|
||||
// Message history request parameters
|
||||
type MessageHistory struct {
|
||||
LastMessageID uint64 `json:",string"`
|
||||
|
||||
@@ -82,6 +82,7 @@ const (
|
||||
MessageTypeChannelEvent = "channelEvent"
|
||||
MessageTypeInlineImage = "inlineImage"
|
||||
MessageTypeAttachment = "attachment"
|
||||
MessageTypeIlleism = "illeism"
|
||||
)
|
||||
|
||||
func (mtype MessageType) String() string {
|
||||
|
||||
@@ -49,9 +49,6 @@ func (s *Session) dispatch(raw []byte) error {
|
||||
|
||||
case p.Users != nil:
|
||||
return s.userList(ctx, p.Users)
|
||||
|
||||
case p.ExecCommand != nil:
|
||||
return s.execCommand(ctx, p.ExecCommand)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/crusttech/crust/internal/payload"
|
||||
"github.com/crusttech/crust/internal/payload/incoming"
|
||||
"github.com/crusttech/crust/internal/payload/outgoing"
|
||||
"github.com/crusttech/crust/messaging/types"
|
||||
systemService "github.com/crusttech/crust/system/service"
|
||||
)
|
||||
|
||||
func (s *Session) execCommand(ctx context.Context, c *incoming.ExecCommand) error {
|
||||
// @todo: check access / can we join this channel (should be done by service layer)
|
||||
|
||||
log.Printf("Received command '%s(%v)", c.Command, c.Params)
|
||||
|
||||
switch c.Command {
|
||||
case "echo":
|
||||
if c.Input != "" {
|
||||
user, err := systemService.User(ctx).FindByID(s.user.Identity())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.sendReply(&outgoing.Message{
|
||||
ID: factory.Sonyflake.NextID(),
|
||||
User: payload.User(user),
|
||||
CreatedAt: time.Now(),
|
||||
Type: "hallucination",
|
||||
ChannelID: c.ChannelID,
|
||||
Message: c.Input})
|
||||
}
|
||||
|
||||
case "shrug":
|
||||
msg := &types.Message{
|
||||
ChannelID: payload.ParseUInt64(c.ChannelID),
|
||||
Message: "¯\\_(ツ)_/¯",
|
||||
}
|
||||
|
||||
if c.Input != "" {
|
||||
msg.Message = c.Input + " " + msg.Message
|
||||
}
|
||||
_, err := s.svc.msg.With(ctx).Create(msg)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user