Skip to content

Commit

Permalink
Added the feature to create pages from mm for both cloud and server
Browse files Browse the repository at this point in the history
  • Loading branch information
Kshitij Katiyar authored and ayusht2810 committed Feb 12, 2024
1 parent 789389f commit 56bfd5a
Show file tree
Hide file tree
Showing 24 changed files with 1,040 additions and 6 deletions.
4 changes: 4 additions & 0 deletions server/client.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package main

import "github.com/mattermost/mattermost-plugin-confluence/server/serializer"

// Client is the combined interface for all upstream APIs and convenience methods.
type Client interface {
RESTService
Expand All @@ -13,4 +15,6 @@ type RESTService interface {
GetSpaceData(string) (*SpaceResponse, error)
GetUserGroups(*Connection) ([]*UserGroup, error)
GetPageData(pageID int) (*PageResponse, error)
GetSpacesForConfluenceURL() ([]*SpaceForConfluenceURL, error)
CreatePage(string, *serializer.PageDetails) (*CreatePageResponse, error)
}
45 changes: 44 additions & 1 deletion server/client_cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import (

const (
PathAccessibleResources = "/oauth/token/accessible-resources"
PathGetUserGroupsForCloud = "rest/api/user/memberof?accountId=%s"
PathGetUserGroupsForCloud = "/rest/api/user/memberof?accountId=%s"
PathGetSpacesForCloud = "/rest/api/space?limit=100"
PathCreatePageForCloud = "/rest/api/content"
)

type confluenceCloudClient struct {
Expand Down Expand Up @@ -157,3 +159,44 @@ func (ccc *confluenceCloudClient) GetUserGroups(connection *Connection) ([]*User

return userGroups.Groups, nil
}

func (ccc *confluenceCloudClient) GetSpacesForConfluenceURL() ([]*SpaceForConfluenceURL, error) {
spacesForConfluenceURL := SpacesForConfluenceURL{}
url, err := utils.GetEndpointURL(ccc.URL, PathGetSpacesForCloud)

if err != nil {
return nil, errors.Wrap(err, "confluence GetSpacesForConfluenceURL")
}
_, err = utils.CallJSON(ccc.URL, http.MethodGet, url, nil, &spacesForConfluenceURL, ccc.HTTPClient)
if err != nil {
return nil, errors.Wrap(err, "confluence GetSpacesForConfluenceURL")
}
return spacesForConfluenceURL.Spaces, nil
}

func (ccc *confluenceCloudClient) CreatePage(spaceKey string, pageDetails *serializer.PageDetails) (*CreatePageResponse, error) {
requestBody := &CreatePageRequestBody{
Title: pageDetails.Title,
Type: "page",
Space: SpaceForPageCreate{
Key: spaceKey,
},
Body: BodyForPageCreate{
Storage: Storage{
Value: pageDetails.Description,
Representation: "storage",
},
},
}

createPageResponse := &CreatePageResponse{}
url, err := utils.GetEndpointURL(ccc.URL, PathCreatePageForCloud)
if err != nil {
return nil, errors.Wrap(err, "confluence CreatePage")
}
_, err = utils.CallJSON(ccc.URL, http.MethodPost, url, requestBody, createPageResponse, ccc.HTTPClient)
if err != nil {
return nil, errors.Wrap(err, "confluence CreatePage")
}
return createPageResponse, nil
}
70 changes: 70 additions & 0 deletions server/client_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const (
PathDeleteWebhook = "/rest/api/webhooks/%s"
PathGetUserGroupsForServer = "/rest/api/user/memberof?username=%s"
PathAdminData = "/rest/api/audit/retention"
PathGetSpacesForServer = "/rest/api/space?limit=100"
PathCreatePage = "/rest/api/content"
)

const (
Expand All @@ -45,6 +47,35 @@ type CreateWebhookRequestBody struct {
Active string `json:"active"`
Configuration *WebhookConfiguration `json:"configuration"`
}
type SpaceForPageCreate struct {
Key string `json:"key"`
}

type Storage struct {
Value string `json:"value"`
Representation string `json:"representation"`
}

type BodyForPageCreate struct {
Storage Storage `json:"storage"`
}

type CreatePageRequestBody struct {
Title string `json:"title"`
Type string `json:"type"`
Space SpaceForPageCreate `json:"space"`
Body BodyForPageCreate `json:"body"`
}

type CreatePageResponse struct {
Space SpaceResponse `json:"space"`
Links LinksForPageCreate `json:"_links"`
}

type LinksForPageCreate struct {
Self string `json:"webui"`
BaseURL string `json:"base"`
}

type SpaceResponse struct {
ID int64 `json:"id"`
Expand Down Expand Up @@ -254,3 +285,42 @@ func (csc *confluenceServerClient) GetUserGroups(connection *Connection) ([]*Use

return userGroups.Groups, nil
}

func (csc *confluenceServerClient) GetSpacesForConfluenceURL() ([]*SpaceForConfluenceURL, error) {
spacesForConfluenceURL := SpacesForConfluenceURL{}
url, err := utils.GetEndpointURL(csc.URL, PathGetSpacesForServer)
if err != nil {
return nil, errors.Wrap(err, "confluence GetSpacesForConfluenceURL")
}
_, err = utils.CallJSON(csc.URL, http.MethodGet, url, nil, &spacesForConfluenceURL, csc.HTTPClient)
if err != nil {
return nil, errors.Wrap(err, "confluence GetSpacesForConfluenceURL")
}
return spacesForConfluenceURL.Spaces, nil
}

func (csc *confluenceServerClient) CreatePage(spaceKey string, pageDetails *serializer.PageDetails) (*CreatePageResponse, error) {
requestBody := &CreatePageRequestBody{
Title: pageDetails.Title,
Type: "page",
Space: SpaceForPageCreate{
Key: spaceKey,
},
Body: BodyForPageCreate{
Storage: Storage{
Value: pageDetails.Description,
Representation: "storage",
},
},
}
createPageResponse := &CreatePageResponse{}
url, err := utils.GetEndpointURL(csc.URL, PathCreatePage)
if err != nil {
return nil, errors.Wrap(err, "confluence CreatePage")
}
_, err = utils.CallJSON(csc.URL, http.MethodPost, url, requestBody, createPageResponse, csc.HTTPClient)
if err != nil {
return nil, errors.Wrap(err, "confluence CreatePage")
}
return createPageResponse, nil
}
69 changes: 69 additions & 0 deletions server/create_page.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"fmt"
"io/ioutil"
"net/http"

"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/v6/model"

"github.com/mattermost/mattermost-plugin-confluence/server/config"
"github.com/mattermost/mattermost-plugin-confluence/server/serializer"
"github.com/mattermost/mattermost-plugin-confluence/server/utils/types"
)

const (
pageCreateSuccess = "You created a page [%s](%s) in space [%s](%s)"
)

func (p *Plugin) handleCreatePage(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
spaceKey := params["spaceKey"]
channelID := params["channelID"]
userID := r.Header.Get(config.HeaderMattermostUserID)

instance, err := p.getInstanceFromURL(r.URL.Path)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

conn, err := p.userStore.LoadConnection(types.ID(instance.GetURL()), types.ID(userID))
if err != nil {
p.LogAndRespondError(w, http.StatusInternalServerError, "error in loading connection.", err)
return
}

client, err := instance.GetClient(conn)
if err != nil {
p.LogAndRespondError(w, http.StatusInternalServerError, "not able to get Client.", err)
return
}

body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

pageDetails, err := serializer.PageDetailsFromJSON(body)
if err != nil {
p.LogAndRespondError(w, http.StatusInternalServerError, "error decoding request body for page details.", err)
return
}

createPageResponse, err := client.CreatePage(spaceKey, pageDetails)
if err != nil {
p.LogAndRespondError(w, http.StatusInternalServerError, "not able to create page.", err)
return
}

post := &model.Post{
UserId: p.conf.botUserID,
ChannelId: channelID,
Message: fmt.Sprintf(pageCreateSuccess, pageDetails.Title, fmt.Sprintf("%s%s", createPageResponse.Links.BaseURL, createPageResponse.Links.Self), spaceKey, fmt.Sprintf("%s%s", createPageResponse.Links.BaseURL, createPageResponse.Space.Links.Self)),
}
_ = p.API.SendEphemeralPost(userID, post)
ReturnStatusOK(w)
}
40 changes: 40 additions & 0 deletions server/get_spaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"encoding/json"
"net/http"

"github.com/mattermost/mattermost-plugin-confluence/server/config"
"github.com/mattermost/mattermost-plugin-confluence/server/utils/types"
)

func (p *Plugin) handleGetSpacesForConfluenceURL(w http.ResponseWriter, r *http.Request) {
userID := r.Header.Get(config.HeaderMattermostUserID)

instance, err := p.getInstanceFromURL(r.URL.Path)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

conn, err := p.userStore.LoadConnection(types.ID(instance.GetURL()), types.ID(userID))
if err != nil {
p.LogAndRespondError(w, http.StatusInternalServerError, "error in loading connection.", err)
return
}

client, err := instance.GetClient(conn)
if err != nil {
p.LogAndRespondError(w, http.StatusInternalServerError, "not able to get Client.", err)
return
}

spaces, err := client.GetSpacesForConfluenceURL()
if err != nil {
p.LogAndRespondError(w, http.StatusInternalServerError, "not able to get Spaces for confluence url.", err)
return
}
b, _ := json.Marshal(spaces)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(string(b)))
}
2 changes: 2 additions & 0 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ func (p *Plugin) InitAPI() *mux.Router {
apiRouter.HandleFunc(routeUserConnect, p.httpOAuth2Connect).Methods(http.MethodGet)
apiRouter.HandleFunc(routeUserComplete, p.httpOAuth2Complete).Methods(http.MethodGet)

apiRouter.HandleFunc("/spaces", p.handleGetSpacesForConfluenceURL).Methods(http.MethodGet)
apiRouter.HandleFunc("/{channelID:[A-Za-z0-9]+}/spaceKey/{spaceKey:.+}/createpage", p.handleCreatePage).Methods(http.MethodPost)
apiRouter.HandleFunc("/{channelID:[A-Za-z0-9]+}/subscription/{type:[A-Za-z_]+}", p.handleSaveSubscription).Methods(http.MethodPost)
apiRouter.HandleFunc("/{channelID:[A-Za-z0-9]+}/subscription/{type:[A-Za-z_]+}/{oldSubscriptionType:[A-Za-z_]+}", p.handleEditChannelSubscription).Methods(http.MethodPut)
apiRouter.HandleFunc("/server/webhook/{userID:.+}", p.handleConfluenceServerWebhook).Methods(http.MethodPost)
Expand Down
16 changes: 16 additions & 0 deletions server/serializer/create_page.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package serializer

import "encoding/json"

type PageDetails struct {
Title string `json:"title"`
Description string `json:"description"`
}

func PageDetailsFromJSON(body []byte) (*PageDetails, error) {
var pd PageDetails
if err := json.Unmarshal(body, &pd); err != nil {
return nil, err
}
return &pd, nil
}
9 changes: 9 additions & 0 deletions server/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ type UserGroup struct {
Name string `json:"name"`
}

type SpacesForConfluenceURL struct {
Spaces []*SpaceForConfluenceURL `json:"results,omitempty"`
}

type SpaceForConfluenceURL struct {
Key string `json:"key"`
Name string `json:"name"`
}

type Connection struct {
ConfluenceUser
PluginVersion string
Expand Down
6 changes: 5 additions & 1 deletion webapp/src/actions/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
closeSubscriptionModal, openSubscriptionModal, saveChannelSubscription, editChannelSubscription, getChannelSubscription, getConnected,
closeSubscriptionModal, openSubscriptionModal, saveChannelSubscription, editChannelSubscription, getChannelSubscription, getConnected, openCreateConfluencePageModal, closeCreateConfluencePageModal, getSpacesForConfluenceURL, createPageForConfluence,
} from './subscription_modal';

export {
Expand All @@ -9,4 +9,8 @@ export {
editChannelSubscription,
getChannelSubscription,
getConnected,
openCreateConfluencePageModal,
closeCreateConfluencePageModal,
getSpacesForConfluenceURL,
createPageForConfluence,
};
58 changes: 58 additions & 0 deletions webapp/src/actions/subscription_modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,64 @@ export const closeSubscriptionModal = () => (dispatch) => {
});
};

export function openCreateConfluencePageModal(postID) {
return async (dispatch) => {
let data = null;
try {
data = await await Client.getPostDetails(postID);
} catch (error) {
return {error};
}
dispatch({
type: Constants.ACTION_TYPES.OPEN_CREATE_CONFLUENCE_PAGE_MODAL,
data,
});
return {data};
};
}

export const createPageForConfluence = (instanceID, channelID, spaceKey, pageDetials) => {
return async () => {
let data = null;
try {
data = await Client.createPage(instanceID, channelID, spaceKey, pageDetials);
} catch (error) {
return {
data,
error,
};
}

return {
data,
error: null,
};
};
};

export const closeCreateConfluencePageModal = () => (dispatch) => {
dispatch({
type: Constants.ACTION_TYPES.CLOSE_CREATE_CONFLUENCE_PAGE_MODAL,
});
};

export function getSpacesForConfluenceURL(instanceID) {
return async (dispatch) => {
let data = null;
try {
data = await Client.getSpacesForConfluenceURL(instanceID);
} catch (error) {
return {error};
}
dispatch({
type: Constants.ACTION_TYPES.RECEIVED_CONFLUENCE_INSTANCE,
data,
});

return {data};
};
}

export const getChannelSubscription = (channelID, alias, userID) => async (dispatch) => {
try {
const response = await Client.getChannelSubscription(channelID, alias);
Expand Down
Loading

0 comments on commit 56bfd5a

Please sign in to comment.