Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Autolink plugin into an App, and introduce autolink edit modal #159

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ ifneq ($(wildcard $(ASSETS_DIR)/.),)
cp -r $(ASSETS_DIR) dist/$(PLUGIN_ID)/
endif
ifneq ($(HAS_PUBLIC),)
cp -r public/ dist/$(PLUGIN_ID)/
cp -r public/ dist/$(PLUGIN_ID)/public
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's stick with the starter template

Suggested change
cp -r public/ dist/$(PLUGIN_ID)/public
cp -r public dist/$(PLUGIN_ID)/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hanzei The code was originally putting the icon.png file into the dist folder, and not in the dist/public folder. This was how I was able to get the icon in the right folder.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are using a mac, right? Could you please test my code?

endif
ifneq ($(HAS_SERVER),)
mkdir -p dist/$(PLUGIN_ID)/server/dist;
Expand Down
25 changes: 20 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
module github.com/mattermost/mattermost-plugin-autolink

go 1.12
go 1.16

require (
github.com/gorilla/mux v1.7.4
github.com/mattermost/mattermost-server/v5 v5.28.1
github.com/mholt/archiver/v3 v3.3.0
github.com/go-sql-driver/mysql v1.6.0 // indirect
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this tidy-ed?

github.com/gorilla/mux v1.8.0
github.com/hashicorp/go-hclog v0.16.0 // indirect
github.com/hashicorp/go-plugin v1.4.1 // indirect
github.com/klauspost/compress v1.12.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/lib/pq v1.10.1 // indirect
github.com/mattermost/mattermost-plugin-apps v0.4.1-0.20210424000506-01c57929151d
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210413123336-5f2c26dbda0a
github.com/mholt/archiver/v3 v3.5.0
github.com/pelletier/go-toml v1.9.0 // indirect
github.com/pierrec/lz4/v4 v4.1.6 // indirect
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.6.1
github.com/rs/xid v1.3.0 // indirect
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 // indirect
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
google.golang.org/genproto v0.0.0-20210423144448-3a41ef94ed2b // indirect
google.golang.org/grpc v1.37.0 // indirect
)
865 changes: 730 additions & 135 deletions go.sum

Large diffs are not rendered by default.

Binary file added public/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 1 addition & 4 deletions server/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,19 @@ type Handler struct {
authorization Authorization
}

func NewHandler(store Store, authorization Authorization) *Handler {
func RegisterHandler(root *mux.Router, store Store, authorization Authorization) {
h := &Handler{
store: store,
authorization: authorization,
}

root := mux.NewRouter()
api := root.PathPrefix("/api/v1").Subrouter()
api.Use(h.adminOrPluginRequired)
api.HandleFunc("/link", h.setLink).Methods("POST")

api.Handle("{anything:.*}", http.NotFoundHandler())

h.root = root

return h
}

func (h *Handler) handleError(w http.ResponseWriter, err error) {
Expand Down
7 changes: 5 additions & 2 deletions server/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http/httptest"
"testing"

"github.com/gorilla/mux"
"github.com/stretchr/testify/require"

"github.com/mattermost/mattermost-plugin-autolink/server/autolink"
Expand Down Expand Up @@ -138,7 +139,9 @@ func TestSetLink(t *testing.T) {
var saved []autolink.Autolink
var saveCalled bool

h := NewHandler(
root := mux.NewRouter()
RegisterHandler(
root,
&linkStore{
prev: tc.prevLinks,
saveCalled: &saveCalled,
Expand All @@ -161,7 +164,7 @@ func TestSetLink(t *testing.T) {
r.Header.Set("Mattermost-Plugin-ID", "testfrom")
r.Header.Set("Mattermost-User-ID", "testuser")

h.ServeHTTP(w, r)
root.ServeHTTP(w, r)
require.Equal(t, tc.expectStatus, w.Code)
require.Equal(t, tc.expectSaveCalled, saveCalled)
require.Equal(t, tc.expectSaved, saved)
Expand Down
172 changes: 172 additions & 0 deletions server/autolinkapp/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package autolinkapp

import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"

"github.com/gorilla/mux"
"github.com/pkg/errors"

"github.com/mattermost/mattermost-plugin-apps/apps"
"github.com/mattermost/mattermost-plugin-apps/server/utils/httputils"
"github.com/mattermost/mattermost-plugin-autolink/server/autolink"
)

type Store interface {
GetLinks() []autolink.Autolink
SaveLinks([]autolink.Autolink) error
}

type Service interface {
GetPluginURL() string
IsAuthorizedAdmin(userID string) (bool, error)
}

type app struct {
store Store
service Service
}

const (
appID = "com.mattermost.autolink"
appDisplayName = "Autolink"

autolinkCommand = "autolink-app"

appRoute = "/app/v1"
manifestRoute = "/manifest"
bindingsRoute = "/bindings"
iconRoute = "/public/icon.png" // Served from the plugin's public folder

editLinksRoute = "/edit-links"
editLinksCommandRoute = "/edit-links-command"
)

func RegisterHandler(root *mux.Router, store Store, service Service) {
a := &app{
store: store,
service: service,
}

appRouter := root.PathPrefix(appRoute).Subrouter()
appRouter.HandleFunc(manifestRoute, a.handleManifest).Methods(http.MethodGet)

adminRoutes := appRouter.PathPrefix("").Subrouter()
adminRoutes.Use(a.adminRequired)

adminRoutes.HandleFunc(bindingsRoute, a.handleBindings).Methods(http.MethodPost)
adminRoutes.HandleFunc(editLinksRoute+"/form", a.handleFormFetch).Methods(http.MethodPost)
adminRoutes.HandleFunc(editLinksRoute+"/submit", a.handleFormSubmit).Methods(http.MethodPost)
adminRoutes.HandleFunc(editLinksCommandRoute+"/submit", a.handleCommandSubmit).Methods(http.MethodPost)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really /edit-links-command/submit/submit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm that was happening before and I thought I fixed that. Not sure how/why it's working

}

func (a *app) handleManifest(w http.ResponseWriter, r *http.Request) {
pluginURL := a.service.GetPluginURL()
rootURL := pluginURL + appRoute

manifest := apps.Manifest{
AppID: appID,
DisplayName: appDisplayName,
AppType: apps.AppTypeHTTP,
HTTPRootURL: rootURL,
RequestedPermissions: apps.Permissions{
apps.PermissionActAsBot,
},
RequestedLocations: apps.Locations{
apps.LocationCommand,
},
}

httputils.WriteJSON(w, manifest)
}

func (a *app) handleBindings(w http.ResponseWriter, r *http.Request) {
call := &apps.CallRequest{}
err := json.NewDecoder(r.Body).Decode(call)
if err != nil {
httputils.WriteJSON(w, apps.NewErrorCallResponse(errors.Wrap(err, "failed to decode call request body")))
return
}

icon := a.service.GetPluginURL() + iconRoute
resp := &apps.CallResponse{
Type: apps.CallResponseTypeOK,
Data: []apps.Binding{
{
Location: apps.LocationCommand,
Bindings: []*apps.Binding{
{
Icon: icon,
Label: autolinkCommand,
Location: autolinkCommand,
Description: appDisplayName,
Hint: "[edit]",
Bindings: []*apps.Binding{
{
Location: "edit",
Label: "edit",
Form: &apps.Form{Fields: []*apps.Field{}},
hanzei marked this conversation as resolved.
Show resolved Hide resolved
Call: &apps.Call{
Path: editLinksCommandRoute,
},
},
},
},
},
},
},
}

httputils.WriteJSON(w, resp)
}

func (a *app) handleCommandSubmit(w http.ResponseWriter, r *http.Request) {
resp := a.getEmptyFormResponse()
httputils.WriteJSON(w, resp)
}

func (a *app) handleIcon(w http.ResponseWriter, r *http.Request) {
resp := a.getEmptyFormResponse()
httputils.WriteJSON(w, resp)
}
Comment on lines +130 to +133
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused?


func (a *app) adminRequired(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
httputils.WriteJSON(w, apps.NewErrorCallResponse(errors.Wrap(err, "failed to read call request body")))
return
}

r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewBuffer(b))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm reading from the body in this middleware, and want the http handlers to be able to read from the body as well. I don't like this solution

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


call := &apps.CallRequest{}
err = json.Unmarshal(b, call)
if err != nil {
httputils.WriteJSON(w, apps.NewErrorCallResponse(errors.Wrap(err, "failed to decode call request body")))
return
}

userID := call.Context.ActingUserID
if userID == "" {
httputils.WriteJSON(w, apps.NewErrorCallResponse(errors.New("no acting user id provided")))
return
}

authorized, err := a.service.IsAuthorizedAdmin(userID)
if err != nil {
httputils.WriteJSON(w, apps.NewErrorCallResponse(errors.Wrap(err, "not authorized")))
return
}

if !authorized {
httputils.WriteJSON(w, apps.NewErrorCallResponse(errors.New("not authorized")))
return
}

next.ServeHTTP(w, r)
})
}
Loading