mattermost-community-enterp.../platform/services/marketplace/client.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

131 lines
3.2 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package marketplace
import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/httpservice"
)
// Client is the programmatic interface to the marketplace server API.
type Client struct {
address string
httpClient *http.Client
}
// NewClient creates a client to the marketplace server at the given address.
func NewClient(address string, httpService httpservice.HTTPService) (*Client, error) {
var httpClient *http.Client
addressURL, err := url.Parse(address)
if err != nil {
return nil, errors.Wrap(err, "failed to parse marketplace address")
}
if addressURL.Hostname() == "localhost" || addressURL.Hostname() == "127.0.0.1" {
httpClient = httpService.MakeClient(true)
} else {
httpClient = httpService.MakeClient(false)
}
return &Client{
address: address,
httpClient: httpClient,
}, nil
}
// GetPlugins fetches the list of plugins from the configured server.
func (c *Client) GetPlugins(request *model.MarketplacePluginFilter) ([]*model.BaseMarketplacePlugin, error) {
u, err := url.Parse(c.buildURL("/api/v1/plugins"))
if err != nil {
return nil, err
}
request.ApplyToURL(u)
resp, err := c.doGet(u.String())
if err != nil {
return nil, err
}
defer closeBody(resp)
switch resp.StatusCode {
case http.StatusOK:
return model.BaseMarketplacePluginsFromReader(resp.Body)
default:
return nil, errors.Errorf("failed with status code %d", resp.StatusCode)
}
}
func (c *Client) GetPlugin(filter *model.MarketplacePluginFilter, pluginVersion string) (*model.BaseMarketplacePlugin, error) {
filter.ReturnAllVersions = true
if filter.PluginId == "" {
return nil, errors.New("missing pluginID")
}
if pluginVersion == "" {
return nil, errors.New("missing pluginVersion")
}
plugins, err := c.GetPlugins(filter)
if err != nil {
return nil, err
}
for _, plugin := range plugins {
if plugin.Manifest.Version == pluginVersion {
return plugin, nil
}
}
return nil, errors.New("plugin not found")
}
func (c *Client) GetLatestPlugin(filter *model.MarketplacePluginFilter) (*model.BaseMarketplacePlugin, error) {
filter.ReturnAllVersions = false
if filter.PluginId == "" {
return nil, errors.New("no pluginID provided")
}
plugins, err := c.GetPlugins(filter)
if err != nil {
return nil, err
}
if len(plugins) == 0 {
return nil, errors.New("plugin not found")
}
if len(plugins) > 1 {
return nil, errors.Errorf("unexpectedly more then one plugin was returned from the marketplace")
}
return plugins[0], nil
}
// closeBody ensures the Body of an http.Response is properly closed.
func closeBody(r *http.Response) {
if r.Body != nil {
_, _ = io.Copy(io.Discard, r.Body)
_ = r.Body.Close()
}
}
func (c *Client) buildURL(urlPath string, args ...any) string {
if len(args) > 0 {
urlPath = fmt.Sprintf(urlPath, args...)
}
return fmt.Sprintf("%s/%s", strings.TrimRight(c.address, "/"), strings.TrimLeft(urlPath, "/"))
}
func (c *Client) doGet(u string) (*http.Response, error) {
return c.httpClient.Get(u)
}