Adds Remote Cluster related API endpoints (#27432)
* Adds Remote Cluster related API endpoints
New endpoints for the following routes are added:
- Get Remote Clusters at `GET /api/v4/remotecluster`
- Create Remote Cluster at `POST /api/v4/remotecluster`
- Accept Remote Cluster invite at `POST
/api/v4/remotecluster/accept_invite`
- Generate Remote Cluster invite at `POST
/api/v4/remotecluster/{remote_id}/generate_invite`
- Get Remote Cluster at `GET /api/v4/remotecluster/{remote_id}`
- Patch Remote Cluster at `PATCH /api/v4/remotecluster/{remote_id}`
- Delete Remote Cluster at `DELETE /api/v4/remotecluster/{remote_id}`
These endpoints are planned to be used from the system console, and
gated through the `manage_secure_connections` permission.
* Update server/channels/api4/remote_cluster_test.go
Co-authored-by: Doug Lauder <wiggin77@warpmail.net>
* Fix AppError names
---------
Co-authored-by: Doug Lauder <wiggin77@warpmail.net>
Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
cc5e87ae24
Коммит
809ad4f76d
@@ -568,6 +568,10 @@ func (c *Client4) exportRoute(name string) string {
|
||||
return fmt.Sprintf(c.exportsRoute()+"/%v", name)
|
||||
}
|
||||
|
||||
func (c *Client4) remoteClusterRoute() string {
|
||||
return "/remotecluster"
|
||||
}
|
||||
|
||||
func (c *Client4) sharedChannelsRoute() string {
|
||||
return "/sharedchannels"
|
||||
}
|
||||
@@ -8698,6 +8702,154 @@ func (c *Client4) GetRemoteClusterInfo(ctx context.Context, remoteID string) (Re
|
||||
return rci, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) GetRemoteClusters(ctx context.Context, page, perPage int, filter RemoteClusterQueryFilter) ([]*RemoteCluster, *Response, error) {
|
||||
v := url.Values{}
|
||||
if page != 0 {
|
||||
v.Set("page", fmt.Sprintf("%d", page))
|
||||
}
|
||||
if perPage != 0 {
|
||||
v.Set("per_page", fmt.Sprintf("%d", perPage))
|
||||
}
|
||||
if filter.ExcludeOffline {
|
||||
v.Set("exclude_offline", "true")
|
||||
}
|
||||
if filter.InChannel != "" {
|
||||
v.Set("in_channel", filter.InChannel)
|
||||
}
|
||||
if filter.NotInChannel != "" {
|
||||
v.Set("not_in_channel", filter.NotInChannel)
|
||||
}
|
||||
if filter.Topic != "" {
|
||||
v.Set("topic", filter.Topic)
|
||||
}
|
||||
if filter.CreatorId != "" {
|
||||
v.Set("creator_id", filter.CreatorId)
|
||||
}
|
||||
if filter.OnlyConfirmed {
|
||||
v.Set("only_confirmed", "true")
|
||||
}
|
||||
if filter.PluginID != "" {
|
||||
v.Set("plugin_id", filter.PluginID)
|
||||
}
|
||||
if filter.OnlyPlugins {
|
||||
v.Set("only_plugins", "true")
|
||||
}
|
||||
if filter.ExcludePlugins {
|
||||
v.Set("exclude_plugins", "true")
|
||||
}
|
||||
url := c.remoteClusterRoute()
|
||||
if len(v) > 0 {
|
||||
url += "?" + v.Encode()
|
||||
}
|
||||
|
||||
r, err := c.DoAPIGet(ctx, url, "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var rcs []*RemoteCluster
|
||||
json.NewDecoder(r.Body).Decode(&rcs)
|
||||
|
||||
return rcs, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) CreateRemoteCluster(ctx context.Context, rcWithPassword *RemoteClusterWithPassword) (*RemoteClusterWithInvite, *Response, error) {
|
||||
rcJSON, err := json.Marshal(rcWithPassword)
|
||||
if err != nil {
|
||||
return nil, nil, NewAppError("CreateRemoteCluster", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
r, err := c.DoAPIPost(ctx, c.remoteClusterRoute(), string(rcJSON))
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var rcWithInvite RemoteClusterWithInvite
|
||||
if err := json.NewDecoder(r.Body).Decode(&rcWithInvite); err != nil {
|
||||
return nil, nil, NewAppError("CreateRemoteCluster", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return &rcWithInvite, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) RemoteClusterAcceptInvite(ctx context.Context, rcAcceptInvite *RemoteClusterAcceptInvite) (*RemoteCluster, *Response, error) {
|
||||
rcAcceptInviteJSON, err := json.Marshal(rcAcceptInvite)
|
||||
if err != nil {
|
||||
return nil, nil, NewAppError("RemoteClusterAcceptInvite", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/accept_invite", c.remoteClusterRoute())
|
||||
r, err := c.DoAPIPost(ctx, url, string(rcAcceptInviteJSON))
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var rc RemoteCluster
|
||||
if err := json.NewDecoder(r.Body).Decode(&rc); err != nil {
|
||||
return nil, nil, NewAppError("RemoteClusterAcceptInvite", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return &rc, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) GenerateRemoteClusterInvite(ctx context.Context, remoteClusterId, password string) (string, *Response, error) {
|
||||
url := fmt.Sprintf("%s/%s/generate_invite", c.remoteClusterRoute(), remoteClusterId)
|
||||
r, err := c.DoAPIPost(ctx, url, MapToJSON(map[string]string{"password": password}))
|
||||
if err != nil {
|
||||
return "", BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
b, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return "", nil, NewAppError("GenerateRemoteClusterInvite", "api.read_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return string(b), BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) GetRemoteCluster(ctx context.Context, remoteClusterId string) (*RemoteCluster, *Response, error) {
|
||||
r, err := c.DoAPIGet(ctx, fmt.Sprintf("%s/%s", c.remoteClusterRoute(), remoteClusterId), "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var rc *RemoteCluster
|
||||
json.NewDecoder(r.Body).Decode(&rc)
|
||||
|
||||
return rc, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) PatchRemoteCluster(ctx context.Context, remoteClusterId string, patch *RemoteClusterPatch) (*RemoteCluster, *Response, error) {
|
||||
patchJSON, err := json.Marshal(patch)
|
||||
if err != nil {
|
||||
return nil, nil, NewAppError("PatchRemoteCluster", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/%s", c.remoteClusterRoute(), remoteClusterId)
|
||||
r, err := c.DoAPIPatchBytes(ctx, url, patchJSON)
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var rc RemoteCluster
|
||||
if err := json.NewDecoder(r.Body).Decode(&rc); err != nil {
|
||||
return nil, nil, NewAppError("PatchRemoteCluster", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return &rc, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) DeleteRemoteCluster(ctx context.Context, remoteClusterId string) (*Response, error) {
|
||||
r, err := c.DoAPIDelete(ctx, fmt.Sprintf("%s/%s", c.remoteClusterRoute(), remoteClusterId))
|
||||
if err != nil {
|
||||
return BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
return BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) GetAncillaryPermissions(ctx context.Context, subsectionPermissions []string) ([]string, *Response, error) {
|
||||
var returnedPermissions []string
|
||||
url := fmt.Sprintf("%s/ancillary?subsection_permissions=%s", c.permissionsRoute(), strings.Join(subsectionPermissions, ","))
|
||||
|
||||
@@ -126,6 +126,37 @@ func (rc *RemoteCluster) IsValid() *AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *RemoteCluster) Sanitize() {
|
||||
rc.Token = ""
|
||||
rc.RemoteToken = ""
|
||||
}
|
||||
|
||||
type RemoteClusterPatch struct {
|
||||
DisplayName *string `json:"display_name"`
|
||||
}
|
||||
|
||||
func (rcp *RemoteClusterPatch) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"display_name": rcp.DisplayName,
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *RemoteCluster) Patch(patch *RemoteClusterPatch) {
|
||||
if patch.DisplayName != nil {
|
||||
rc.DisplayName = *patch.DisplayName
|
||||
}
|
||||
}
|
||||
|
||||
type RemoteClusterWithPassword struct {
|
||||
*RemoteCluster
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type RemoteClusterWithInvite struct {
|
||||
RemoteCluster *RemoteCluster `json:"remote_cluster"`
|
||||
Invite string `json:"invite"`
|
||||
}
|
||||
|
||||
func newIDFromBytes(b []byte) string {
|
||||
hash := md5.New()
|
||||
_, _ = hash.Write(b)
|
||||
@@ -393,6 +424,13 @@ func (rci *RemoteClusterInvite) Decrypt(encrypted []byte, password string) error
|
||||
return json.Unmarshal(plain, &rci)
|
||||
}
|
||||
|
||||
type RemoteClusterAcceptInvite struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Invite string `json:"invite"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// RemoteClusterQueryFilter provides filter criteria for RemoteClusterStore.GetAll
|
||||
type RemoteClusterQueryFilter struct {
|
||||
ExcludeOffline bool
|
||||
@@ -403,5 +441,6 @@ type RemoteClusterQueryFilter struct {
|
||||
OnlyConfirmed bool
|
||||
PluginID string
|
||||
OnlyPlugins bool
|
||||
ExcludePlugins bool
|
||||
RequireOptions Bitmask
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user