mirror of
https://github.com/coder/coder.git
synced 2025-07-21 01:28:49 +00:00
feat: expose application name via Appearance API (#9886)
This commit is contained in:
6
coderd/apidoc/docs.go
generated
6
coderd/apidoc/docs.go
generated
@ -7023,6 +7023,9 @@ const docTemplate = `{
|
||||
"codersdk.AppearanceConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"application_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"logo_url": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -10201,6 +10204,9 @@ const docTemplate = `{
|
||||
"codersdk.UpdateAppearanceConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"application_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"logo_url": {
|
||||
"type": "string"
|
||||
},
|
||||
|
6
coderd/apidoc/swagger.json
generated
6
coderd/apidoc/swagger.json
generated
@ -6233,6 +6233,9 @@
|
||||
"codersdk.AppearanceConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"application_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"logo_url": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -9225,6 +9228,9 @@
|
||||
"codersdk.UpdateAppearanceConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"application_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"logo_url": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -851,6 +851,11 @@ func (q *querier) GetAppSecurityKey(ctx context.Context) (string, error) {
|
||||
return q.db.GetAppSecurityKey(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetApplicationName(ctx context.Context) (string, error) {
|
||||
// No authz checks
|
||||
return q.db.GetApplicationName(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetAuditLogsOffset(ctx context.Context, arg database.GetAuditLogsOffsetParams) ([]database.GetAuditLogsOffsetRow, error) {
|
||||
// To optimize audit logs, we only check the global audit log permission once.
|
||||
// This is because we expect a large unbounded set of audit logs, and applying a SQL
|
||||
@ -2808,6 +2813,13 @@ func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error {
|
||||
return q.db.UpsertAppSecurityKey(ctx, data)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertApplicationName(ctx context.Context, value string) error {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceDeploymentValues); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.UpsertApplicationName(ctx, value)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
|
||||
return err
|
||||
|
@ -160,6 +160,7 @@ type data struct {
|
||||
derpMeshKey string
|
||||
lastUpdateCheck []byte
|
||||
serviceBanner []byte
|
||||
applicationName string
|
||||
logoURL string
|
||||
appSecurityKey string
|
||||
oauthSigningKey string
|
||||
@ -1128,6 +1129,17 @@ func (q *FakeQuerier) GetAppSecurityKey(_ context.Context) (string, error) {
|
||||
return q.appSecurityKey, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetApplicationName(_ context.Context) (string, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
if q.applicationName == "" {
|
||||
return "", sql.ErrNoRows
|
||||
}
|
||||
|
||||
return q.applicationName, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetAuditLogsOffset(_ context.Context, arg database.GetAuditLogsOffsetParams) ([]database.GetAuditLogsOffsetRow, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return nil, err
|
||||
@ -6319,6 +6331,14 @@ func (q *FakeQuerier) UpsertAppSecurityKey(_ context.Context, data string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertApplicationName(_ context.Context, data string) error {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
q.applicationName = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertDefaultProxy(_ context.Context, arg database.UpsertDefaultProxyParams) error {
|
||||
q.defaultProxyDisplayName = arg.DisplayName
|
||||
q.defaultProxyIconURL = arg.IconUrl
|
||||
|
@ -293,6 +293,13 @@ func (m metricsStore) GetAppSecurityKey(ctx context.Context) (string, error) {
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (m metricsStore) GetApplicationName(ctx context.Context) (string, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetApplicationName(ctx)
|
||||
m.queryLatencies.WithLabelValues("GetApplicationName").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetAuditLogsOffset(ctx context.Context, arg database.GetAuditLogsOffsetParams) ([]database.GetAuditLogsOffsetRow, error) {
|
||||
start := time.Now()
|
||||
rows, err := m.s.GetAuditLogsOffset(ctx, arg)
|
||||
@ -1761,6 +1768,13 @@ func (m metricsStore) UpsertAppSecurityKey(ctx context.Context, value string) er
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpsertApplicationName(ctx context.Context, value string) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpsertApplicationName(ctx, value)
|
||||
m.queryLatencies.WithLabelValues("UpsertApplicationName").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpsertDefaultProxy(ctx, arg)
|
||||
|
@ -488,6 +488,21 @@ func (mr *MockStoreMockRecorder) GetAppSecurityKey(arg0 interface{}) *gomock.Cal
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppSecurityKey", reflect.TypeOf((*MockStore)(nil).GetAppSecurityKey), arg0)
|
||||
}
|
||||
|
||||
// GetApplicationName mocks base method.
|
||||
func (m *MockStore) GetApplicationName(arg0 context.Context) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetApplicationName", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetApplicationName indicates an expected call of GetApplicationName.
|
||||
func (mr *MockStoreMockRecorder) GetApplicationName(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetApplicationName", reflect.TypeOf((*MockStore)(nil).GetApplicationName), arg0)
|
||||
}
|
||||
|
||||
// GetAuditLogsOffset mocks base method.
|
||||
func (m *MockStore) GetAuditLogsOffset(arg0 context.Context, arg1 database.GetAuditLogsOffsetParams) ([]database.GetAuditLogsOffsetRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -3698,6 +3713,20 @@ func (mr *MockStoreMockRecorder) UpsertAppSecurityKey(arg0, arg1 interface{}) *g
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertAppSecurityKey", reflect.TypeOf((*MockStore)(nil).UpsertAppSecurityKey), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpsertApplicationName mocks base method.
|
||||
func (m *MockStore) UpsertApplicationName(arg0 context.Context, arg1 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpsertApplicationName", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpsertApplicationName indicates an expected call of UpsertApplicationName.
|
||||
func (mr *MockStoreMockRecorder) UpsertApplicationName(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertApplicationName", reflect.TypeOf((*MockStore)(nil).UpsertApplicationName), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpsertDefaultProxy mocks base method.
|
||||
func (m *MockStore) UpsertDefaultProxy(arg0 context.Context, arg1 database.UpsertDefaultProxyParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -63,6 +63,7 @@ type sqlcQuerier interface {
|
||||
GetAllTailnetAgents(ctx context.Context) ([]TailnetAgent, error)
|
||||
GetAllTailnetClients(ctx context.Context) ([]GetAllTailnetClientsRow, error)
|
||||
GetAppSecurityKey(ctx context.Context) (string, error)
|
||||
GetApplicationName(ctx context.Context) (string, error)
|
||||
// GetAuditLogsBefore retrieves `row_limit` number of audit logs before the provided
|
||||
// ID.
|
||||
GetAuditLogsOffset(ctx context.Context, arg GetAuditLogsOffsetParams) ([]GetAuditLogsOffsetRow, error)
|
||||
@ -329,6 +330,7 @@ type sqlcQuerier interface {
|
||||
UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error
|
||||
UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error
|
||||
UpsertAppSecurityKey(ctx context.Context, value string) error
|
||||
UpsertApplicationName(ctx context.Context, value string) error
|
||||
// The default proxy is implied and not actually stored in the database.
|
||||
// So we need to store it's configuration here for display purposes.
|
||||
// The functional values are immutable and controlled implicitly.
|
||||
|
@ -4066,6 +4066,17 @@ func (q *sqlQuerier) GetAppSecurityKey(ctx context.Context) (string, error) {
|
||||
return value, err
|
||||
}
|
||||
|
||||
const getApplicationName = `-- name: GetApplicationName :one
|
||||
SELECT value FROM site_configs WHERE key = 'application_name'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetApplicationName(ctx context.Context) (string, error) {
|
||||
row := q.db.QueryRowContext(ctx, getApplicationName)
|
||||
var value string
|
||||
err := row.Scan(&value)
|
||||
return value, err
|
||||
}
|
||||
|
||||
const getDERPMeshKey = `-- name: GetDERPMeshKey :one
|
||||
SELECT value FROM site_configs WHERE key = 'derp_mesh_key'
|
||||
`
|
||||
@ -4178,6 +4189,16 @@ func (q *sqlQuerier) UpsertAppSecurityKey(ctx context.Context, value string) err
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertApplicationName = `-- name: UpsertApplicationName :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ('application_name', $1)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'application_name'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) UpsertApplicationName(ctx context.Context, value string) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertApplicationName, value)
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertDefaultProxy = `-- name: UpsertDefaultProxy :exec
|
||||
INSERT INTO site_configs (key, value)
|
||||
VALUES
|
||||
|
@ -50,6 +50,13 @@ ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'logo_url';
|
||||
-- name: GetLogoURL :one
|
||||
SELECT value FROM site_configs WHERE key = 'logo_url';
|
||||
|
||||
-- name: UpsertApplicationName :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ('application_name', $1)
|
||||
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'application_name';
|
||||
|
||||
-- name: GetApplicationName :one
|
||||
SELECT value FROM site_configs WHERE key = 'application_name';
|
||||
|
||||
-- name: GetAppSecurityKey :one
|
||||
SELECT value FROM site_configs WHERE key = 'app_signing_key';
|
||||
|
||||
|
@ -1832,12 +1832,14 @@ func (c *Client) DeploymentStats(ctx context.Context) (DeploymentStats, error) {
|
||||
}
|
||||
|
||||
type AppearanceConfig struct {
|
||||
ApplicationName string `json:"application_name"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
ServiceBanner ServiceBannerConfig `json:"service_banner"`
|
||||
SupportLinks []LinkConfig `json:"support_links,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateAppearanceConfig struct {
|
||||
ApplicationName string `json:"application_name"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
ServiceBanner ServiceBannerConfig `json:"service_banner"`
|
||||
}
|
||||
|
3
docs/api/enterprise.md
generated
3
docs/api/enterprise.md
generated
@ -19,6 +19,7 @@ curl -X GET http://coder-server:8080/api/v2/appearance \
|
||||
|
||||
```json
|
||||
{
|
||||
"application_name": "string",
|
||||
"logo_url": "string",
|
||||
"service_banner": {
|
||||
"background_color": "string",
|
||||
@ -61,6 +62,7 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \
|
||||
|
||||
```json
|
||||
{
|
||||
"application_name": "string",
|
||||
"logo_url": "string",
|
||||
"service_banner": {
|
||||
"background_color": "string",
|
||||
@ -82,6 +84,7 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \
|
||||
|
||||
```json
|
||||
{
|
||||
"application_name": "string",
|
||||
"logo_url": "string",
|
||||
"service_banner": {
|
||||
"background_color": "string",
|
||||
|
8
docs/api/schemas.md
generated
8
docs/api/schemas.md
generated
@ -952,6 +952,7 @@ _None_
|
||||
|
||||
```json
|
||||
{
|
||||
"application_name": "string",
|
||||
"logo_url": "string",
|
||||
"service_banner": {
|
||||
"background_color": "string",
|
||||
@ -971,7 +972,8 @@ _None_
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ---------------- | ------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| ------------------ | ------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `application_name` | string | false | | |
|
||||
| `logo_url` | string | false | | |
|
||||
| `service_banner` | [codersdk.ServiceBannerConfig](#codersdkservicebannerconfig) | false | | |
|
||||
| `support_links` | array of [codersdk.LinkConfig](#codersdklinkconfig) | false | | |
|
||||
@ -4950,6 +4952,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
||||
|
||||
```json
|
||||
{
|
||||
"application_name": "string",
|
||||
"logo_url": "string",
|
||||
"service_banner": {
|
||||
"background_color": "string",
|
||||
@ -4962,7 +4965,8 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ---------------- | ------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| ------------------ | ------------------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `application_name` | string | false | | |
|
||||
| `logo_url` | string | false | | |
|
||||
| `service_banner` | [codersdk.ServiceBannerConfig](#codersdkservicebannerconfig) | false | | |
|
||||
|
||||
|
@ -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,6 +96,7 @@ func (api *API) fetchAppearanceConfig(ctx context.Context) (codersdk.AppearanceC
|
||||
}
|
||||
|
||||
cfg := codersdk.AppearanceConfig{
|
||||
ApplicationName: applicationName,
|
||||
LogoURL: logoURL,
|
||||
}
|
||||
if serviceBannerJSON != "" {
|
||||
@ -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) {
|
||||
|
@ -1088,6 +1088,7 @@ export const getAppearance = async (): Promise<TypesGen.AppearanceConfig> => {
|
||||
} catch (ex) {
|
||||
if (axios.isAxiosError(ex) && ex.response?.status === 404) {
|
||||
return {
|
||||
application_name: "",
|
||||
logo_url: "",
|
||||
service_banner: {
|
||||
enabled: false,
|
||||
|
2
site/src/api/typesGenerated.ts
generated
2
site/src/api/typesGenerated.ts
generated
@ -46,6 +46,7 @@ export interface AppHostResponse {
|
||||
|
||||
// From codersdk/deployment.go
|
||||
export interface AppearanceConfig {
|
||||
readonly application_name: string;
|
||||
readonly logo_url: string;
|
||||
readonly service_banner: ServiceBannerConfig;
|
||||
readonly support_links?: LinkConfig[];
|
||||
@ -1091,6 +1092,7 @@ export interface UpdateActiveTemplateVersion {
|
||||
|
||||
// From codersdk/deployment.go
|
||||
export interface UpdateAppearanceConfig {
|
||||
readonly application_name: string;
|
||||
readonly logo_url: string;
|
||||
readonly service_banner: ServiceBannerConfig;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ const meta: Meta<typeof AppearanceSettingsPageView> = {
|
||||
component: AppearanceSettingsPageView,
|
||||
args: {
|
||||
appearance: {
|
||||
application_name: "",
|
||||
logo_url: "https://github.com/coder.png",
|
||||
service_banner: {
|
||||
enabled: true,
|
||||
|
@ -2152,6 +2152,7 @@ export const MockDeploymentConfig: DeploymentConfig = {
|
||||
};
|
||||
|
||||
export const MockAppearanceConfig: TypesGen.AppearanceConfig = {
|
||||
application_name: "",
|
||||
logo_url: "",
|
||||
service_banner: {
|
||||
enabled: false,
|
||||
|
Reference in New Issue
Block a user