From e44848562ab4f30ef09e52c408b4350c49c5b079 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 22 Feb 2023 14:33:00 -0700 Subject: [PATCH 01/13] backend: support links --- cli/deployment/config.go | 15 ++++ coderd/apidoc/docs.go | 69 +++++++++++++++++ coderd/apidoc/swagger.json | 69 +++++++++++++++++ codersdk/deployment.go | 15 +++- docs/api/enterprise.md | 27 ++++++- docs/api/general.md | 25 ++++++ docs/api/schemas.md | 132 +++++++++++++++++++++++++++++++- enterprise/coderd/appearance.go | 25 ++++++ site/src/api/typesGenerated.ts | 22 +++++- 9 files changed, 393 insertions(+), 6 deletions(-) diff --git a/cli/deployment/config.go b/cli/deployment/config.go index 41f53eb600fd2..d265ae67eeffe 100644 --- a/cli/deployment/config.go +++ b/cli/deployment/config.go @@ -570,6 +570,15 @@ func newConfig() *codersdk.DeploymentConfig { Flag: "disable-password-auth", Default: false, }, + Support: &codersdk.SupportConfig{ + Links: &codersdk.DeploymentConfigField[[]codersdk.LinkConfig]{ + Name: "Support links", + Usage: "Use custom support links", + Flag: "support-links", + Default: []codersdk.LinkConfig{}, + Enterprise: true, + }, + }, } } @@ -649,6 +658,10 @@ func setConfig(prefix string, vip *viper.Viper, target interface{}) { // Do not bind to CODER_GITAUTH, instead bind to CODER_GITAUTH_0_*, etc. values := readSliceFromViper[codersdk.GitAuthConfig](vip, prefix, value) val.FieldByName("Value").Set(reflect.ValueOf(values)) + case []codersdk.LinkConfig: + // Do not bind to CODER_SUPPORT_LINKS, instead bind to CODER_SUPPORT_LINKS_0_*, etc. + values := readSliceFromViper[codersdk.LinkConfig](vip, prefix, value) + val.FieldByName("Value").Set(reflect.ValueOf(values)) default: panic(fmt.Sprintf("unsupported type %T", value)) } @@ -824,6 +837,8 @@ func setFlags(prefix string, flagset *pflag.FlagSet, vip *viper.Viper, target in _ = flagset.DurationP(flg, shorthand, vip.GetDuration(prefix), usage) case []string: _ = flagset.StringSliceP(flg, shorthand, vip.GetStringSlice(prefix), usage) + case []codersdk.LinkConfig: + // Ignore this one! case []codersdk.GitAuthConfig: // Ignore this one! default: diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index d4e1236849534..35def522b54cd 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -5379,6 +5379,12 @@ const docTemplate = `{ }, "service_banner": { "$ref": "#/definitions/codersdk.ServiceBannerConfig" + }, + "support_links": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.LinkConfig" + } } } }, @@ -6196,6 +6202,9 @@ const docTemplate = `{ "strict_transport_security_options": { "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" }, + "support": { + "$ref": "#/definitions/codersdk.SupportConfig" + }, "swagger": { "$ref": "#/definitions/codersdk.SwaggerConfig" }, @@ -6254,6 +6263,44 @@ const docTemplate = `{ } } }, + "codersdk.DeploymentConfigField-array_codersdk_LinkConfig": { + "type": "object", + "properties": { + "default": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.LinkConfig" + } + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.LinkConfig" + } + } + } + }, "codersdk.DeploymentConfigField-array_string": { "type": "object", "properties": { @@ -6651,6 +6698,20 @@ const docTemplate = `{ } } }, + "codersdk.LinkConfig": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "name": { + "type": "string" + }, + "target": { + "type": "string" + } + } + }, "codersdk.LogLevel": { "type": "string", "enum": [ @@ -7362,6 +7423,14 @@ const docTemplate = `{ } } }, + "codersdk.SupportConfig": { + "type": "object", + "properties": { + "links": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_codersdk_LinkConfig" + } + } + }, "codersdk.SwaggerConfig": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 894a2d0b6cafd..6c3d52f027e22 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -4760,6 +4760,12 @@ }, "service_banner": { "$ref": "#/definitions/codersdk.ServiceBannerConfig" + }, + "support_links": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.LinkConfig" + } } } }, @@ -5516,6 +5522,9 @@ "strict_transport_security_options": { "$ref": "#/definitions/codersdk.DeploymentConfigField-array_string" }, + "support": { + "$ref": "#/definitions/codersdk.SupportConfig" + }, "swagger": { "$ref": "#/definitions/codersdk.SwaggerConfig" }, @@ -5574,6 +5583,44 @@ } } }, + "codersdk.DeploymentConfigField-array_codersdk_LinkConfig": { + "type": "object", + "properties": { + "default": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.LinkConfig" + } + }, + "enterprise": { + "type": "boolean" + }, + "flag": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "secret": { + "type": "boolean" + }, + "shorthand": { + "type": "string" + }, + "usage": { + "type": "string" + }, + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.LinkConfig" + } + } + } + }, "codersdk.DeploymentConfigField-array_string": { "type": "object", "properties": { @@ -5961,6 +6008,20 @@ } } }, + "codersdk.LinkConfig": { + "type": "object", + "properties": { + "icon": { + "type": "string" + }, + "name": { + "type": "string" + }, + "target": { + "type": "string" + } + } + }, "codersdk.LogLevel": { "type": "string", "enum": ["trace", "debug", "info", "warn", "error"], @@ -6608,6 +6669,14 @@ } } }, + "codersdk.SupportConfig": { + "type": "object", + "properties": { + "links": { + "$ref": "#/definitions/codersdk.DeploymentConfigField-array_codersdk_LinkConfig" + } + } + }, "codersdk.SwaggerConfig": { "type": "object", "properties": { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 69bb6b6b3f0e7..258806561e796 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -153,6 +153,8 @@ type DeploymentConfig struct { Address *DeploymentConfigField[string] `json:"address" typescript:",notnull"` // DEPRECATED: Use Experiments instead. Experimental *DeploymentConfigField[bool] `json:"experimental" typescript:",notnull"` + + Support *SupportConfig `json:"support" typescript:",notnull"` } type DERP struct { @@ -276,8 +278,18 @@ type DangerousConfig struct { AllowPathAppSiteOwnerAccess *DeploymentConfigField[bool] `json:"allow_path_app_site_owner_access" typescript:",notnull"` } +type SupportConfig struct { + Links *DeploymentConfigField[[]LinkConfig] `json:"links" typescript:",notnull"` +} + +type LinkConfig struct { + Name string `json:"name"` + Target string `json:"target"` + Icon string `json:"icon,omitempty"` +} + type Flaggable interface { - string | time.Duration | bool | int | []string | []GitAuthConfig + string | time.Duration | bool | int | []string | []GitAuthConfig | []LinkConfig } type DeploymentConfigField[T Flaggable] struct { @@ -348,6 +360,7 @@ func (c *Client) DeploymentConfig(ctx context.Context) (DeploymentConfig, error) type AppearanceConfig struct { LogoURL string `json:"logo_url"` ServiceBanner ServiceBannerConfig `json:"service_banner"` + SupportLinks []LinkConfig `json:"support_links,omitempty"` } type ServiceBannerConfig struct { diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index be461cc606e44..3c6185c16b9dc 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -24,7 +24,14 @@ curl -X GET http://coder-server:8080/api/v2/appearance \ "background_color": "string", "enabled": true, "message": "string" - } + }, + "support_links": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] } ``` @@ -59,7 +66,14 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \ "background_color": "string", "enabled": true, "message": "string" - } + }, + "support_links": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] } ``` @@ -80,7 +94,14 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \ "background_color": "string", "enabled": true, "message": "string" - } + }, + "support_links": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] } ``` diff --git a/docs/api/general.md b/docs/api/general.md index 15854e7cfec86..8102f24af5df1 100644 --- a/docs/api/general.md +++ b/docs/api/general.md @@ -879,6 +879,31 @@ curl -X GET http://coder-server:8080/api/v2/config/deployment \ "usage": "string", "value": ["string"] }, + "support": { + "links": { + "default": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] + } + }, "swagger": { "enable": { "default": true, diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 948a0e6304197..6ae541c71dc9b 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -440,7 +440,14 @@ "background_color": "string", "enabled": true, "message": "string" - } + }, + "support_links": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] } ``` @@ -450,6 +457,7 @@ | ---------------- | ------------------------------------------------------------ | -------- | ------------ | ----------- | | `logo_url` | string | false | | | | `service_banner` | [codersdk.ServiceBannerConfig](#codersdkservicebannerconfig) | false | | | +| `support_links` | array of [codersdk.LinkConfig](#codersdklinkconfig) | false | | | ## codersdk.AssignableRoles @@ -2285,6 +2293,31 @@ CreateParameterRequest is a structure used to create a new parameter value for a "usage": "string", "value": ["string"] }, + "support": { + "links": { + "default": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] + } + }, "swagger": { "enable": { "default": true, @@ -2546,6 +2579,7 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `ssh_keygen_algorithm` | [codersdk.DeploymentConfigField-string](#codersdkdeploymentconfigfield-string) | false | | | | `strict_transport_security` | [codersdk.DeploymentConfigField-int](#codersdkdeploymentconfigfield-int) | false | | | | `strict_transport_security_options` | [codersdk.DeploymentConfigField-array_string](#codersdkdeploymentconfigfield-array_string) | false | | | +| `support` | [codersdk.SupportConfig](#codersdksupportconfig) | false | | | | `swagger` | [codersdk.SwaggerConfig](#codersdkswaggerconfig) | false | | | | `telemetry` | [codersdk.TelemetryConfig](#codersdktelemetryconfig) | false | | | | `tls` | [codersdk.TLSConfig](#codersdktlsconfig) | false | | | @@ -2607,6 +2641,48 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `usage` | string | false | | | | `value` | array of [codersdk.GitAuthConfig](#codersdkgitauthconfig) | false | | | +## codersdk.DeploymentConfigField-array_codersdk_LinkConfig + +```json +{ + "default": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------ | --------------------------------------------------- | -------- | ------------ | ----------- | +| `default` | array of [codersdk.LinkConfig](#codersdklinkconfig) | false | | | +| `enterprise` | boolean | false | | | +| `flag` | string | false | | | +| `hidden` | boolean | false | | | +| `name` | string | false | | | +| `secret` | boolean | false | | | +| `shorthand` | string | false | | | +| `usage` | string | false | | | +| `value` | array of [codersdk.LinkConfig](#codersdklinkconfig) | false | | | + ## codersdk.DeploymentConfigField-array_string ```json @@ -3043,6 +3119,24 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `uploaded_at` | string | false | | | | `uuid` | string | false | | | +## codersdk.LinkConfig + +```json +{ + "icon": "string", + "name": "string", + "target": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| -------- | ------ | -------- | ------------ | ----------- | +| `icon` | string | false | | | +| `name` | string | false | | | +| `target` | string | false | | | + ## codersdk.LogLevel ```json @@ -4120,6 +4214,42 @@ Parameter represents a set value for the scope. | `enabled` | boolean | false | | | | `message` | string | false | | | +## codersdk.SupportConfig + +```json +{ + "links": { + "default": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ], + "enterprise": true, + "flag": "string", + "hidden": true, + "name": "string", + "secret": true, + "shorthand": "string", + "usage": "string", + "value": [ + { + "icon": "string", + "name": "string", + "target": "string" + } + ] + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------- | -------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | ----------- | +| `links` | [codersdk.DeploymentConfigField-array_codersdk_LinkConfig](#codersdkdeploymentconfigfield-array_codersdk_linkconfig) | false | | | + ## codersdk.SwaggerConfig ```json diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index e60e7a2ed5388..28a035eb57f55 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -15,6 +15,26 @@ import ( "github.com/coder/coder/codersdk" ) +var ( + defaultSupportLinks = []codersdk.LinkConfig{ + { + Name: "Documentation", + Target: "https://coder.com/docs/coder-oss", + Icon: "docs", + }, + { + Name: "Report a bug", + Target: "https://github.com/coder/coder/issues/new?labels=needs+grooming&body=TODO", + Icon: "bug", + }, + { + Name: "Join the Coder Discord", + Target: "https://coder.com/chat?utm_source=coder&utm_medium=coder&utm_campaign=server-footer", + Icon: "chat", + }, + } +) + // @Summary Get appearance // @ID get-appearance // @Security CoderSessionToken @@ -67,6 +87,11 @@ func (api *API) appearance(rw http.ResponseWriter, r *http.Request) { } } + cfg.SupportLinks = defaultSupportLinks + if len(api.DeploymentConfig.Support.Links.Value) > 0 { + cfg.SupportLinks = api.DeploymentConfig.Support.Links.Value + } + httpapi.Write(r.Context(), rw, http.StatusOK, cfg) } diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index c7159fb50a8f8..7a55f448eef97 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -34,6 +34,7 @@ export interface AppHostResponse { export interface AppearanceConfig { readonly logo_url: string readonly service_banner: ServiceBannerConfig + readonly support_links?: LinkConfig[] } // From codersdk/roles.go @@ -334,6 +335,7 @@ export interface DeploymentConfig { readonly disable_password_auth: DeploymentConfigField readonly address: DeploymentConfigField readonly experimental: DeploymentConfigField + readonly support: SupportConfig } // From codersdk/deployment.go @@ -434,6 +436,13 @@ export interface License { readonly claims: Record } +// From codersdk/deployment.go +export interface LinkConfig { + readonly name: string + readonly target: string + readonly icon?: string +} + // From codersdk/deployment.go export interface LoggingConfig { readonly human: DeploymentConfigField @@ -657,6 +666,11 @@ export interface ServiceBannerConfig { readonly background_color?: string } +// From codersdk/deployment.go +export interface SupportConfig { + readonly links: DeploymentConfigField +} + // From codersdk/deployment.go export interface SwaggerConfig { readonly enable: DeploymentConfigField @@ -1326,4 +1340,10 @@ export const WorkspaceTransitions: WorkspaceTransition[] = [ ] // From codersdk/deployment.go -export type Flaggable = string | number | boolean | string[] | GitAuthConfig[] +export type Flaggable = + | string + | number + | boolean + | string[] + | GitAuthConfig[] + | LinkConfig[] From bf7d34fe18b1e3bd83899f4e3b053b14d6d0a365 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 22 Feb 2023 15:55:24 -0700 Subject: [PATCH 02/13] frontend: Support links --- codersdk/deployment.go | 2 +- site/src/api/typesGenerated.ts | 2 +- site/src/components/Navbar/Navbar.tsx | 1 + site/src/components/NavbarView/NavbarView.tsx | 3 + .../components/UserDropdown/UsersDropdown.tsx | 3 + .../UserDropdownContent.tsx | 65 +++++++------------ 6 files changed, 33 insertions(+), 43 deletions(-) diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 258806561e796..688d2230f9107 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -285,7 +285,7 @@ type SupportConfig struct { type LinkConfig struct { Name string `json:"name"` Target string `json:"target"` - Icon string `json:"icon,omitempty"` + Icon string `json:"icon"` } type Flaggable interface { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 7a55f448eef97..102c6a97a7030 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -440,7 +440,7 @@ export interface License { export interface LinkConfig { readonly name: string readonly target: string - readonly icon?: string + readonly icon: string } // From codersdk/deployment.go diff --git a/site/src/components/Navbar/Navbar.tsx b/site/src/components/Navbar/Navbar.tsx index c7090178f2d5d..f46e6bfccbf43 100644 --- a/site/src/components/Navbar/Navbar.tsx +++ b/site/src/components/Navbar/Navbar.tsx @@ -22,6 +22,7 @@ export const Navbar: FC = () => { user={me} logo_url={appearance.config.logo_url} buildInfo={buildInfo} + supportLinks={appearance.config.support_links} onSignOut={onSignOut} canViewAuditLog={canViewAuditLog} canViewDeployment={canViewDeployment} diff --git a/site/src/components/NavbarView/NavbarView.tsx b/site/src/components/NavbarView/NavbarView.tsx index 44123fa0100f4..039cb4126686c 100644 --- a/site/src/components/NavbarView/NavbarView.tsx +++ b/site/src/components/NavbarView/NavbarView.tsx @@ -19,6 +19,7 @@ export interface NavbarViewProps { logo_url?: string user?: TypesGen.User buildInfo?: TypesGen.BuildInfoResponse + supportLinks?: TypesGen.LinkConfig[] onSignOut: () => void canViewAuditLog: boolean canViewDeployment: boolean @@ -86,6 +87,7 @@ export const NavbarView: React.FC> = ({ user, logo_url, buildInfo, + supportLinks, onSignOut, canViewAuditLog, canViewDeployment, @@ -147,6 +149,7 @@ export const NavbarView: React.FC> = ({ )} diff --git a/site/src/components/UserDropdown/UsersDropdown.tsx b/site/src/components/UserDropdown/UsersDropdown.tsx index e9650fc29d8dd..3d670f3be3273 100644 --- a/site/src/components/UserDropdown/UsersDropdown.tsx +++ b/site/src/components/UserDropdown/UsersDropdown.tsx @@ -13,12 +13,14 @@ import { UserDropdownContent } from "../UserDropdownContent/UserDropdownContent" export interface UserDropdownProps { user: TypesGen.User buildInfo?: TypesGen.BuildInfoResponse + supportLinks?: TypesGen.LinkConfig[] onSignOut: () => void } export const UserDropdown: FC> = ({ buildInfo, user, + supportLinks, onSignOut, }: UserDropdownProps) => { const styles = useStyles() @@ -69,6 +71,7 @@ export const UserDropdown: FC> = ({ diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.tsx index b69b3a6585343..efc524b5bd8d1 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.tsx @@ -25,6 +25,7 @@ export const Language = { export interface UserDropdownContentProps { user: TypesGen.User buildInfo?: TypesGen.BuildInfoResponse + supportLinks?: TypesGen.LinkConfig[] onPopoverClose: () => void onSignOut: () => void } @@ -32,14 +33,11 @@ export interface UserDropdownContentProps { export const UserDropdownContent: FC = ({ buildInfo, user, + supportLinks, onPopoverClose, onSignOut, }) => { const styles = useStyles() - const githubUrl = `https://github.com/coder/coder/issues/new?labels=needs+grooming&body=${encodeURIComponent(`Version: [\`${buildInfo?.version}\`](${buildInfo?.external_url}) - - `)}` - const discordUrl = `https://coder.com/chat?utm_source=coder&utm_medium=coder&utm_campaign=server-footer` return (
@@ -64,43 +62,28 @@ export const UserDropdownContent: FC = ({ - - - - {Language.docsLabel} - - - - - - - {Language.bugLabel} - - - - - - - {Language.discordLabel} - - - - + <> + { supportLinks && supportLinks.map((link) => ( + + + {link.icon === "bug" && ( )} + {link.icon === "chat" && ( )} + {link.icon === "docs" && ( )} + {link.name} + + + )) } + + + {supportLinks && ( + + )} Date: Wed, 22 Feb 2023 16:04:07 -0700 Subject: [PATCH 03/13] fmt --- enterprise/coderd/appearance.go | 36 ++++++++-------- .../UserDropdownContent.tsx | 43 +++++++++++-------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index 28a035eb57f55..1e22b30dbd734 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -15,25 +15,23 @@ import ( "github.com/coder/coder/codersdk" ) -var ( - defaultSupportLinks = []codersdk.LinkConfig{ - { - Name: "Documentation", - Target: "https://coder.com/docs/coder-oss", - Icon: "docs", - }, - { - Name: "Report a bug", - Target: "https://github.com/coder/coder/issues/new?labels=needs+grooming&body=TODO", - Icon: "bug", - }, - { - Name: "Join the Coder Discord", - Target: "https://coder.com/chat?utm_source=coder&utm_medium=coder&utm_campaign=server-footer", - Icon: "chat", - }, - } -) +var defaultSupportLinks = []codersdk.LinkConfig{ + { + Name: "Documentation", + Target: "https://coder.com/docs/coder-oss", + Icon: "docs", + }, + { + Name: "Report a bug", + Target: "https://github.com/coder/coder/issues/new?labels=needs+grooming&body=TODO", + Icon: "bug", + }, + { + Name: "Join the Coder Discord", + Target: "https://coder.com/chat?utm_source=coder&utm_medium=coder&utm_campaign=server-footer", + Icon: "chat", + }, +} // @Summary Get appearance // @ID get-appearance diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.tsx index efc524b5bd8d1..3b4fac601c03b 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.tsx @@ -63,27 +63,32 @@ export const UserDropdownContent: FC = ({ <> - { supportLinks && supportLinks.map((link) => ( - - - {link.icon === "bug" && ( )} - {link.icon === "chat" && ( )} - {link.icon === "docs" && ( )} - {link.name} - - - )) } + {supportLinks && + supportLinks.map((link) => ( + + + {link.icon === "bug" && ( + + )} + {link.icon === "chat" && ( + + )} + {link.icon === "docs" && ( + + )} + {link.name} + + + ))} - {supportLinks && ( - - )} + {supportLinks && } Date: Wed, 22 Feb 2023 16:23:46 -0700 Subject: [PATCH 04/13] test: CODER_SUPPORT_LINKS_0_NAME --- cli/deployment/config_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cli/deployment/config_test.go b/cli/deployment/config_test.go index d6d5b93d98043..37dbc9a9e42e1 100644 --- a/cli/deployment/config_test.go +++ b/cli/deployment/config_test.go @@ -222,6 +222,29 @@ func TestConfig(t *testing.T) { Regex: "gitlab.com", }}, config.GitAuth.Value) }, + }, { + Name: "Support links", + Env: map[string]string{ + "CODER_SUPPORT_LINKS_0_NAME": "First link", + "CODER_SUPPORT_LINKS_0_TARGET": "http://target-link-1", + "CODER_SUPPORT_LINKS_0_ICON": "bug", + + "CODER_SUPPORT_LINKS_1_NAME": "Second link", + "CODER_SUPPORT_LINKS_1_TARGET": "http://target-link-2", + "CODER_SUPPORT_LINKS_1_ICON": "chat", + }, + Valid: func(config *codersdk.DeploymentConfig) { + require.Len(t, config.Support.Links.Value, 2) + require.Equal(t, []codersdk.LinkConfig{{ + Name: "First link", + Target: "http://target-link-1", + Icon: "bug", + }, { + Name: "Second link", + Target: "http://target-link-2", + Icon: "chat", + }}, config.Support.Links.Value) + }, }, { Name: "Wrong env must not break default values", Env: map[string]string{ From 132324ccd1fd215957462c69b940849e13f147fc Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 22 Feb 2023 17:10:24 -0700 Subject: [PATCH 05/13] Go tests --- enterprise/coderd/appearance.go | 16 +++++-- enterprise/coderd/appearance_test.go | 69 ++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index 1e22b30dbd734..1d488c60abf87 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -48,7 +48,9 @@ func (api *API) appearance(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() if !isEntitled { - httpapi.Write(ctx, rw, http.StatusOK, codersdk.AppearanceConfig{}) + httpapi.Write(ctx, rw, http.StatusOK, codersdk.AppearanceConfig{ + SupportLinks: defaultSupportLinks, + }) return } @@ -85,8 +87,9 @@ func (api *API) appearance(rw http.ResponseWriter, r *http.Request) { } } - cfg.SupportLinks = defaultSupportLinks - if len(api.DeploymentConfig.Support.Links.Value) > 0 { + if len(api.DeploymentConfig.Support.Links.Value) == 0 { + cfg.SupportLinks = defaultSupportLinks + } else { cfg.SupportLinks = api.DeploymentConfig.Support.Links.Value } @@ -128,6 +131,13 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) { return } + if len(appearance.SupportLinks) > 0 { + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: "Support links cannot be dynamically updated, use the config file instead.", + }) + return + } + if appearance.ServiceBanner.Enabled { if err := validateHexColor(appearance.ServiceBanner.BackgroundColor); err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ diff --git a/enterprise/coderd/appearance_test.go b/enterprise/coderd/appearance_test.go index a1192c784aa4d..b1f517ed2bff5 100644 --- a/enterprise/coderd/appearance_test.go +++ b/enterprise/coderd/appearance_test.go @@ -43,6 +43,8 @@ func TestServiceBanners(t *testing.T) { basicUserClient, _ := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID) + sb.SupportLinks = nil // clean "support links" as they can't be modified using API + // Regular user should be unable to set the banner sb.ServiceBanner.Enabled = true err = basicUserClient.UpdateAppearance(ctx, sb) @@ -60,6 +62,7 @@ func TestServiceBanners(t *testing.T) { require.NoError(t, err) gotBanner, err := adminClient.Appearance(ctx) require.NoError(t, err) + gotBanner.SupportLinks = nil // clean "support links" before comparison require.Equal(t, wantBanner, gotBanner) // But even an admin can't give a bad color @@ -67,3 +70,69 @@ func TestServiceBanners(t *testing.T) { err = adminClient.UpdateAppearance(ctx, wantBanner) require.Error(t, err) } + +func TestCustomSupportLinks(t *testing.T) { + t.Parallel() + + supportLinks := []codersdk.LinkConfig{ + { + Name: "First link", + Target: "http://first-link-1", + Icon: "chat", + }, + { + Name: "Second link", + Target: "http://second-link-2", + Icon: "bug", + }, + } + cfg := coderdtest.DeploymentConfig(t) + cfg.Support = new(codersdk.SupportConfig) + cfg.Support.Links = &codersdk.DeploymentConfigField[[]codersdk.LinkConfig]{ + Value: supportLinks, + } + + client := coderdenttest.New(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + DeploymentConfig: cfg, + }, + }) + coderdtest.CreateFirstUser(t, client) + coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureAppearance: 1, + }, + }) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + defer cancel() + + appearance, err := client.Appearance(ctx) + require.NoError(t, err) + + require.Len(t, appearance.SupportLinks, 2) + require.Equal(t, supportLinks, appearance.SupportLinks) +} + +func TestDefaultSupportLinks(t *testing.T) { + t.Parallel() + + client := coderdenttest.New(t, nil) + coderdtest.CreateFirstUser(t, client) + coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureAppearance: 1, + }, + }) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + defer cancel() + + appearance, err := client.Appearance(ctx) + require.NoError(t, err) + + require.Len(t, appearance.SupportLinks, 3) // Documentation, Report a bug, Join the Coder Discord + require.Equal(t, appearance.SupportLinks[0].Name, "Documentation") + require.Equal(t, appearance.SupportLinks[1].Name, "Report a bug") + require.Equal(t, appearance.SupportLinks[2].Name, "Join the Coder Discord") +} From b36838f7f7958fe9a4afa68c1fc3a8bd4bcbacc4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 23 Feb 2023 16:40:32 -0700 Subject: [PATCH 06/13] Use UpdateAppearanceConfig --- coderd/apidoc/docs.go | 15 ++++++++++++-- coderd/apidoc/swagger.json | 15 ++++++++++++-- codersdk/deployment.go | 7 ++++++- docs/api/enterprise.md | 30 ++++++++-------------------- docs/api/schemas.md | 20 +++++++++++++++++++ enterprise/coderd/appearance.go | 13 +++--------- enterprise/coderd/appearance_test.go | 14 +++++++------ site/src/api/typesGenerated.ts | 6 ++++++ 8 files changed, 77 insertions(+), 43 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 35def522b54cd..5912070e2e322 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -93,7 +93,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.AppearanceConfig" + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" } } ], @@ -101,7 +101,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AppearanceConfig" + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" } } } @@ -7864,6 +7864,17 @@ const docTemplate = `{ } } }, + "codersdk.UpdateAppearanceConfig": { + "type": "object", + "properties": { + "logo_url": { + "type": "string" + }, + "service_banner": { + "$ref": "#/definitions/codersdk.ServiceBannerConfig" + } + } + }, "codersdk.UpdateCheckResponse": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 6c3d52f027e22..b21f7e6b86166 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -71,7 +71,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.AppearanceConfig" + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" } } ], @@ -79,7 +79,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.AppearanceConfig" + "$ref": "#/definitions/codersdk.UpdateAppearanceConfig" } } } @@ -7080,6 +7080,17 @@ } } }, + "codersdk.UpdateAppearanceConfig": { + "type": "object", + "properties": { + "logo_url": { + "type": "string" + }, + "service_banner": { + "$ref": "#/definitions/codersdk.ServiceBannerConfig" + } + } + }, "codersdk.UpdateCheckResponse": { "type": "object", "properties": { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 688d2230f9107..7fb80303e6b56 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -363,6 +363,11 @@ type AppearanceConfig struct { SupportLinks []LinkConfig `json:"support_links,omitempty"` } +type UpdateAppearanceConfig struct { + LogoURL string `json:"logo_url"` + ServiceBanner ServiceBannerConfig `json:"service_banner"` +} + type ServiceBannerConfig struct { Enabled bool `json:"enabled"` Message string `json:"message,omitempty"` @@ -384,7 +389,7 @@ func (c *Client) Appearance(ctx context.Context) (AppearanceConfig, error) { return cfg, json.NewDecoder(res.Body).Decode(&cfg) } -func (c *Client) UpdateAppearance(ctx context.Context, appearance AppearanceConfig) error { +func (c *Client) UpdateAppearance(ctx context.Context, appearance UpdateAppearanceConfig) error { res, err := c.Request(ctx, http.MethodPut, "/api/v2/appearance", appearance) if err != nil { return err diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index 3c6185c16b9dc..87eae9c4f57c1 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -66,22 +66,15 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \ "background_color": "string", "enabled": true, "message": "string" - }, - "support_links": [ - { - "icon": "string", - "name": "string", - "target": "string" - } - ] + } } ``` ### Parameters -| Name | In | Type | Required | Description | -| ------ | ---- | ---------------------------------------------------------------- | -------- | ------------------------- | -| `body` | body | [codersdk.AppearanceConfig](schemas.md#codersdkappearanceconfig) | true | Update appearance request | +| Name | In | Type | Required | Description | +| ------ | ---- | ---------------------------------------------------------------------------- | -------- | ------------------------- | +| `body` | body | [codersdk.UpdateAppearanceConfig](schemas.md#codersdkupdateappearanceconfig) | true | Update appearance request | ### Example responses @@ -94,22 +87,15 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \ "background_color": "string", "enabled": true, "message": "string" - }, - "support_links": [ - { - "icon": "string", - "name": "string", - "target": "string" - } - ] + } } ``` ### Responses -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.AppearanceConfig](schemas.md#codersdkappearanceconfig) | +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UpdateAppearanceConfig](schemas.md#codersdkupdateappearanceconfig) | To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 6ae541c71dc9b..9ca068a81aacb 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -4887,6 +4887,26 @@ Parameter represents a set value for the scope. | ---- | ------ | -------- | ------------ | ----------- | | `id` | string | true | | | +## codersdk.UpdateAppearanceConfig + +```json +{ + "logo_url": "string", + "service_banner": { + "background_color": "string", + "enabled": true, + "message": "string" + } +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------------------------------------------------------------ | -------- | ------------ | ----------- | +| `logo_url` | string | false | | | +| `service_banner` | [codersdk.ServiceBannerConfig](#codersdkservicebannerconfig) | false | | | + ## codersdk.UpdateCheckResponse ```json diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index 1d488c60abf87..50d8587a6f205 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -113,8 +113,8 @@ func validateHexColor(color string) error { // @Accept json // @Produce json // @Tags Enterprise -// @Param request body codersdk.AppearanceConfig true "Update appearance request" -// @Success 200 {object} codersdk.AppearanceConfig +// @Param request body codersdk.UpdateAppearanceConfig true "Update appearance request" +// @Success 200 {object} codersdk.UpdateAppearanceConfig // @Router /appearance [put] func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -126,18 +126,11 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) { return } - var appearance codersdk.AppearanceConfig + var appearance codersdk.UpdateAppearanceConfig if !httpapi.Read(ctx, rw, r, &appearance) { return } - if len(appearance.SupportLinks) > 0 { - httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ - Message: "Support links cannot be dynamically updated, use the config file instead.", - }) - return - } - if appearance.ServiceBanner.Enabled { if err := validateHexColor(appearance.ServiceBanner.BackgroundColor); err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ diff --git a/enterprise/coderd/appearance_test.go b/enterprise/coderd/appearance_test.go index b1f517ed2bff5..b7323b57c397c 100644 --- a/enterprise/coderd/appearance_test.go +++ b/enterprise/coderd/appearance_test.go @@ -43,18 +43,20 @@ func TestServiceBanners(t *testing.T) { basicUserClient, _ := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID) - sb.SupportLinks = nil // clean "support links" as they can't be modified using API - + uac := codersdk.UpdateAppearanceConfig{ + ServiceBanner: sb.ServiceBanner, + } // Regular user should be unable to set the banner - sb.ServiceBanner.Enabled = true - err = basicUserClient.UpdateAppearance(ctx, sb) + uac.ServiceBanner.Enabled = true + + err = basicUserClient.UpdateAppearance(ctx, uac) require.Error(t, err) var sdkError *codersdk.Error require.True(t, errors.As(err, &sdkError)) require.Equal(t, http.StatusForbidden, sdkError.StatusCode()) // But an admin can - wantBanner := sb + wantBanner := uac wantBanner.ServiceBanner.Enabled = true wantBanner.ServiceBanner.Message = "Hey" wantBanner.ServiceBanner.BackgroundColor = "#00FF00" @@ -63,7 +65,7 @@ func TestServiceBanners(t *testing.T) { gotBanner, err := adminClient.Appearance(ctx) require.NoError(t, err) gotBanner.SupportLinks = nil // clean "support links" before comparison - require.Equal(t, wantBanner, gotBanner) + require.Equal(t, wantBanner.ServiceBanner, gotBanner.ServiceBanner) // But even an admin can't give a bad color wantBanner.ServiceBanner.BackgroundColor = "#bad color" diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 102c6a97a7030..46b1c19de5c71 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -827,6 +827,12 @@ export interface UpdateActiveTemplateVersion { readonly id: string } +// From codersdk/deployment.go +export interface UpdateAppearanceConfig { + readonly logo_url: string + readonly service_banner: ServiceBannerConfig +} + // From codersdk/updatecheck.go export interface UpdateCheckResponse { readonly current: boolean From b766773338565922791c754c061ae1935a0166f8 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 23 Feb 2023 17:06:05 -0700 Subject: [PATCH 07/13] ui: UpdateAppearanceConfig --- enterprise/coderd/appearance.go | 2 +- .../AppearanceSettingsPage/AppearanceSettingsPage.tsx | 4 ++-- .../AppearanceSettingsPage/AppearanceSettingsPageView.tsx | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index 50d8587a6f205..3f45fd7a96564 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -23,7 +23,7 @@ var defaultSupportLinks = []codersdk.LinkConfig{ }, { Name: "Report a bug", - Target: "https://github.com/coder/coder/issues/new?labels=needs+grooming&body=TODO", + Target: "https://github.com/coder/coder/issues/new?labels=needs+grooming&body={CODER_BUILD_INFO}", Icon: "bug", }, { diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx index a39ac1b2e37e0..c84751223f0db 100644 --- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx @@ -1,4 +1,4 @@ -import { AppearanceConfig } from "api/typesGenerated" +import { UpdateAppearanceConfig } from "api/typesGenerated" import { useDashboard } from "components/Dashboard/DashboardProvider" import { FC } from "react" import { Helmet } from "react-helmet-async" @@ -15,7 +15,7 @@ const AppearanceSettingsPage: FC = () => { entitlements.features["appearance"].entitlement !== "not_entitled" const updateAppearance = ( - newConfig: Partial, + newConfig: Partial, preview: boolean, ) => { const newAppearance = { diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx index 14b4bad0827a0..a01ff820a2526 100644 --- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx +++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx @@ -17,16 +17,16 @@ import { useTranslation } from "react-i18next" import makeStyles from "@material-ui/core/styles/makeStyles" import Switch from "@material-ui/core/Switch" import TextField from "@material-ui/core/TextField" -import { AppearanceConfig } from "api/typesGenerated" +import { UpdateAppearanceConfig } from "api/typesGenerated" import { Stack } from "components/Stack/Stack" import { useFormik } from "formik" import { useTheme } from "@material-ui/core/styles" export type AppearanceSettingsPageViewProps = { - appearance: AppearanceConfig + appearance: UpdateAppearanceConfig isEntitled: boolean updateAppearance: ( - newConfig: Partial, + newConfig: Partial, preview: boolean, ) => void } @@ -48,7 +48,7 @@ export const AppearanceSettingsPageView = ({ }) const logoFieldHelpers = getFormHelpers(logoForm) - const serviceBannerForm = useFormik({ + const serviceBannerForm = useFormik({ initialValues: { message: appearance.service_banner.message, enabled: appearance.service_banner.enabled, From caadba7e2ee5a7df512fcd13913c08c7563cc319 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 23 Feb 2023 17:10:49 -0700 Subject: [PATCH 08/13] fix: fmt --- .../AppearanceSettingsPageView.tsx | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx index a01ff820a2526..f64850d836c68 100644 --- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx +++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx @@ -48,20 +48,22 @@ export const AppearanceSettingsPageView = ({ }) const logoFieldHelpers = getFormHelpers(logoForm) - const serviceBannerForm = useFormik({ - initialValues: { - message: appearance.service_banner.message, - enabled: appearance.service_banner.enabled, - background_color: appearance.service_banner.background_color, + const serviceBannerForm = useFormik( + { + initialValues: { + message: appearance.service_banner.message, + enabled: appearance.service_banner.enabled, + background_color: appearance.service_banner.background_color, + }, + onSubmit: (values) => + updateAppearance( + { + service_banner: values, + }, + false, + ), }, - onSubmit: (values) => - updateAppearance( - { - service_banner: values, - }, - false, - ), - }) + ) const serviceBannerFieldHelpers = getFormHelpers(serviceBannerForm) const [backgroundColor, setBackgroundColor] = useState( serviceBannerForm.values.background_color, From 9568256664779e23c043d158f848792ef27f9b8d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 23 Feb 2023 17:57:21 -0700 Subject: [PATCH 09/13] Fix: site --- .../UserDropdownContent.test.tsx | 9 +++++---- .../UserDropdownContent.tsx | 3 --- site/src/testHelpers/entities.ts | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx index 0e9442e20a0c7..767b5c463c80a 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx @@ -1,5 +1,5 @@ import { screen } from "@testing-library/react" -import { MockBuildInfo, MockUser } from "../../testHelpers/entities" +import { MockBuildInfo, MockSupportLinks, MockUser } from "../../testHelpers/entities" import { render } from "../../testHelpers/renderHelpers" import { Language, UserDropdownContent } from "./UserDropdownContent" @@ -21,16 +21,17 @@ describe("UserDropdownContent", () => { , ) expect(screen.getByText(Language.accountLabel)).toBeDefined() - expect(screen.getByText(Language.docsLabel)).toBeDefined() expect(screen.getByText(Language.signOutLabel)).toBeDefined() - expect(screen.getByText(Language.bugLabel)).toBeDefined() - expect(screen.getByText(Language.discordLabel)).toBeDefined() expect(screen.getByText(Language.copyrightText)).toBeDefined() + expect(screen.getByText(MockSupportLinks[0].name)).toBeDefined() + expect(screen.getByText(MockSupportLinks[1].name)).toBeDefined() + expect(screen.getByText(MockSupportLinks[2].name)).toBeDefined() expect(screen.getByText(MockBuildInfo.version)).toBeDefined() }) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.tsx index 3b4fac601c03b..67c1b971a3be0 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.tsx @@ -15,10 +15,7 @@ import { combineClasses } from "util/combineClasses" export const Language = { accountLabel: "Account", - docsLabel: "Documentation", signOutLabel: "Sign Out", - bugLabel: "Report a Bug", - discordLabel: "Join the Coder Discord", copyrightText: `\u00a9 ${new Date().getFullYear()} Coder Technologies, Inc.`, } diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 8876bd169a7a2..b735dc8956a9b 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -60,6 +60,24 @@ export const MockBuildInfo: TypesGen.BuildInfoResponse = { version: "v99.999.9999+c9cdf14", } +export const MockSupportLinks: TypesGen.LinkConfig[] = [ + { + name: "First link", + target: "http://first-link", + icon: "chat" + }, + { + name: "Second link", + target: "http://second-link", + icon: "docs" + }, + { + name: "Third link", + target: "http://third-link", + icon: "" + } +]; + export const MockUpdateCheck: TypesGen.UpdateCheckResponse = { current: true, url: "file:///mock-url", From 81a1fcb5dd5fdb3ec7778b9bf6086c9faec3ccdb Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 23 Feb 2023 18:10:26 -0700 Subject: [PATCH 10/13] Fix: site tests --- .../UserDropdownContent/UserDropdownContent.test.tsx | 2 ++ .../components/UserDropdownContent/UserDropdownContent.tsx | 7 ++++++- site/src/testHelpers/entities.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx index 767b5c463c80a..f9a7fb2d3ee8c 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx @@ -32,6 +32,8 @@ describe("UserDropdownContent", () => { expect(screen.getByText(MockSupportLinks[0].name)).toBeDefined() expect(screen.getByText(MockSupportLinks[1].name)).toBeDefined() expect(screen.getByText(MockSupportLinks[2].name)).toBeDefined() + expect(screen.getByText(MockSupportLinks[2].name) + .closest("a")).toHaveAttribute("href", "https://github.com/coder/coder/issues/new?labels=needs+grooming&body=Version%3A%20%5B%60v99.999.9999%2Bc9cdf14%60%5D(file%3A%2F%2F%2Fmock-url)") expect(screen.getByText(MockBuildInfo.version)).toBeDefined() }) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.tsx index 67c1b971a3be0..99fbbc9ec38d8 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.tsx @@ -63,7 +63,7 @@ export const UserDropdownContent: FC = ({ {supportLinks && supportLinks.map((link) => ( ({ color: theme.palette.text.primary, }, })) + +const includeBuildInfo = (href: string, buildInfo?: TypesGen.BuildInfoResponse): string => { + return href.replace("{CODER_BUILD_INFO}", + `${encodeURIComponent(`Version: [\`${buildInfo?.version}\`](${buildInfo?.external_url})`)}`) +} diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index b735dc8956a9b..59a217683d990 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -73,7 +73,7 @@ export const MockSupportLinks: TypesGen.LinkConfig[] = [ }, { name: "Third link", - target: "http://third-link", + target: "https://github.com/coder/coder/issues/new?labels=needs+grooming&body={CODER_BUILD_INFO}", icon: "" } ]; From 06ebb8feaed08a9c74ae5663ab0022b0446c0374 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 23 Feb 2023 19:16:09 -0700 Subject: [PATCH 11/13] fix: fmt --- .../UserDropdownContent.test.tsx | 14 +++++++++++--- .../UserDropdownContent/UserDropdownContent.tsx | 13 ++++++++++--- site/src/testHelpers/entities.ts | 13 +++++++------ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx index f9a7fb2d3ee8c..3eb538c6b6921 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.test.tsx @@ -1,5 +1,9 @@ import { screen } from "@testing-library/react" -import { MockBuildInfo, MockSupportLinks, MockUser } from "../../testHelpers/entities" +import { + MockBuildInfo, + MockSupportLinks, + MockUser, +} from "../../testHelpers/entities" import { render } from "../../testHelpers/renderHelpers" import { Language, UserDropdownContent } from "./UserDropdownContent" @@ -32,8 +36,12 @@ describe("UserDropdownContent", () => { expect(screen.getByText(MockSupportLinks[0].name)).toBeDefined() expect(screen.getByText(MockSupportLinks[1].name)).toBeDefined() expect(screen.getByText(MockSupportLinks[2].name)).toBeDefined() - expect(screen.getByText(MockSupportLinks[2].name) - .closest("a")).toHaveAttribute("href", "https://github.com/coder/coder/issues/new?labels=needs+grooming&body=Version%3A%20%5B%60v99.999.9999%2Bc9cdf14%60%5D(file%3A%2F%2F%2Fmock-url)") + expect( + screen.getByText(MockSupportLinks[2].name).closest("a"), + ).toHaveAttribute( + "href", + "https://github.com/coder/coder/issues/new?labels=needs+grooming&body=Version%3A%20%5B%60v99.999.9999%2Bc9cdf14%60%5D(file%3A%2F%2F%2Fmock-url)", + ) expect(screen.getByText(MockBuildInfo.version)).toBeDefined() }) diff --git a/site/src/components/UserDropdownContent/UserDropdownContent.tsx b/site/src/components/UserDropdownContent/UserDropdownContent.tsx index 99fbbc9ec38d8..d51dfcab102f5 100644 --- a/site/src/components/UserDropdownContent/UserDropdownContent.tsx +++ b/site/src/components/UserDropdownContent/UserDropdownContent.tsx @@ -167,7 +167,14 @@ const useStyles = makeStyles((theme) => ({ }, })) -const includeBuildInfo = (href: string, buildInfo?: TypesGen.BuildInfoResponse): string => { - return href.replace("{CODER_BUILD_INFO}", - `${encodeURIComponent(`Version: [\`${buildInfo?.version}\`](${buildInfo?.external_url})`)}`) +const includeBuildInfo = ( + href: string, + buildInfo?: TypesGen.BuildInfoResponse, +): string => { + return href.replace( + "{CODER_BUILD_INFO}", + `${encodeURIComponent( + `Version: [\`${buildInfo?.version}\`](${buildInfo?.external_url})`, + )}`, + ) } diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 59a217683d990..8881682f94d38 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -64,19 +64,20 @@ export const MockSupportLinks: TypesGen.LinkConfig[] = [ { name: "First link", target: "http://first-link", - icon: "chat" + icon: "chat", }, { name: "Second link", target: "http://second-link", - icon: "docs" + icon: "docs", }, { name: "Third link", - target: "https://github.com/coder/coder/issues/new?labels=needs+grooming&body={CODER_BUILD_INFO}", - icon: "" - } -]; + target: + "https://github.com/coder/coder/issues/new?labels=needs+grooming&body={CODER_BUILD_INFO}", + icon: "", + }, +] export const MockUpdateCheck: TypesGen.UpdateCheckResponse = { current: true, From 9be1f98881b34a4c6eac736081f2fa0fe3f90cd6 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 23 Feb 2023 19:24:35 -0700 Subject: [PATCH 12/13] fix --- site/src/components/UserDropdown/UserDropdown.test.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/site/src/components/UserDropdown/UserDropdown.test.tsx b/site/src/components/UserDropdown/UserDropdown.test.tsx index c12a79a1da165..9c05202437cdc 100644 --- a/site/src/components/UserDropdown/UserDropdown.test.tsx +++ b/site/src/components/UserDropdown/UserDropdown.test.tsx @@ -1,5 +1,5 @@ import { fireEvent, screen } from "@testing-library/react" -import { MockUser } from "../../testHelpers/entities" +import { MockSupportLinks, MockUser } from "../../testHelpers/entities" import { render } from "../../testHelpers/renderHelpers" import { Language } from "../UserDropdownContent/UserDropdownContent" import { UserDropdown, UserDropdownProps } from "./UsersDropdown" @@ -8,6 +8,7 @@ const renderAndClick = async (props: Partial = {}) => { render( , ) @@ -20,7 +21,9 @@ describe("UserDropdown", () => { it("opens the menu", async () => { await renderAndClick() expect(screen.getByText(Language.accountLabel)).toBeDefined() - expect(screen.getByText(Language.docsLabel)).toBeDefined() + expect(screen.getByText(MockSupportLinks[0].name)).toBeDefined() + expect(screen.getByText(MockSupportLinks[1].name)).toBeDefined() + expect(screen.getByText(MockSupportLinks[2].name)).toBeDefined() expect(screen.getByText(Language.signOutLabel)).toBeDefined() }) }) From 3fe1bbc2a796295fe5a6f6c89d1ad1350cc2b6b6 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 24 Feb 2023 10:23:47 -0700 Subject: [PATCH 13/13] test: check default support links --- enterprise/coderd/appearance.go | 6 +++--- enterprise/coderd/appearance_test.go | 15 +++------------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index 3f45fd7a96564..7c230218e868f 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -15,7 +15,7 @@ import ( "github.com/coder/coder/codersdk" ) -var defaultSupportLinks = []codersdk.LinkConfig{ +var DefaultSupportLinks = []codersdk.LinkConfig{ { Name: "Documentation", Target: "https://coder.com/docs/coder-oss", @@ -49,7 +49,7 @@ func (api *API) appearance(rw http.ResponseWriter, r *http.Request) { if !isEntitled { httpapi.Write(ctx, rw, http.StatusOK, codersdk.AppearanceConfig{ - SupportLinks: defaultSupportLinks, + SupportLinks: DefaultSupportLinks, }) return } @@ -88,7 +88,7 @@ func (api *API) appearance(rw http.ResponseWriter, r *http.Request) { } if len(api.DeploymentConfig.Support.Links.Value) == 0 { - cfg.SupportLinks = defaultSupportLinks + cfg.SupportLinks = DefaultSupportLinks } else { cfg.SupportLinks = api.DeploymentConfig.Support.Links.Value } diff --git a/enterprise/coderd/appearance_test.go b/enterprise/coderd/appearance_test.go index b7323b57c397c..c1451c1193499 100644 --- a/enterprise/coderd/appearance_test.go +++ b/enterprise/coderd/appearance_test.go @@ -10,6 +10,7 @@ import ( "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/codersdk" + "github.com/coder/coder/enterprise/coderd" "github.com/coder/coder/enterprise/coderd/coderdenttest" "github.com/coder/coder/enterprise/coderd/license" "github.com/coder/coder/testutil" @@ -111,8 +112,6 @@ func TestCustomSupportLinks(t *testing.T) { appearance, err := client.Appearance(ctx) require.NoError(t, err) - - require.Len(t, appearance.SupportLinks, 2) require.Equal(t, supportLinks, appearance.SupportLinks) } @@ -121,20 +120,12 @@ func TestDefaultSupportLinks(t *testing.T) { client := coderdenttest.New(t, nil) coderdtest.CreateFirstUser(t, client) - coderdenttest.AddLicense(t, client, coderdenttest.LicenseOptions{ - Features: license.Features{ - codersdk.FeatureAppearance: 1, - }, - }) + // Don't need to set the license, as default links are passed without it. ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) defer cancel() appearance, err := client.Appearance(ctx) require.NoError(t, err) - - require.Len(t, appearance.SupportLinks, 3) // Documentation, Report a bug, Join the Coder Discord - require.Equal(t, appearance.SupportLinks[0].Name, "Documentation") - require.Equal(t, appearance.SupportLinks[1].Name, "Report a bug") - require.Equal(t, appearance.SupportLinks[2].Name, "Join the Coder Discord") + require.Equal(t, coderd.DefaultSupportLinks, appearance.SupportLinks) }