From f4320996645bacf17a4e15bad2a1eb4457b784d2 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 24 Apr 2024 17:19:39 +0000 Subject: [PATCH] bep --- coderd/apidoc/docs.go | 6 ++++ coderd/apidoc/swagger.json | 6 ++++ coderd/database/dbauthz/dbauthz.go | 12 +++++++ coderd/database/dbmem/dbmem.go | 8 +++++ coderd/database/dbmetrics/dbmetrics.go | 14 ++++++++ coderd/database/dbmock/dbmock.go | 29 ++++++++++++++++ coderd/database/querier.go | 2 ++ coderd/database/queries.sql.go | 21 ++++++++++++ coderd/database/queries/siteconfig.sql | 7 ++++ codersdk/deployment.go | 2 ++ docs/api/enterprise.md | 9 +++-- docs/api/schemas.md | 8 +++-- enterprise/coderd/appearance.go | 18 ++++++++++ site/src/api/api.ts | 3 ++ site/src/api/typesGenerated.ts | 2 ++ .../AppearanceSettingsPage.tsx | 1 + .../AppearanceSettingsPageView.stories.tsx | 1 + .../AppearanceSettingsPageView.tsx | 34 +++++++++++++++++++ site/src/pages/LoginPage/LoginPage.tsx | 3 ++ site/src/pages/LoginPage/LoginPageView.tsx | 6 ++++ site/src/testHelpers/entities.ts | 1 + 21 files changed, 188 insertions(+), 5 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 60ed16632c..1d80fc11c0 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8280,6 +8280,9 @@ const docTemplate = `{ "items": { "$ref": "#/definitions/codersdk.LinkConfig" } + }, + "terms_of_service": { + "type": "string" } } }, @@ -11896,6 +11899,9 @@ const docTemplate = `{ }, "service_banner": { "$ref": "#/definitions/codersdk.ServiceBannerConfig" + }, + "terms_of_service": { + "type": "string" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 785450be71..d413ecf96b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7349,6 +7349,9 @@ "items": { "$ref": "#/definitions/codersdk.LinkConfig" } + }, + "terms_of_service": { + "type": "string" } } }, @@ -10755,6 +10758,9 @@ }, "service_banner": { "$ref": "#/definitions/codersdk.ServiceBannerConfig" + }, + "terms_of_service": { + "type": "string" } } }, diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index a638b705a5..f6eaa102ea 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1793,6 +1793,11 @@ func (q *querier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTe return q.db.GetAuthorizedTemplates(ctx, arg, prep) } +func (q *querier) GetTermsOfService(ctx context.Context) (string, error) { + // No authz checks + return q.db.GetTermsOfService(ctx) +} + func (q *querier) GetUnexpiredLicenses(ctx context.Context) ([]database.License, error) { if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil { return nil, err @@ -3438,6 +3443,13 @@ func (q *querier) UpsertTemplateUsageStats(ctx context.Context) error { return q.db.UpsertTemplateUsageStats(ctx) } +func (q *querier) UpsertTermsOfService(ctx context.Context, value string) error { + if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceDeploymentValues); err != nil { + return err + } + return q.db.UpsertTermsOfService(ctx, value) +} + func (q *querier) UpsertWorkspaceAgentPortShare(ctx context.Context, arg database.UpsertWorkspaceAgentPortShareParams) (database.WorkspaceAgentPortShare, error) { workspace, err := q.db.GetWorkspaceByID(ctx, arg.WorkspaceID) if err != nil { diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index fcc3140133..f17292d4fc 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -4213,6 +4213,10 @@ func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.G return q.GetAuthorizedTemplates(ctx, arg, nil) } +func (q *FakeQuerier) GetTermsOfService(ctx context.Context) (string, error) { + panic("not implemented") +} + func (q *FakeQuerier) GetUnexpiredLicenses(_ context.Context) ([]database.License, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -8932,6 +8936,10 @@ TemplateUsageStatsInsertLoop: return nil } +func (q *FakeQuerier) UpsertTermsOfService(ctx context.Context, value string) error { + panic("not implemented") +} + func (q *FakeQuerier) UpsertWorkspaceAgentPortShare(_ context.Context, arg database.UpsertWorkspaceAgentPortShareParams) (database.WorkspaceAgentPortShare, error) { err := validateDatabaseType(arg) if err != nil { diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 4cb81c1ede..2224569c00 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -1038,6 +1038,13 @@ func (m metricsStore) GetTemplatesWithFilter(ctx context.Context, arg database.G return templates, err } +func (m metricsStore) GetTermsOfService(ctx context.Context) (string, error) { + start := time.Now() + r0, r1 := m.s.GetTermsOfService(ctx) + m.queryLatencies.WithLabelValues("GetTermsOfService").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) GetUnexpiredLicenses(ctx context.Context) ([]database.License, error) { start := time.Now() licenses, err := m.s.GetUnexpiredLicenses(ctx) @@ -2256,6 +2263,13 @@ func (m metricsStore) UpsertTemplateUsageStats(ctx context.Context) error { return r0 } +func (m metricsStore) UpsertTermsOfService(ctx context.Context, value string) error { + start := time.Now() + r0 := m.s.UpsertTermsOfService(ctx, value) + m.queryLatencies.WithLabelValues("UpsertTermsOfService").Observe(time.Since(start).Seconds()) + return r0 +} + func (m metricsStore) UpsertWorkspaceAgentPortShare(ctx context.Context, arg database.UpsertWorkspaceAgentPortShareParams) (database.WorkspaceAgentPortShare, error) { start := time.Now() r0, r1 := m.s.UpsertWorkspaceAgentPortShare(ctx, arg) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index 2bb62e8c92..0df1e9c853 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -2145,6 +2145,21 @@ func (mr *MockStoreMockRecorder) GetTemplatesWithFilter(arg0, arg1 any) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplatesWithFilter", reflect.TypeOf((*MockStore)(nil).GetTemplatesWithFilter), arg0, arg1) } +// GetTermsOfService mocks base method. +func (m *MockStore) GetTermsOfService(arg0 context.Context) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTermsOfService", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTermsOfService indicates an expected call of GetTermsOfService. +func (mr *MockStoreMockRecorder) GetTermsOfService(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTermsOfService", reflect.TypeOf((*MockStore)(nil).GetTermsOfService), arg0) +} + // GetUnexpiredLicenses mocks base method. func (m *MockStore) GetUnexpiredLicenses(arg0 context.Context) ([]database.License, error) { m.ctrl.T.Helper() @@ -4723,6 +4738,20 @@ func (mr *MockStoreMockRecorder) UpsertTemplateUsageStats(arg0 any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertTemplateUsageStats", reflect.TypeOf((*MockStore)(nil).UpsertTemplateUsageStats), arg0) } +// UpsertTermsOfService mocks base method. +func (m *MockStore) UpsertTermsOfService(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpsertTermsOfService", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpsertTermsOfService indicates an expected call of UpsertTermsOfService. +func (mr *MockStoreMockRecorder) UpsertTermsOfService(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertTermsOfService", reflect.TypeOf((*MockStore)(nil).UpsertTermsOfService), arg0, arg1) +} + // UpsertWorkspaceAgentPortShare mocks base method. func (m *MockStore) UpsertWorkspaceAgentPortShare(arg0 context.Context, arg1 database.UpsertWorkspaceAgentPortShareParams) (database.WorkspaceAgentPortShare, error) { m.ctrl.T.Helper() diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 7d8f504cb5..d4ee75a8cc 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -215,6 +215,7 @@ type sqlcQuerier interface { GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) GetTemplates(ctx context.Context) ([]Template, error) GetTemplatesWithFilter(ctx context.Context, arg GetTemplatesWithFilterParams) ([]Template, error) + GetTermsOfService(ctx context.Context) (string, error) GetUnexpiredLicenses(ctx context.Context) ([]License, error) // GetUserActivityInsights returns the ranking with top active users. // The result can be filtered on template_ids, meaning only user data @@ -435,6 +436,7 @@ type sqlcQuerier interface { // used to store the data, and the minutes are summed for each user and template // combination. The result is stored in the template_usage_stats table. UpsertTemplateUsageStats(ctx context.Context) error + UpsertTermsOfService(ctx context.Context, value string) error UpsertWorkspaceAgentPortShare(ctx context.Context, arg UpsertWorkspaceAgentPortShareParams) (WorkspaceAgentPortShare, error) } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 41171a7473..f28fa33d9a 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -5637,6 +5637,17 @@ func (q *sqlQuerier) GetServiceBanner(ctx context.Context) (string, error) { return value, err } +const getTermsOfService = `-- name: GetTermsOfService :one +SELECT value FROM site_configs WHERE key = 'terms_of_service' +` + +func (q *sqlQuerier) GetTermsOfService(ctx context.Context) (string, error) { + row := q.db.QueryRowContext(ctx, getTermsOfService) + var value string + err := row.Scan(&value) + return value, err +} + const insertDERPMeshKey = `-- name: InsertDERPMeshKey :exec INSERT INTO site_configs (key, value) VALUES ('derp_mesh_key', $1) ` @@ -5748,6 +5759,16 @@ func (q *sqlQuerier) UpsertServiceBanner(ctx context.Context, value string) erro return err } +const upsertTermsOfService = `-- name: UpsertTermsOfService :exec +INSERT INTO site_configs (key, value) VALUES ('terms_of_service', $1) +ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'terms_of_service' +` + +func (q *sqlQuerier) UpsertTermsOfService(ctx context.Context, value string) error { + _, err := q.db.ExecContext(ctx, upsertTermsOfService, value) + return err +} + const cleanTailnetCoordinators = `-- name: CleanTailnetCoordinators :exec DELETE FROM tailnet_coordinators diff --git a/coderd/database/queries/siteconfig.sql b/coderd/database/queries/siteconfig.sql index a432b71e3a..52145cc508 100644 --- a/coderd/database/queries/siteconfig.sql +++ b/coderd/database/queries/siteconfig.sql @@ -57,6 +57,13 @@ ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'application -- name: GetApplicationName :one SELECT value FROM site_configs WHERE key = 'application_name'; +-- name: UpsertTermsOfService :exec +INSERT INTO site_configs (key, value) VALUES ('terms_of_service', $1) +ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'terms_of_service'; + +-- name: GetTermsOfService :one +SELECT value FROM site_configs WHERE key = 'terms_of_service'; + -- name: GetAppSecurityKey :one SELECT value FROM site_configs WHERE key = 'app_signing_key'; diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 34eaa4edd4..273722b180 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -2078,6 +2078,7 @@ type AppearanceConfig struct { ApplicationName string `json:"application_name"` LogoURL string `json:"logo_url"` ServiceBanner ServiceBannerConfig `json:"service_banner"` + TermsOfService string `json:"terms_of_service"` SupportLinks []LinkConfig `json:"support_links,omitempty"` } @@ -2085,6 +2086,7 @@ type UpdateAppearanceConfig struct { ApplicationName string `json:"application_name"` LogoURL string `json:"logo_url"` ServiceBanner ServiceBannerConfig `json:"service_banner"` + TermsOfService string `json:"terms_of_service"` } type ServiceBannerConfig struct { diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index 0b05a9fffe..bd1e6f8a65 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -32,7 +32,8 @@ curl -X GET http://coder-server:8080/api/v2/appearance \ "name": "string", "target": "string" } - ] + ], + "terms_of_service": "string" } ``` @@ -68,7 +69,8 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \ "background_color": "string", "enabled": true, "message": "string" - } + }, + "terms_of_service": "string" } ``` @@ -90,7 +92,8 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \ "background_color": "string", "enabled": true, "message": "string" - } + }, + "terms_of_service": "string" } ``` diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 1d00ac18c3..85a90949c2 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -762,7 +762,8 @@ "name": "string", "target": "string" } - ] + ], + "terms_of_service": "string" } ``` @@ -774,6 +775,7 @@ | `logo_url` | string | false | | | | `service_banner` | [codersdk.ServiceBannerConfig](#codersdkservicebannerconfig) | false | | | | `support_links` | array of [codersdk.LinkConfig](#codersdklinkconfig) | false | | | +| `terms_of_service` | string | false | | | ## codersdk.ArchiveTemplateVersionsRequest @@ -5172,7 +5174,8 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "background_color": "string", "enabled": true, "message": "string" - } + }, + "terms_of_service": "string" } ``` @@ -5183,6 +5186,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o | `application_name` | string | false | | | | `logo_url` | string | false | | | | `service_banner` | [codersdk.ServiceBannerConfig](#codersdkservicebannerconfig) | false | | | +| `terms_of_service` | string | false | | | ## codersdk.UpdateCheckResponse diff --git a/enterprise/coderd/appearance.go b/enterprise/coderd/appearance.go index 70ef238d60..a608dda9a6 100644 --- a/enterprise/coderd/appearance.go +++ b/enterprise/coderd/appearance.go @@ -56,6 +56,7 @@ func (f *appearanceFetcher) Fetch(ctx context.Context) (codersdk.AppearanceConfi var applicationName string var logoURL string var serviceBannerJSON string + var termsOfService string eg.Go(func() (err error) { applicationName, err = f.database.GetApplicationName(ctx) if err != nil && !errors.Is(err, sql.ErrNoRows) { @@ -77,6 +78,13 @@ func (f *appearanceFetcher) Fetch(ctx context.Context) (codersdk.AppearanceConfi } return nil }) + eg.Go(func() (err error) { + termsOfService, err = f.database.GetTermsOfService(ctx) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return xerrors.Errorf("get terms of service: %w", err) + } + return nil + }) err := eg.Wait() if err != nil { return codersdk.AppearanceConfig{}, err @@ -85,6 +93,7 @@ func (f *appearanceFetcher) Fetch(ctx context.Context) (codersdk.AppearanceConfi cfg := codersdk.AppearanceConfig{ ApplicationName: applicationName, LogoURL: logoURL, + TermsOfService: termsOfService, } if serviceBannerJSON != "" { err = json.Unmarshal([]byte(serviceBannerJSON), &cfg.ServiceBanner) @@ -176,6 +185,15 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) { return } + err = api.Database.UpsertTermsOfService(ctx, appearance.TermsOfService) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Unable to set terms of service", + Detail: err.Error(), + }) + return + } + err = api.Database.UpsertLogoURL(ctx, appearance.LogoURL) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ diff --git a/site/src/api/api.ts b/site/src/api/api.ts index a243123a31..1559382948 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1282,6 +1282,8 @@ export const getAppearance = async (): Promise => { const response = await axios.get(`/api/v2/appearance`); return response.data || {}; } catch (ex) { + // This endpoint is only available on enterprise binaries. A 404 is expected + // from AGPL builds, and should be be treated as a "successful" response. if (axios.isAxiosError(ex) && ex.response?.status === 404) { return { application_name: "", @@ -1289,6 +1291,7 @@ export const getAppearance = async (): Promise => { service_banner: { enabled: false, }, + terms_of_service: "", }; } throw ex; diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 13971a0345..d40df83ea0 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -49,6 +49,7 @@ export interface AppearanceConfig { readonly application_name: string; readonly logo_url: string; readonly service_banner: ServiceBannerConfig; + readonly terms_of_service: string; readonly support_links?: readonly LinkConfig[]; } @@ -1279,6 +1280,7 @@ export interface UpdateAppearanceConfig { readonly application_name: string; readonly logo_url: string; readonly service_banner: ServiceBannerConfig; + readonly terms_of_service: string; } // From codersdk/updatecheck.go diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx index 2f05eacb9a..bf6c091a7e 100644 --- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx +++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPage.tsx @@ -22,6 +22,7 @@ const AppearanceSettingsPage: FC = () => { newConfig: Partial, preview: boolean, ) => { + console.log(newConfig); const newAppearance = { ...appearance.config, ...newConfig }; if (preview) { appearance.setPreview(newAppearance); diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.stories.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.stories.tsx index 60e0581ddc..03de9c0871 100644 --- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.stories.tsx +++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.stories.tsx @@ -13,6 +13,7 @@ const meta: Meta = { message: "hello world", background_color: "white", }, + terms_of_service: "", }, isEntitled: false, }, diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx index 784ccb94ac..a78670e5fb 100644 --- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx +++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx @@ -80,6 +80,16 @@ export const AppearanceSettingsPageView: FC< serviceBannerForm.values.background_color, ); + const termsOfServiceForm = useFormik<{ + terms_of_service: string; + }>({ + initialValues: { + terms_of_service: appearance.terms_of_service, + }, + onSubmit: (values) => onSaveAppearance(values, false), + }); + const termsOfServiceFieldHelpers = getFormHelpers(termsOfServiceForm); + return ( <>
)} + +
Submit} + > + +
); }; diff --git a/site/src/pages/LoginPage/LoginPage.tsx b/site/src/pages/LoginPage/LoginPage.tsx index 418a734efb..948a855b3b 100644 --- a/site/src/pages/LoginPage/LoginPage.tsx +++ b/site/src/pages/LoginPage/LoginPage.tsx @@ -8,6 +8,7 @@ import { useAuthContext } from "contexts/auth/AuthProvider"; import { getApplicationName } from "utils/appearance"; import { retrieveRedirect } from "utils/redirect"; import { LoginPageView } from "./LoginPageView"; +import { appearance } from "api/queries/appearance"; export const LoginPage: FC = () => { const location = useLocation(); @@ -24,6 +25,7 @@ export const LoginPage: FC = () => { const applicationName = getApplicationName(); const navigate = useNavigate(); const buildInfoQuery = useQuery(buildInfo()); + const siteConfigQuery = useQuery(appearance()); if (isSignedIn) { // If the redirect is going to a workspace application, and we @@ -68,6 +70,7 @@ export const LoginPage: FC = () => { error={signInError} isLoading={isLoading || authMethodsQuery.isLoading} buildInfo={buildInfoQuery.data} + termsOfService={siteConfigQuery.data?.terms_of_service} isSigningIn={isSigningIn} onSignIn={async ({ email, password }) => { await signIn(email, password); diff --git a/site/src/pages/LoginPage/LoginPageView.tsx b/site/src/pages/LoginPage/LoginPageView.tsx index cd585f3f1d..0ea0ab28f8 100644 --- a/site/src/pages/LoginPage/LoginPageView.tsx +++ b/site/src/pages/LoginPage/LoginPageView.tsx @@ -13,6 +13,7 @@ export interface LoginPageViewProps { error: unknown; isLoading: boolean; buildInfo?: BuildInfoResponse; + termsOfService?: string; isSigningIn: boolean; onSignIn: (credentials: { email: string; password: string }) => void; } @@ -22,6 +23,7 @@ export const LoginPageView: FC = ({ error, isLoading, buildInfo, + termsOfService, isSigningIn, onSignIn, }) => { @@ -49,6 +51,10 @@ export const LoginPageView: FC = ({ ); + if (termsOfService) { + return <>{termsOfService}; + } + return (
diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 3d9f837ab3..2d57224322 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -2347,6 +2347,7 @@ export const MockAppearanceConfig: TypesGen.AppearanceConfig = { service_banner: { enabled: false, }, + terms_of_service: "", }; export const MockWorkspaceBuildParameter1: TypesGen.WorkspaceBuildParameter = {