Support for interactive menus in message attachments (#9285)
Этот коммит содержится в:
коммит произвёл
George Goldberg
родитель
9599f1a52f
Коммит
4de2e5206e
@@ -513,7 +513,12 @@ func doPostAction(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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
|
c.Err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
pchan := a.Srv.Store.Post().GetSingle(postId)
|
||||||
|
|
||||||
var post *model.Post
|
var post *model.Post
|
||||||
@@ -870,9 +870,15 @@ func (a *App) DoPostAction(postId string, actionId string, userId string) *model
|
|||||||
request := &model.PostActionIntegrationRequest{
|
request := &model.PostActionIntegrationRequest{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
PostId: postId,
|
PostId: postId,
|
||||||
|
Type: action.Type,
|
||||||
Context: action.Integration.Context,
|
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, _ := http.NewRequest("POST", action.Integration.URL, strings.NewReader(request.ToJson()))
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Accept", "application/json")
|
req.Header.Set("Accept", "application/json")
|
||||||
|
|||||||
@@ -134,6 +134,12 @@ func TestPostAction(t *testing.T) {
|
|||||||
err := json.NewDecoder(r.Body).Decode(&request)
|
err := json.NewDecoder(r.Body).Decode(&request)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, request.UserId, th.BasicUser.Id)
|
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.Equal(t, "foo", request.Context["s"])
|
||||||
assert.EqualValues(t, 3, request.Context["n"])
|
assert.EqualValues(t, 3, request.Context["n"])
|
||||||
fmt.Fprintf(w, `{"update": {"message": "updated"}, "ephemeral_text": "foo"}`)
|
fmt.Fprintf(w, `{"update": {"message": "updated"}, "ephemeral_text": "foo"}`)
|
||||||
@@ -158,7 +164,9 @@ func TestPostAction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
URL: ts.URL,
|
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)
|
||||||
require.NotEmpty(t, attachments[0].Actions[0].Id)
|
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)
|
require.NotNil(t, err)
|
||||||
assert.Equal(t, http.StatusNotFound, err.StatusCode)
|
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)
|
require.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ const (
|
|||||||
PROPS_ADD_CHANNEL_MEMBER = "add_channel_member"
|
PROPS_ADD_CHANNEL_MEMBER = "add_channel_member"
|
||||||
POST_PROPS_ADDED_USER_ID = "addedUserId"
|
POST_PROPS_ADDED_USER_ID = "addedUserId"
|
||||||
POST_PROPS_DELETE_BY = "deleteBy"
|
POST_PROPS_DELETE_BY = "deleteBy"
|
||||||
|
POST_ACTION_TYPE_BUTTON = "button"
|
||||||
|
POST_ACTION_TYPE_SELECT = "select"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Post struct {
|
type Post struct {
|
||||||
@@ -108,21 +110,35 @@ type PostForIndexing struct {
|
|||||||
ParentCreateAt *int64 `json:"parent_create_at"`
|
ParentCreateAt *int64 `json:"parent_create_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DoPostActionRequest struct {
|
||||||
|
SelectedOption string `json:"selected_option"`
|
||||||
|
}
|
||||||
|
|
||||||
type PostAction struct {
|
type PostAction struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
DataSource string `json:"data_source"`
|
||||||
|
Options []*PostActionOptions `json:"options"`
|
||||||
Integration *PostActionIntegration `json:"integration,omitempty"`
|
Integration *PostActionIntegration `json:"integration,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PostActionOptions struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
type PostActionIntegration struct {
|
type PostActionIntegration struct {
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
Context StringInterface `json:"context,omitempty"`
|
Context StringInterface `json:"context,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostActionIntegrationRequest struct {
|
type PostActionIntegrationRequest struct {
|
||||||
UserId string `json:"user_id"`
|
UserId string `json:"user_id"`
|
||||||
PostId string `json:"post_id"`
|
PostId string `json:"post_id"`
|
||||||
Context StringInterface `json:"context,omitempty"`
|
Type string `json:"type"`
|
||||||
|
DataSource string `json:"data_source"`
|
||||||
|
Context StringInterface `json:"context,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostActionIntegrationResponse struct {
|
type PostActionIntegrationResponse struct {
|
||||||
@@ -455,6 +471,12 @@ func (o *PostEphemeral) ToUnsanitizedJson() string {
|
|||||||
return string(b)
|
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
|
// 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
|
// 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.
|
// will contain the URL returned by that invocation instead.
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user