summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2018-08-24 06:16:17 -0400
committerGeorge Goldberg <george@gberg.me>2018-08-24 11:16:17 +0100
commit4de2e5206e23ee268b38f071ecd8fcbea0bbc80a (patch)
treebe3d191545afdfa91a861be69a04dd1f646a1e16
parent9599f1a52fd4b2d029113b3ec56bd75453ae4d7b (diff)
downloadchat-4de2e5206e23ee268b38f071ecd8fcbea0bbc80a.tar.gz
chat-4de2e5206e23ee268b38f071ecd8fcbea0bbc80a.tar.bz2
chat-4de2e5206e23ee268b38f071ecd8fcbea0bbc80a.zip
Support for interactive menus in message attachments (#9285)
-rw-r--r--api4/post.go7
-rw-r--r--app/post.go8
-rw-r--r--app/post_test.go54
-rw-r--r--model/post.go28
4 files changed, 89 insertions, 8 deletions
diff --git a/api4/post.go b/api4/post.go
index 2568ade0a..b4edc5124 100644
--- a/api4/post.go
+++ b/api4/post.go
@@ -513,7 +513,12 @@ func doPostAction(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if err := c.App.DoPostAction(c.Params.PostId, c.Params.ActionId, c.Session.UserId); err != nil {
+ actionRequest := model.DoPostActionRequestFromJson(r.Body)
+ if actionRequest == nil {
+ actionRequest = &model.DoPostActionRequest{}
+ }
+
+ if err := c.App.DoPostAction(c.Params.PostId, c.Params.ActionId, c.Session.UserId, actionRequest.SelectedOption); err != nil {
c.Err = err
return
}
diff --git a/app/post.go b/app/post.go
index 0eed87280..fb0ef3f78 100644
--- a/app/post.go
+++ b/app/post.go
@@ -852,7 +852,7 @@ func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) {
}
}
-func (a *App) DoPostAction(postId string, actionId string, userId string) *model.AppError {
+func (a *App) DoPostAction(postId, actionId, userId, selectedOption string) *model.AppError {
pchan := a.Srv.Store.Post().GetSingle(postId)
var post *model.Post
@@ -870,9 +870,15 @@ func (a *App) DoPostAction(postId string, actionId string, userId string) *model
request := &model.PostActionIntegrationRequest{
UserId: userId,
PostId: postId,
+ Type: action.Type,
Context: action.Integration.Context,
}
+ if action.Type == model.POST_ACTION_TYPE_SELECT {
+ request.DataSource = action.DataSource
+ request.Context["selected_option"] = selectedOption
+ }
+
req, _ := http.NewRequest("POST", action.Integration.URL, strings.NewReader(request.ToJson()))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
diff --git a/app/post_test.go b/app/post_test.go
index 482197833..66a141172 100644
--- a/app/post_test.go
+++ b/app/post_test.go
@@ -134,6 +134,12 @@ func TestPostAction(t *testing.T) {
err := json.NewDecoder(r.Body).Decode(&request)
assert.NoError(t, err)
assert.Equal(t, request.UserId, th.BasicUser.Id)
+ if request.Type == model.POST_ACTION_TYPE_SELECT {
+ assert.Equal(t, request.DataSource, "some_source")
+ assert.Equal(t, request.Context["selected_option"], "selected")
+ } else {
+ assert.Equal(t, request.DataSource, "")
+ }
assert.Equal(t, "foo", request.Context["s"])
assert.EqualValues(t, 3, request.Context["n"])
fmt.Fprintf(w, `{"update": {"message": "updated"}, "ephemeral_text": "foo"}`)
@@ -158,7 +164,9 @@ func TestPostAction(t *testing.T) {
},
URL: ts.URL,
},
- Name: "action",
+ Name: "action",
+ Type: "some_type",
+ DataSource: "some_source",
},
},
},
@@ -175,11 +183,51 @@ func TestPostAction(t *testing.T) {
require.NotEmpty(t, attachments[0].Actions)
require.NotEmpty(t, attachments[0].Actions[0].Id)
- err = th.App.DoPostAction(post.Id, "notavalidid", th.BasicUser.Id)
+ menuPost := model.Post{
+ Message: "Interactive post",
+ ChannelId: th.BasicChannel.Id,
+ PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()),
+ UserId: th.BasicUser.Id,
+ Props: model.StringInterface{
+ "attachments": []*model.SlackAttachment{
+ {
+ Text: "hello",
+ Actions: []*model.PostAction{
+ {
+ Integration: &model.PostActionIntegration{
+ Context: model.StringInterface{
+ "s": "foo",
+ "n": 3,
+ },
+ URL: ts.URL,
+ },
+ Name: "action",
+ Type: model.POST_ACTION_TYPE_SELECT,
+ DataSource: "some_source",
+ },
+ },
+ },
+ },
+ },
+ }
+
+ post2, err := th.App.CreatePostAsUser(&menuPost)
+ require.Nil(t, err)
+
+ attachments2, ok := post2.Props["attachments"].([]*model.SlackAttachment)
+ require.True(t, ok)
+
+ require.NotEmpty(t, attachments2[0].Actions)
+ require.NotEmpty(t, attachments2[0].Actions[0].Id)
+
+ err = th.App.DoPostAction(post.Id, "notavalidid", th.BasicUser.Id, "")
require.NotNil(t, err)
assert.Equal(t, http.StatusNotFound, err.StatusCode)
- err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id)
+ err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "")
+ require.Nil(t, err)
+
+ err = th.App.DoPostAction(post2.Id, attachments2[0].Actions[0].Id, th.BasicUser.Id, "selected")
require.Nil(t, err)
}
diff --git a/model/post.go b/model/post.go
index 635493c9d..82cc0a0e9 100644
--- a/model/post.go
+++ b/model/post.go
@@ -50,6 +50,8 @@ const (
PROPS_ADD_CHANNEL_MEMBER = "add_channel_member"
POST_PROPS_ADDED_USER_ID = "addedUserId"
POST_PROPS_DELETE_BY = "deleteBy"
+ POST_ACTION_TYPE_BUTTON = "button"
+ POST_ACTION_TYPE_SELECT = "select"
)
type Post struct {
@@ -108,21 +110,35 @@ type PostForIndexing struct {
ParentCreateAt *int64 `json:"parent_create_at"`
}
+type DoPostActionRequest struct {
+ SelectedOption string `json:"selected_option"`
+}
+
type PostAction struct {
Id string `json:"id"`
Name string `json:"name"`
+ Type string `json:"type"`
+ DataSource string `json:"data_source"`
+ Options []*PostActionOptions `json:"options"`
Integration *PostActionIntegration `json:"integration,omitempty"`
}
+type PostActionOptions struct {
+ Text string `json:"text"`
+ Value string `json:"value"`
+}
+
type PostActionIntegration struct {
URL string `json:"url,omitempty"`
Context StringInterface `json:"context,omitempty"`
}
type PostActionIntegrationRequest struct {
- UserId string `json:"user_id"`
- PostId string `json:"post_id"`
- Context StringInterface `json:"context,omitempty"`
+ UserId string `json:"user_id"`
+ PostId string `json:"post_id"`
+ Type string `json:"type"`
+ DataSource string `json:"data_source"`
+ Context StringInterface `json:"context,omitempty"`
}
type PostActionIntegrationResponse struct {
@@ -455,6 +471,12 @@ func (o *PostEphemeral) ToUnsanitizedJson() string {
return string(b)
}
+func DoPostActionRequestFromJson(data io.Reader) *DoPostActionRequest {
+ var o *DoPostActionRequest
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
+
// RewriteImageURLs takes a message and returns a copy that has all of the image URLs replaced
// according to the function f. For each image URL, f will be invoked, and the resulting markdown
// will contain the URL returned by that invocation instead.