mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: expose application name via Appearance API (#9886)
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -67,8 +66,16 @@ func (api *API) fetchAppearanceConfig(ctx context.Context) (codersdk.AppearanceC
|
||||
}
|
||||
|
||||
var eg errgroup.Group
|
||||
var applicationName string
|
||||
var logoURL string
|
||||
var serviceBannerJSON string
|
||||
eg.Go(func() (err error) {
|
||||
applicationName, err = api.Database.GetApplicationName(ctx)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return xerrors.Errorf("get application name: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() (err error) {
|
||||
logoURL, err = api.Database.GetLogoURL(ctx)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
@@ -89,7 +96,8 @@ func (api *API) fetchAppearanceConfig(ctx context.Context) (codersdk.AppearanceC
|
||||
}
|
||||
|
||||
cfg := codersdk.AppearanceConfig{
|
||||
LogoURL: logoURL,
|
||||
ApplicationName: applicationName,
|
||||
LogoURL: logoURL,
|
||||
}
|
||||
if serviceBannerJSON != "" {
|
||||
err = json.Unmarshal([]byte(serviceBannerJSON), &cfg.ServiceBanner)
|
||||
@@ -111,7 +119,7 @@ func (api *API) fetchAppearanceConfig(ctx context.Context) (codersdk.AppearanceC
|
||||
|
||||
func validateHexColor(color string) error {
|
||||
if len(color) != 7 {
|
||||
return xerrors.New("expected 7 characters")
|
||||
return xerrors.New("expected # prefix and 6 characters")
|
||||
}
|
||||
if color[0] != '#' {
|
||||
return xerrors.New("no # prefix")
|
||||
@@ -147,7 +155,8 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) {
|
||||
if appearance.ServiceBanner.Enabled {
|
||||
if err := validateHexColor(appearance.ServiceBanner.BackgroundColor); err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: fmt.Sprintf("parse color: %+v", err),
|
||||
Message: "Invalid color format",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -156,7 +165,8 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) {
|
||||
serviceBannerJSON, err := json.Marshal(appearance.ServiceBanner)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: fmt.Sprintf("marshal banner: %+v", err),
|
||||
Message: "Unable to marshal service banner",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -164,7 +174,17 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) {
|
||||
err = api.Database.UpsertServiceBanner(ctx, string(serviceBannerJSON))
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: fmt.Sprintf("database error: %+v", err),
|
||||
Message: "Unable to set service banner",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = api.Database.UpsertApplicationName(ctx, appearance.ApplicationName)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Unable to set application name",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -172,7 +192,8 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) {
|
||||
err = api.Database.UpsertLogoURL(ctx, appearance.LogoURL)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: fmt.Sprintf("database error: %+v", err),
|
||||
Message: "Unable to set logo URL",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clibase"
|
||||
@@ -20,6 +21,37 @@ import (
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestCustomLogoAndCompanyName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Prepare enterprise deployment
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
adminClient, _ := coderdenttest.New(t, &coderdenttest.Options{DontAddLicense: true})
|
||||
coderdenttest.AddLicense(t, adminClient, coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureAppearance: 1,
|
||||
},
|
||||
})
|
||||
|
||||
// Update logo and application name
|
||||
uac := codersdk.UpdateAppearanceConfig{
|
||||
ApplicationName: "ACME Ltd",
|
||||
LogoURL: "http://logo-url/file.png",
|
||||
}
|
||||
|
||||
err := adminClient.UpdateAppearance(ctx, uac)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify update
|
||||
got, err := adminClient.Appearance(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, uac.ApplicationName, got.ApplicationName)
|
||||
require.Equal(t, uac.LogoURL, got.LogoURL)
|
||||
}
|
||||
|
||||
func TestServiceBanners(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -77,6 +109,13 @@ func TestServiceBanners(t *testing.T) {
|
||||
wantBanner.ServiceBanner.BackgroundColor = "#bad color"
|
||||
err = adminClient.UpdateAppearance(ctx, wantBanner)
|
||||
require.Error(t, err)
|
||||
|
||||
var sdkErr *codersdk.Error
|
||||
if assert.ErrorAs(t, err, &sdkErr) {
|
||||
assert.Equal(t, http.StatusBadRequest, sdkErr.StatusCode())
|
||||
assert.Contains(t, sdkErr.Message, "Invalid color format")
|
||||
assert.Contains(t, sdkErr.Detail, "expected # prefix and 6 characters")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Agent", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user