chore: rename notification banners to announcement banners (#13419)

This commit is contained in:
Kayla Washburn-Love
2024-05-31 10:59:28 -06:00
committed by GitHub
parent de8149fbfd
commit b248f125e1
42 changed files with 355 additions and 349 deletions
+14 -14
View File
@@ -176,7 +176,7 @@ func New(options Options) Agent {
ignorePorts: options.IgnorePorts,
portCacheDuration: options.PortCacheDuration,
reportMetadataInterval: options.ReportMetadataInterval,
notificationBannersRefreshInterval: options.ServiceBannerRefreshInterval,
announcementBannersRefreshInterval: options.ServiceBannerRefreshInterval,
sshMaxTimeout: options.SSHMaxTimeout,
subsystems: options.Subsystems,
addresses: options.Addresses,
@@ -193,7 +193,7 @@ func New(options Options) Agent {
// that gets closed on disconnection. This is used to wait for graceful disconnection from the
// coordinator during shut down.
close(a.coordDisconnected)
a.notificationBanners.Store(new([]codersdk.BannerConfig))
a.announcementBanners.Store(new([]codersdk.BannerConfig))
a.sessionToken.Store(new(string))
a.init()
return a
@@ -234,8 +234,8 @@ type agent struct {
manifest atomic.Pointer[agentsdk.Manifest] // manifest is atomic because values can change after reconnection.
reportMetadataInterval time.Duration
scriptRunner *agentscripts.Runner
notificationBanners atomic.Pointer[[]codersdk.BannerConfig] // notificationBanners is atomic because it is periodically updated.
notificationBannersRefreshInterval time.Duration
announcementBanners atomic.Pointer[[]codersdk.BannerConfig] // announcementBanners is atomic because it is periodically updated.
announcementBannersRefreshInterval time.Duration
sessionToken atomic.Pointer[string]
sshServer *agentssh.Server
sshMaxTimeout time.Duration
@@ -274,7 +274,7 @@ func (a *agent) init() {
sshSrv, err := agentssh.NewServer(a.hardCtx, a.logger.Named("ssh-server"), a.prometheusRegistry, a.filesystem, &agentssh.Config{
MaxTimeout: a.sshMaxTimeout,
MOTDFile: func() string { return a.manifest.Load().MOTDFile },
NotificationBanners: func() *[]codersdk.BannerConfig { return a.notificationBanners.Load() },
AnnouncementBanners: func() *[]codersdk.BannerConfig { return a.announcementBanners.Load() },
UpdateEnv: a.updateCommandEnv,
WorkingDirectory: func() string { return a.manifest.Load().Directory },
})
@@ -709,14 +709,14 @@ func (a *agent) setLifecycle(state codersdk.WorkspaceAgentLifecycle) {
// (and must be done before the session actually starts).
func (a *agent) fetchServiceBannerLoop(ctx context.Context, conn drpc.Conn) error {
aAPI := proto.NewDRPCAgentClient(conn)
ticker := time.NewTicker(a.notificationBannersRefreshInterval)
ticker := time.NewTicker(a.announcementBannersRefreshInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
bannersProto, err := aAPI.GetNotificationBanners(ctx, &proto.GetNotificationBannersRequest{})
bannersProto, err := aAPI.GetAnnouncementBanners(ctx, &proto.GetAnnouncementBannersRequest{})
if err != nil {
if ctx.Err() != nil {
return ctx.Err()
@@ -724,11 +724,11 @@ func (a *agent) fetchServiceBannerLoop(ctx context.Context, conn drpc.Conn) erro
a.logger.Error(ctx, "failed to update notification banners", slog.Error(err))
return err
}
banners := make([]codersdk.BannerConfig, 0, len(bannersProto.NotificationBanners))
for _, bannerProto := range bannersProto.NotificationBanners {
banners := make([]codersdk.BannerConfig, 0, len(bannersProto.AnnouncementBanners))
for _, bannerProto := range bannersProto.AnnouncementBanners {
banners = append(banners, agentsdk.BannerConfigFromProto(bannerProto))
}
a.notificationBanners.Store(&banners)
a.announcementBanners.Store(&banners)
}
}
}
@@ -763,15 +763,15 @@ func (a *agent) run() (retErr error) {
connMan.start("init notification banners", gracefulShutdownBehaviorStop,
func(ctx context.Context, conn drpc.Conn) error {
aAPI := proto.NewDRPCAgentClient(conn)
bannersProto, err := aAPI.GetNotificationBanners(ctx, &proto.GetNotificationBannersRequest{})
bannersProto, err := aAPI.GetAnnouncementBanners(ctx, &proto.GetAnnouncementBannersRequest{})
if err != nil {
return xerrors.Errorf("fetch service banner: %w", err)
}
banners := make([]codersdk.BannerConfig, 0, len(bannersProto.NotificationBanners))
for _, bannerProto := range bannersProto.NotificationBanners {
banners := make([]codersdk.BannerConfig, 0, len(bannersProto.AnnouncementBanners))
for _, bannerProto := range bannersProto.AnnouncementBanners {
banners = append(banners, agentsdk.BannerConfigFromProto(bannerProto))
}
a.notificationBanners.Store(&banners)
a.announcementBanners.Store(&banners)
return nil
},
)
+2 -2
View File
@@ -614,7 +614,7 @@ func TestAgent_Session_TTY_MOTD_Update(t *testing.T) {
// Set new banner func and wait for the agent to call it to update the
// banner.
ready := make(chan struct{}, 2)
client.SetNotificationBannersFunc(func() ([]codersdk.BannerConfig, error) {
client.SetAnnouncementBannersFunc(func() ([]codersdk.BannerConfig, error) {
select {
case ready <- struct{}{}:
default:
@@ -2200,7 +2200,7 @@ func setupSSHSession(
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
opts = append(opts, func(c *agenttest.Client, o *agent.Options) {
c.SetNotificationBannersFunc(func() ([]codersdk.BannerConfig, error) {
c.SetAnnouncementBannersFunc(func() ([]codersdk.BannerConfig, error) {
return []codersdk.BannerConfig{banner}, nil
})
})
+9 -9
View File
@@ -63,7 +63,7 @@ type Config struct {
// file will be displayed to the user upon login.
MOTDFile func() string
// ServiceBanner returns the configuration for the Coder service banner.
NotificationBanners func() *[]codersdk.BannerConfig
AnnouncementBanners func() *[]codersdk.BannerConfig
// UpdateEnv updates the environment variables for the command to be
// executed. It can be used to add, modify or replace environment variables.
UpdateEnv func(current []string) (updated []string, err error)
@@ -123,8 +123,8 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
if config.MOTDFile == nil {
config.MOTDFile = func() string { return "" }
}
if config.NotificationBanners == nil {
config.NotificationBanners = func() *[]codersdk.BannerConfig { return &[]codersdk.BannerConfig{} }
if config.AnnouncementBanners == nil {
config.AnnouncementBanners = func() *[]codersdk.BannerConfig { return &[]codersdk.BannerConfig{} }
}
if config.WorkingDirectory == nil {
config.WorkingDirectory = func() string {
@@ -441,13 +441,13 @@ func (s *Server) startPTYSession(logger slog.Logger, session ptySession, magicTy
session.DisablePTYEmulation()
if isLoginShell(session.RawCommand()) {
banners := s.config.NotificationBanners()
banners := s.config.AnnouncementBanners()
if banners != nil {
for _, banner := range *banners {
err := showNotificationBanner(session, banner)
err := showAnnouncementBanner(session, banner)
if err != nil {
logger.Error(ctx, "agent failed to show service banner", slog.Error(err))
s.metrics.sessionErrors.WithLabelValues(magicTypeLabel, "yes", "notification_banner").Add(1)
logger.Error(ctx, "agent failed to show announcement banner", slog.Error(err))
s.metrics.sessionErrors.WithLabelValues(magicTypeLabel, "yes", "announcement_banner").Add(1)
break
}
}
@@ -894,9 +894,9 @@ func isQuietLogin(fs afero.Fs, rawCommand string) bool {
return err == nil
}
// showNotificationBanner will write the service banner if enabled and not blank
// showAnnouncementBanner will write the service banner if enabled and not blank
// along with a blank line for spacing.
func showNotificationBanner(session io.Writer, banner codersdk.BannerConfig) error {
func showAnnouncementBanner(session io.Writer, banner codersdk.BannerConfig) error {
if banner.Enabled && banner.Message != "" {
// The banner supports Markdown so we might want to parse it but Markdown is
// still fairly readable in its raw form.
+10 -10
View File
@@ -138,8 +138,8 @@ func (c *Client) GetStartupLogs() []agentsdk.Log {
return c.logs
}
func (c *Client) SetNotificationBannersFunc(f func() ([]codersdk.ServiceBannerConfig, error)) {
c.fakeAgentAPI.SetNotificationBannersFunc(f)
func (c *Client) SetAnnouncementBannersFunc(f func() ([]codersdk.BannerConfig, error)) {
c.fakeAgentAPI.SetAnnouncementBannersFunc(f)
}
func (c *Client) PushDERPMapUpdate(update *tailcfg.DERPMap) error {
@@ -171,7 +171,7 @@ type FakeAgentAPI struct {
lifecycleStates []codersdk.WorkspaceAgentLifecycle
metadata map[string]agentsdk.Metadata
getNotificationBannersFunc func() ([]codersdk.BannerConfig, error)
getAnnouncementBannersFunc func() ([]codersdk.BannerConfig, error)
}
func (f *FakeAgentAPI) GetManifest(context.Context, *agentproto.GetManifestRequest) (*agentproto.Manifest, error) {
@@ -182,20 +182,20 @@ func (*FakeAgentAPI) GetServiceBanner(context.Context, *agentproto.GetServiceBan
return &agentproto.ServiceBanner{}, nil
}
func (f *FakeAgentAPI) SetNotificationBannersFunc(fn func() ([]codersdk.BannerConfig, error)) {
func (f *FakeAgentAPI) SetAnnouncementBannersFunc(fn func() ([]codersdk.BannerConfig, error)) {
f.Lock()
defer f.Unlock()
f.getNotificationBannersFunc = fn
f.getAnnouncementBannersFunc = fn
f.logger.Info(context.Background(), "updated notification banners")
}
func (f *FakeAgentAPI) GetNotificationBanners(context.Context, *agentproto.GetNotificationBannersRequest) (*agentproto.GetNotificationBannersResponse, error) {
func (f *FakeAgentAPI) GetAnnouncementBanners(context.Context, *agentproto.GetAnnouncementBannersRequest) (*agentproto.GetAnnouncementBannersResponse, error) {
f.Lock()
defer f.Unlock()
if f.getNotificationBannersFunc == nil {
return &agentproto.GetNotificationBannersResponse{NotificationBanners: []*agentproto.BannerConfig{}}, nil
if f.getAnnouncementBannersFunc == nil {
return &agentproto.GetAnnouncementBannersResponse{AnnouncementBanners: []*agentproto.BannerConfig{}}, nil
}
banners, err := f.getNotificationBannersFunc()
banners, err := f.getAnnouncementBannersFunc()
if err != nil {
return nil, err
}
@@ -203,7 +203,7 @@ func (f *FakeAgentAPI) GetNotificationBanners(context.Context, *agentproto.GetNo
for _, banner := range banners {
bannersProto = append(bannersProto, agentsdk.ProtoFromBannerConfig(banner))
}
return &agentproto.GetNotificationBannersResponse{NotificationBanners: bannersProto}, nil
return &agentproto.GetAnnouncementBannersResponse{AnnouncementBanners: bannersProto}, nil
}
func (f *FakeAgentAPI) UpdateStats(ctx context.Context, req *agentproto.UpdateStatsRequest) (*agentproto.UpdateStatsResponse, error) {
+38 -38
View File
@@ -1859,14 +1859,14 @@ func (x *BatchCreateLogsResponse) GetLogLimitExceeded() bool {
return false
}
type GetNotificationBannersRequest struct {
type GetAnnouncementBannersRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetNotificationBannersRequest) Reset() {
*x = GetNotificationBannersRequest{}
func (x *GetAnnouncementBannersRequest) Reset() {
*x = GetAnnouncementBannersRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_agent_proto_agent_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -1874,13 +1874,13 @@ func (x *GetNotificationBannersRequest) Reset() {
}
}
func (x *GetNotificationBannersRequest) String() string {
func (x *GetAnnouncementBannersRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetNotificationBannersRequest) ProtoMessage() {}
func (*GetAnnouncementBannersRequest) ProtoMessage() {}
func (x *GetNotificationBannersRequest) ProtoReflect() protoreflect.Message {
func (x *GetAnnouncementBannersRequest) ProtoReflect() protoreflect.Message {
mi := &file_agent_proto_agent_proto_msgTypes[22]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -1892,21 +1892,21 @@ func (x *GetNotificationBannersRequest) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use GetNotificationBannersRequest.ProtoReflect.Descriptor instead.
func (*GetNotificationBannersRequest) Descriptor() ([]byte, []int) {
// Deprecated: Use GetAnnouncementBannersRequest.ProtoReflect.Descriptor instead.
func (*GetAnnouncementBannersRequest) Descriptor() ([]byte, []int) {
return file_agent_proto_agent_proto_rawDescGZIP(), []int{22}
}
type GetNotificationBannersResponse struct {
type GetAnnouncementBannersResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
NotificationBanners []*BannerConfig `protobuf:"bytes,1,rep,name=notification_banners,json=notificationBanners,proto3" json:"notification_banners,omitempty"`
AnnouncementBanners []*BannerConfig `protobuf:"bytes,1,rep,name=announcement_banners,json=announcementBanners,proto3" json:"announcement_banners,omitempty"`
}
func (x *GetNotificationBannersResponse) Reset() {
*x = GetNotificationBannersResponse{}
func (x *GetAnnouncementBannersResponse) Reset() {
*x = GetAnnouncementBannersResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_agent_proto_agent_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -1914,13 +1914,13 @@ func (x *GetNotificationBannersResponse) Reset() {
}
}
func (x *GetNotificationBannersResponse) String() string {
func (x *GetAnnouncementBannersResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetNotificationBannersResponse) ProtoMessage() {}
func (*GetAnnouncementBannersResponse) ProtoMessage() {}
func (x *GetNotificationBannersResponse) ProtoReflect() protoreflect.Message {
func (x *GetAnnouncementBannersResponse) ProtoReflect() protoreflect.Message {
mi := &file_agent_proto_agent_proto_msgTypes[23]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@@ -1932,14 +1932,14 @@ func (x *GetNotificationBannersResponse) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
// Deprecated: Use GetNotificationBannersResponse.ProtoReflect.Descriptor instead.
func (*GetNotificationBannersResponse) Descriptor() ([]byte, []int) {
// Deprecated: Use GetAnnouncementBannersResponse.ProtoReflect.Descriptor instead.
func (*GetAnnouncementBannersResponse) Descriptor() ([]byte, []int) {
return file_agent_proto_agent_proto_rawDescGZIP(), []int{23}
}
func (x *GetNotificationBannersResponse) GetNotificationBanners() []*BannerConfig {
func (x *GetAnnouncementBannersResponse) GetAnnouncementBanners() []*BannerConfig {
if x != nil {
return x.NotificationBanners
return x.AnnouncementBanners
}
return nil
}
@@ -2742,16 +2742,16 @@ var file_agent_proto_agent_proto_rawDesc = []byte{
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69,
0x6d, 0x69, 0x74, 0x5f, 0x65, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x45, 0x78, 0x63, 0x65,
0x65, 0x64, 0x65, 0x64, 0x22, 0x1f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x14, 0x6e, 0x6f, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x18,
0x65, 0x64, 0x65, 0x64, 0x22, 0x1f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75,
0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f,
0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75,
0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x13, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x6d, 0x0a, 0x0c, 0x42, 0x61, 0x6e, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x13, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x6d, 0x0a, 0x0c, 0x42, 0x61, 0x6e, 0x6e,
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
@@ -2812,13 +2812,13 @@ var file_agent_proto_agent_proto_rawDesc = []byte{
0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x63,
0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61,
0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x69,
0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x12,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f,
0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x12,
0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e,
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
0x47, 0x65, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42,
0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27,
0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64,
0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x67, 0x65, 0x6e,
@@ -2869,8 +2869,8 @@ var file_agent_proto_agent_proto_goTypes = []interface{}{
(*Log)(nil), // 26: coder.agent.v2.Log
(*BatchCreateLogsRequest)(nil), // 27: coder.agent.v2.BatchCreateLogsRequest
(*BatchCreateLogsResponse)(nil), // 28: coder.agent.v2.BatchCreateLogsResponse
(*GetNotificationBannersRequest)(nil), // 29: coder.agent.v2.GetNotificationBannersRequest
(*GetNotificationBannersResponse)(nil), // 30: coder.agent.v2.GetNotificationBannersResponse
(*GetAnnouncementBannersRequest)(nil), // 29: coder.agent.v2.GetAnnouncementBannersRequest
(*GetAnnouncementBannersResponse)(nil), // 30: coder.agent.v2.GetAnnouncementBannersResponse
(*BannerConfig)(nil), // 31: coder.agent.v2.BannerConfig
(*WorkspaceApp_Healthcheck)(nil), // 32: coder.agent.v2.WorkspaceApp.Healthcheck
(*WorkspaceAgentMetadata_Result)(nil), // 33: coder.agent.v2.WorkspaceAgentMetadata.Result
@@ -2911,7 +2911,7 @@ var file_agent_proto_agent_proto_depIdxs = []int32{
42, // 23: coder.agent.v2.Log.created_at:type_name -> google.protobuf.Timestamp
6, // 24: coder.agent.v2.Log.level:type_name -> coder.agent.v2.Log.Level
26, // 25: coder.agent.v2.BatchCreateLogsRequest.logs:type_name -> coder.agent.v2.Log
31, // 26: coder.agent.v2.GetNotificationBannersResponse.notification_banners:type_name -> coder.agent.v2.BannerConfig
31, // 26: coder.agent.v2.GetAnnouncementBannersResponse.announcement_banners:type_name -> coder.agent.v2.BannerConfig
40, // 27: coder.agent.v2.WorkspaceApp.Healthcheck.interval:type_name -> google.protobuf.Duration
42, // 28: coder.agent.v2.WorkspaceAgentMetadata.Result.collected_at:type_name -> google.protobuf.Timestamp
40, // 29: coder.agent.v2.WorkspaceAgentMetadata.Description.interval:type_name -> google.protobuf.Duration
@@ -2927,7 +2927,7 @@ var file_agent_proto_agent_proto_depIdxs = []int32{
22, // 39: coder.agent.v2.Agent.UpdateStartup:input_type -> coder.agent.v2.UpdateStartupRequest
24, // 40: coder.agent.v2.Agent.BatchUpdateMetadata:input_type -> coder.agent.v2.BatchUpdateMetadataRequest
27, // 41: coder.agent.v2.Agent.BatchCreateLogs:input_type -> coder.agent.v2.BatchCreateLogsRequest
29, // 42: coder.agent.v2.Agent.GetNotificationBanners:input_type -> coder.agent.v2.GetNotificationBannersRequest
29, // 42: coder.agent.v2.Agent.GetAnnouncementBanners:input_type -> coder.agent.v2.GetAnnouncementBannersRequest
10, // 43: coder.agent.v2.Agent.GetManifest:output_type -> coder.agent.v2.Manifest
12, // 44: coder.agent.v2.Agent.GetServiceBanner:output_type -> coder.agent.v2.ServiceBanner
16, // 45: coder.agent.v2.Agent.UpdateStats:output_type -> coder.agent.v2.UpdateStatsResponse
@@ -2936,7 +2936,7 @@ var file_agent_proto_agent_proto_depIdxs = []int32{
21, // 48: coder.agent.v2.Agent.UpdateStartup:output_type -> coder.agent.v2.Startup
25, // 49: coder.agent.v2.Agent.BatchUpdateMetadata:output_type -> coder.agent.v2.BatchUpdateMetadataResponse
28, // 50: coder.agent.v2.Agent.BatchCreateLogs:output_type -> coder.agent.v2.BatchCreateLogsResponse
30, // 51: coder.agent.v2.Agent.GetNotificationBanners:output_type -> coder.agent.v2.GetNotificationBannersResponse
30, // 51: coder.agent.v2.Agent.GetAnnouncementBanners:output_type -> coder.agent.v2.GetAnnouncementBannersResponse
43, // [43:52] is the sub-list for method output_type
34, // [34:43] is the sub-list for method input_type
34, // [34:34] is the sub-list for extension type_name
@@ -3215,7 +3215,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetNotificationBannersRequest); i {
switch v := v.(*GetAnnouncementBannersRequest); i {
case 0:
return &v.state
case 1:
@@ -3227,7 +3227,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetNotificationBannersResponse); i {
switch v := v.(*GetAnnouncementBannersResponse); i {
case 0:
return &v.state
case 1:
+4 -4
View File
@@ -251,10 +251,10 @@ message BatchCreateLogsResponse {
bool log_limit_exceeded = 1;
}
message GetNotificationBannersRequest {}
message GetAnnouncementBannersRequest {}
message GetNotificationBannersResponse {
repeated BannerConfig notification_banners = 1;
message GetAnnouncementBannersResponse {
repeated BannerConfig announcement_banners = 1;
}
message BannerConfig {
@@ -272,5 +272,5 @@ service Agent {
rpc UpdateStartup(UpdateStartupRequest) returns (Startup);
rpc BatchUpdateMetadata(BatchUpdateMetadataRequest) returns (BatchUpdateMetadataResponse);
rpc BatchCreateLogs(BatchCreateLogsRequest) returns (BatchCreateLogsResponse);
rpc GetNotificationBanners(GetNotificationBannersRequest) returns (GetNotificationBannersResponse);
rpc GetAnnouncementBanners(GetAnnouncementBannersRequest) returns (GetAnnouncementBannersResponse);
}
+14 -14
View File
@@ -46,7 +46,7 @@ type DRPCAgentClient interface {
UpdateStartup(ctx context.Context, in *UpdateStartupRequest) (*Startup, error)
BatchUpdateMetadata(ctx context.Context, in *BatchUpdateMetadataRequest) (*BatchUpdateMetadataResponse, error)
BatchCreateLogs(ctx context.Context, in *BatchCreateLogsRequest) (*BatchCreateLogsResponse, error)
GetNotificationBanners(ctx context.Context, in *GetNotificationBannersRequest) (*GetNotificationBannersResponse, error)
GetAnnouncementBanners(ctx context.Context, in *GetAnnouncementBannersRequest) (*GetAnnouncementBannersResponse, error)
}
type drpcAgentClient struct {
@@ -131,9 +131,9 @@ func (c *drpcAgentClient) BatchCreateLogs(ctx context.Context, in *BatchCreateLo
return out, nil
}
func (c *drpcAgentClient) GetNotificationBanners(ctx context.Context, in *GetNotificationBannersRequest) (*GetNotificationBannersResponse, error) {
out := new(GetNotificationBannersResponse)
err := c.cc.Invoke(ctx, "/coder.agent.v2.Agent/GetNotificationBanners", drpcEncoding_File_agent_proto_agent_proto{}, in, out)
func (c *drpcAgentClient) GetAnnouncementBanners(ctx context.Context, in *GetAnnouncementBannersRequest) (*GetAnnouncementBannersResponse, error) {
out := new(GetAnnouncementBannersResponse)
err := c.cc.Invoke(ctx, "/coder.agent.v2.Agent/GetAnnouncementBanners", drpcEncoding_File_agent_proto_agent_proto{}, in, out)
if err != nil {
return nil, err
}
@@ -149,7 +149,7 @@ type DRPCAgentServer interface {
UpdateStartup(context.Context, *UpdateStartupRequest) (*Startup, error)
BatchUpdateMetadata(context.Context, *BatchUpdateMetadataRequest) (*BatchUpdateMetadataResponse, error)
BatchCreateLogs(context.Context, *BatchCreateLogsRequest) (*BatchCreateLogsResponse, error)
GetNotificationBanners(context.Context, *GetNotificationBannersRequest) (*GetNotificationBannersResponse, error)
GetAnnouncementBanners(context.Context, *GetAnnouncementBannersRequest) (*GetAnnouncementBannersResponse, error)
}
type DRPCAgentUnimplementedServer struct{}
@@ -186,7 +186,7 @@ func (s *DRPCAgentUnimplementedServer) BatchCreateLogs(context.Context, *BatchCr
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCAgentUnimplementedServer) GetNotificationBanners(context.Context, *GetNotificationBannersRequest) (*GetNotificationBannersResponse, error) {
func (s *DRPCAgentUnimplementedServer) GetAnnouncementBanners(context.Context, *GetAnnouncementBannersRequest) (*GetAnnouncementBannersResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
@@ -269,14 +269,14 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
)
}, DRPCAgentServer.BatchCreateLogs, true
case 8:
return "/coder.agent.v2.Agent/GetNotificationBanners", drpcEncoding_File_agent_proto_agent_proto{},
return "/coder.agent.v2.Agent/GetAnnouncementBanners", drpcEncoding_File_agent_proto_agent_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCAgentServer).
GetNotificationBanners(
GetAnnouncementBanners(
ctx,
in1.(*GetNotificationBannersRequest),
in1.(*GetAnnouncementBannersRequest),
)
}, DRPCAgentServer.GetNotificationBanners, true
}, DRPCAgentServer.GetAnnouncementBanners, true
default:
return "", nil, nil, nil, false
}
@@ -414,16 +414,16 @@ func (x *drpcAgent_BatchCreateLogsStream) SendAndClose(m *BatchCreateLogsRespons
return x.CloseSend()
}
type DRPCAgent_GetNotificationBannersStream interface {
type DRPCAgent_GetAnnouncementBannersStream interface {
drpc.Stream
SendAndClose(*GetNotificationBannersResponse) error
SendAndClose(*GetAnnouncementBannersResponse) error
}
type drpcAgent_GetNotificationBannersStream struct {
type drpcAgent_GetAnnouncementBannersStream struct {
drpc.Stream
}
func (x *drpcAgent_GetNotificationBannersStream) SendAndClose(m *GetNotificationBannersResponse) error {
func (x *drpcAgent_GetAnnouncementBannersStream) SendAndClose(m *GetAnnouncementBannersResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_agent_proto_agent_proto{}); err != nil {
return err
}
@@ -11,12 +11,12 @@ import (
"github.com/coder/coder/v2/codersdk/agentsdk"
)
type NotificationBannerAPI struct {
type AnnouncementBannerAPI struct {
appearanceFetcher *atomic.Pointer[appearance.Fetcher]
}
// Deprecated: GetServiceBanner has been deprecated in favor of GetNotificationBanners.
func (a *NotificationBannerAPI) GetServiceBanner(ctx context.Context, _ *proto.GetServiceBannerRequest) (*proto.ServiceBanner, error) {
// Deprecated: GetServiceBanner has been deprecated in favor of GetAnnouncementBanners.
func (a *AnnouncementBannerAPI) GetServiceBanner(ctx context.Context, _ *proto.GetServiceBannerRequest) (*proto.ServiceBanner, error) {
cfg, err := (*a.appearanceFetcher.Load()).Fetch(ctx)
if err != nil {
return nil, xerrors.Errorf("fetch appearance: %w", err)
@@ -24,16 +24,16 @@ func (a *NotificationBannerAPI) GetServiceBanner(ctx context.Context, _ *proto.G
return agentsdk.ProtoFromServiceBanner(cfg.ServiceBanner), nil
}
func (a *NotificationBannerAPI) GetNotificationBanners(ctx context.Context, _ *proto.GetNotificationBannersRequest) (*proto.GetNotificationBannersResponse, error) {
func (a *AnnouncementBannerAPI) GetAnnouncementBanners(ctx context.Context, _ *proto.GetAnnouncementBannersRequest) (*proto.GetAnnouncementBannersResponse, error) {
cfg, err := (*a.appearanceFetcher.Load()).Fetch(ctx)
if err != nil {
return nil, xerrors.Errorf("fetch appearance: %w", err)
}
banners := make([]*proto.BannerConfig, 0, len(cfg.NotificationBanners))
for _, banner := range cfg.NotificationBanners {
banners := make([]*proto.BannerConfig, 0, len(cfg.AnnouncementBanners))
for _, banner := range cfg.AnnouncementBanners {
banners = append(banners, agentsdk.ProtoFromBannerConfig(banner))
}
return &proto.GetNotificationBannersResponse{
NotificationBanners: banners,
return &proto.GetAnnouncementBannersResponse{
AnnouncementBanners: banners,
}, nil
}
@@ -14,7 +14,7 @@ import (
"github.com/coder/coder/v2/codersdk/agentsdk"
)
func TestGetNotificationBanners(t *testing.T) {
func TestGetAnnouncementBanners(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
@@ -26,15 +26,15 @@ func TestGetNotificationBanners(t *testing.T) {
BackgroundColor: "#00FF00",
}}
var ff appearance.Fetcher = fakeFetcher{cfg: codersdk.AppearanceConfig{NotificationBanners: cfg}}
var ff appearance.Fetcher = fakeFetcher{cfg: codersdk.AppearanceConfig{AnnouncementBanners: cfg}}
ptr := atomic.Pointer[appearance.Fetcher]{}
ptr.Store(&ff)
api := &NotificationBannerAPI{appearanceFetcher: &ptr}
resp, err := api.GetNotificationBanners(context.Background(), &agentproto.GetNotificationBannersRequest{})
api := &AnnouncementBannerAPI{appearanceFetcher: &ptr}
resp, err := api.GetAnnouncementBanners(context.Background(), &agentproto.GetAnnouncementBannersRequest{})
require.NoError(t, err)
require.Len(t, resp.NotificationBanners, 1)
require.Equal(t, cfg[0], agentsdk.BannerConfigFromProto(resp.NotificationBanners[0]))
require.Len(t, resp.AnnouncementBanners, 1)
require.Equal(t, cfg[0], agentsdk.BannerConfigFromProto(resp.AnnouncementBanners[0]))
})
t.Run("FetchError", func(t *testing.T) {
@@ -45,8 +45,8 @@ func TestGetNotificationBanners(t *testing.T) {
ptr := atomic.Pointer[appearance.Fetcher]{}
ptr.Store(&ff)
api := &NotificationBannerAPI{appearanceFetcher: &ptr}
resp, err := api.GetNotificationBanners(context.Background(), &agentproto.GetNotificationBannersRequest{})
api := &AnnouncementBannerAPI{appearanceFetcher: &ptr}
resp, err := api.GetAnnouncementBanners(context.Background(), &agentproto.GetAnnouncementBannersRequest{})
require.Error(t, err)
require.ErrorIs(t, err, expectedErr)
require.Nil(t, resp)
+2 -2
View File
@@ -36,7 +36,7 @@ import (
type API struct {
opts Options
*ManifestAPI
*NotificationBannerAPI
*AnnouncementBannerAPI
*StatsAPI
*LifecycleAPI
*AppsAPI
@@ -108,7 +108,7 @@ func New(opts Options) *API {
},
}
api.NotificationBannerAPI = &NotificationBannerAPI{
api.AnnouncementBannerAPI = &AnnouncementBannerAPI{
appearanceFetcher: opts.AppearanceFetcher,
}
+14 -14
View File
@@ -8378,20 +8378,20 @@ const docTemplate = `{
"codersdk.AppearanceConfig": {
"type": "object",
"properties": {
"announcement_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"application_name": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"notification_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"service_banner": {
"description": "Deprecated: ServiceBanner has been replaced by NotificationBanners.",
"description": "Deprecated: ServiceBanner has been replaced by AnnouncementBanners.",
"allOf": [
{
"$ref": "#/definitions/codersdk.BannerConfig"
@@ -12148,20 +12148,20 @@ const docTemplate = `{
"codersdk.UpdateAppearanceConfig": {
"type": "object",
"properties": {
"announcement_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"application_name": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"notification_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"service_banner": {
"description": "Deprecated: ServiceBanner has been replaced by NotificationBanners.",
"description": "Deprecated: ServiceBanner has been replaced by AnnouncementBanners.",
"allOf": [
{
"$ref": "#/definitions/codersdk.BannerConfig"
+14 -14
View File
@@ -7433,20 +7433,20 @@
"codersdk.AppearanceConfig": {
"type": "object",
"properties": {
"announcement_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"application_name": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"notification_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"service_banner": {
"description": "Deprecated: ServiceBanner has been replaced by NotificationBanners.",
"description": "Deprecated: ServiceBanner has been replaced by AnnouncementBanners.",
"allOf": [
{
"$ref": "#/definitions/codersdk.BannerConfig"
@@ -10997,20 +10997,20 @@
"codersdk.UpdateAppearanceConfig": {
"type": "object",
"properties": {
"announcement_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"application_name": {
"type": "string"
},
"logo_url": {
"type": "string"
},
"notification_banners": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.BannerConfig"
}
},
"service_banner": {
"description": "Deprecated: ServiceBanner has been replaced by NotificationBanners.",
"description": "Deprecated: ServiceBanner has been replaced by AnnouncementBanners.",
"allOf": [
{
"$ref": "#/definitions/codersdk.BannerConfig"
+1 -1
View File
@@ -32,7 +32,7 @@ type AGPLFetcher struct{}
func (AGPLFetcher) Fetch(context.Context) (codersdk.AppearanceConfig, error) {
return codersdk.AppearanceConfig{
NotificationBanners: []codersdk.BannerConfig{},
AnnouncementBanners: []codersdk.BannerConfig{},
SupportLinks: DefaultSupportLinks,
}, nil
}
+12 -12
View File
@@ -1142,6 +1142,11 @@ func (q *querier) GetAllTailnetTunnels(ctx context.Context) ([]database.TailnetT
return q.db.GetAllTailnetTunnels(ctx)
}
func (q *querier) GetAnnouncementBanners(ctx context.Context) (string, error) {
// No authz checks
return q.db.GetAnnouncementBanners(ctx)
}
func (q *querier) GetAppSecurityKey(ctx context.Context) (string, error) {
// No authz checks
return q.db.GetAppSecurityKey(ctx)
@@ -1359,11 +1364,6 @@ func (q *querier) GetLogoURL(ctx context.Context) (string, error) {
return q.db.GetLogoURL(ctx)
}
func (q *querier) GetNotificationBanners(ctx context.Context) (string, error) {
// No authz checks
return q.db.GetNotificationBanners(ctx)
}
func (q *querier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceOauth2App); err != nil {
return database.OAuth2ProviderApp{}, err
@@ -3405,6 +3405,13 @@ func (q *querier) UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Cont
return fetchAndExec(q.log, q.auth, policy.ActionUpdate, fetch, q.db.UpdateWorkspacesDormantDeletingAtByTemplateID)(ctx, arg)
}
func (q *querier) UpsertAnnouncementBanners(ctx context.Context, value string) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
return err
}
return q.db.UpsertAnnouncementBanners(ctx, value)
}
func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error {
// No authz checks as this is done during startup
return q.db.UpsertAppSecurityKey(ctx, data)
@@ -3538,13 +3545,6 @@ func (q *querier) UpsertLogoURL(ctx context.Context, value string) error {
return q.db.UpsertLogoURL(ctx, value)
}
func (q *querier) UpsertNotificationBanners(ctx context.Context, value string) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
return err
}
return q.db.UpsertNotificationBanners(ctx, value)
}
func (q *querier) UpsertOAuthSigningKey(ctx context.Context, value string) error {
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
return err
+3 -3
View File
@@ -528,7 +528,7 @@ func (s *MethodTestSuite) TestLicense() {
s.Run("UpsertLogoURL", s.Subtest(func(db database.Store, check *expects) {
check.Args("value").Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
}))
s.Run("UpsertNotificationBanners", s.Subtest(func(db database.Store, check *expects) {
s.Run("UpsertAnnouncementBanners", s.Subtest(func(db database.Store, check *expects) {
check.Args("value").Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
}))
s.Run("GetLicenseByID", s.Subtest(func(db database.Store, check *expects) {
@@ -559,8 +559,8 @@ func (s *MethodTestSuite) TestLicense() {
require.NoError(s.T(), err)
check.Args().Asserts().Returns("value")
}))
s.Run("GetNotificationBanners", s.Subtest(func(db database.Store, check *expects) {
err := db.UpsertNotificationBanners(context.Background(), "value")
s.Run("GetAnnouncementBanners", s.Subtest(func(db database.Store, check *expects) {
err := db.UpsertAnnouncementBanners(context.Background(), "value")
require.NoError(s.T(), err)
check.Args().Asserts().Returns("value")
}))
+20 -20
View File
@@ -191,7 +191,7 @@ type data struct {
deploymentID string
derpMeshKey string
lastUpdateCheck []byte
notificationBanners []byte
announcementBanners []byte
healthSettings []byte
applicationName string
logoURL string
@@ -1857,6 +1857,17 @@ func (*FakeQuerier) GetAllTailnetTunnels(context.Context) ([]database.TailnetTun
return nil, ErrUnimplemented
}
func (q *FakeQuerier) GetAnnouncementBanners(_ context.Context) (string, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
if q.announcementBanners == nil {
return "", sql.ErrNoRows
}
return string(q.announcementBanners), nil
}
func (q *FakeQuerier) GetAppSecurityKey(_ context.Context) (string, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
@@ -2540,17 +2551,6 @@ func (q *FakeQuerier) GetLogoURL(_ context.Context) (string, error) {
return q.logoURL, nil
}
func (q *FakeQuerier) GetNotificationBanners(_ context.Context) (string, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
if q.notificationBanners == nil {
return "", sql.ErrNoRows
}
return string(q.notificationBanners), nil
}
func (q *FakeQuerier) GetOAuth2ProviderAppByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
q.mutex.Lock()
defer q.mutex.Unlock()
@@ -8358,6 +8358,14 @@ func (q *FakeQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(_ context.Co
return nil
}
func (q *FakeQuerier) UpsertAnnouncementBanners(_ context.Context, data string) error {
q.mutex.RLock()
defer q.mutex.RUnlock()
q.announcementBanners = []byte(data)
return nil
}
func (q *FakeQuerier) UpsertAppSecurityKey(_ context.Context, data string) error {
q.mutex.Lock()
defer q.mutex.Unlock()
@@ -8472,14 +8480,6 @@ func (q *FakeQuerier) UpsertLogoURL(_ context.Context, data string) error {
return nil
}
func (q *FakeQuerier) UpsertNotificationBanners(_ context.Context, data string) error {
q.mutex.RLock()
defer q.mutex.RUnlock()
q.notificationBanners = []byte(data)
return nil
}
func (q *FakeQuerier) UpsertOAuthSigningKey(_ context.Context, value string) error {
q.mutex.Lock()
defer q.mutex.Unlock()
+14 -14
View File
@@ -431,6 +431,13 @@ func (m metricsStore) GetAllTailnetTunnels(ctx context.Context) ([]database.Tail
return r0, r1
}
func (m metricsStore) GetAnnouncementBanners(ctx context.Context) (string, error) {
start := time.Now()
r0, r1 := m.s.GetAnnouncementBanners(ctx)
m.queryLatencies.WithLabelValues("GetAnnouncementBanners").Observe(time.Since(start).Seconds())
return r0, r1
}
func (m metricsStore) GetAppSecurityKey(ctx context.Context) (string, error) {
start := time.Now()
key, err := m.s.GetAppSecurityKey(ctx)
@@ -662,13 +669,6 @@ func (m metricsStore) GetLogoURL(ctx context.Context) (string, error) {
return url, err
}
func (m metricsStore) GetNotificationBanners(ctx context.Context) (string, error) {
start := time.Now()
r0, r1 := m.s.GetNotificationBanners(ctx)
m.queryLatencies.WithLabelValues("GetNotificationBanners").Observe(time.Since(start).Seconds())
return r0, r1
}
func (m metricsStore) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
start := time.Now()
r0, r1 := m.s.GetOAuth2ProviderAppByID(ctx, id)
@@ -2174,6 +2174,13 @@ func (m metricsStore) UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.
return r0
}
func (m metricsStore) UpsertAnnouncementBanners(ctx context.Context, value string) error {
start := time.Now()
r0 := m.s.UpsertAnnouncementBanners(ctx, value)
m.queryLatencies.WithLabelValues("UpsertAnnouncementBanners").Observe(time.Since(start).Seconds())
return r0
}
func (m metricsStore) UpsertAppSecurityKey(ctx context.Context, value string) error {
start := time.Now()
r0 := m.s.UpsertAppSecurityKey(ctx, value)
@@ -2230,13 +2237,6 @@ func (m metricsStore) UpsertLogoURL(ctx context.Context, value string) error {
return r0
}
func (m metricsStore) UpsertNotificationBanners(ctx context.Context, value string) error {
start := time.Now()
r0 := m.s.UpsertNotificationBanners(ctx, value)
m.queryLatencies.WithLabelValues("UpsertNotificationBanners").Observe(time.Since(start).Seconds())
return r0
}
func (m metricsStore) UpsertOAuthSigningKey(ctx context.Context, value string) error {
start := time.Now()
r0 := m.s.UpsertOAuthSigningKey(ctx, value)
+29 -29
View File
@@ -764,6 +764,21 @@ func (mr *MockStoreMockRecorder) GetAllTailnetTunnels(arg0 any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllTailnetTunnels", reflect.TypeOf((*MockStore)(nil).GetAllTailnetTunnels), arg0)
}
// GetAnnouncementBanners mocks base method.
func (m *MockStore) GetAnnouncementBanners(arg0 context.Context) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAnnouncementBanners", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetAnnouncementBanners indicates an expected call of GetAnnouncementBanners.
func (mr *MockStoreMockRecorder) GetAnnouncementBanners(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAnnouncementBanners", reflect.TypeOf((*MockStore)(nil).GetAnnouncementBanners), arg0)
}
// GetAppSecurityKey mocks base method.
func (m *MockStore) GetAppSecurityKey(arg0 context.Context) (string, error) {
m.ctrl.T.Helper()
@@ -1304,21 +1319,6 @@ func (mr *MockStoreMockRecorder) GetLogoURL(arg0 any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogoURL", reflect.TypeOf((*MockStore)(nil).GetLogoURL), arg0)
}
// GetNotificationBanners mocks base method.
func (m *MockStore) GetNotificationBanners(arg0 context.Context) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetNotificationBanners", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetNotificationBanners indicates an expected call of GetNotificationBanners.
func (mr *MockStoreMockRecorder) GetNotificationBanners(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotificationBanners", reflect.TypeOf((*MockStore)(nil).GetNotificationBanners), arg0)
}
// GetOAuth2ProviderAppByID mocks base method.
func (m *MockStore) GetOAuth2ProviderAppByID(arg0 context.Context, arg1 uuid.UUID) (database.OAuth2ProviderApp, error) {
m.ctrl.T.Helper()
@@ -4553,6 +4553,20 @@ func (mr *MockStoreMockRecorder) UpdateWorkspacesDormantDeletingAtByTemplateID(a
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspacesDormantDeletingAtByTemplateID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspacesDormantDeletingAtByTemplateID), arg0, arg1)
}
// UpsertAnnouncementBanners mocks base method.
func (m *MockStore) UpsertAnnouncementBanners(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertAnnouncementBanners", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UpsertAnnouncementBanners indicates an expected call of UpsertAnnouncementBanners.
func (mr *MockStoreMockRecorder) UpsertAnnouncementBanners(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertAnnouncementBanners", reflect.TypeOf((*MockStore)(nil).UpsertAnnouncementBanners), arg0, arg1)
}
// UpsertAppSecurityKey mocks base method.
func (m *MockStore) UpsertAppSecurityKey(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
@@ -4666,20 +4680,6 @@ func (mr *MockStoreMockRecorder) UpsertLogoURL(arg0, arg1 any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertLogoURL", reflect.TypeOf((*MockStore)(nil).UpsertLogoURL), arg0, arg1)
}
// UpsertNotificationBanners mocks base method.
func (m *MockStore) UpsertNotificationBanners(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpsertNotificationBanners", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// UpsertNotificationBanners indicates an expected call of UpsertNotificationBanners.
func (mr *MockStoreMockRecorder) UpsertNotificationBanners(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertNotificationBanners", reflect.TypeOf((*MockStore)(nil).UpsertNotificationBanners), arg0, arg1)
}
// UpsertOAuthSigningKey mocks base method.
func (m *MockStore) UpsertOAuthSigningKey(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
@@ -0,0 +1,3 @@
update site_configs SET
key = 'notification_banners'
where key = 'announcement_banners';
@@ -0,0 +1,3 @@
update site_configs SET
key = 'announcement_banners'
where key = 'notification_banners';
+2 -2
View File
@@ -97,6 +97,7 @@ type sqlcQuerier interface {
GetAllTailnetCoordinators(ctx context.Context) ([]TailnetCoordinator, error)
GetAllTailnetPeers(ctx context.Context) ([]TailnetPeer, error)
GetAllTailnetTunnels(ctx context.Context) ([]TailnetTunnel, error)
GetAnnouncementBanners(ctx context.Context) (string, error)
GetAppSecurityKey(ctx context.Context) (string, error)
GetApplicationName(ctx context.Context) (string, error)
// GetAuditLogsBefore retrieves `row_limit` number of audit logs before the provided
@@ -137,7 +138,6 @@ type sqlcQuerier interface {
GetLicenseByID(ctx context.Context, id int32) (License, error)
GetLicenses(ctx context.Context) ([]License, error)
GetLogoURL(ctx context.Context) (string, error)
GetNotificationBanners(ctx context.Context) (string, error)
GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderApp, error)
GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error)
GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppCode, error)
@@ -416,6 +416,7 @@ type sqlcQuerier interface {
UpdateWorkspaceProxyDeleted(ctx context.Context, arg UpdateWorkspaceProxyDeletedParams) error
UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error
UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error
UpsertAnnouncementBanners(ctx context.Context, value string) error
UpsertAppSecurityKey(ctx context.Context, value string) error
UpsertApplicationName(ctx context.Context, value string) error
UpsertCustomRole(ctx context.Context, arg UpsertCustomRoleParams) (CustomRole, error)
@@ -427,7 +428,6 @@ type sqlcQuerier interface {
UpsertJFrogXrayScanByWorkspaceAndAgentID(ctx context.Context, arg UpsertJFrogXrayScanByWorkspaceAndAgentIDParams) error
UpsertLastUpdateCheck(ctx context.Context, value string) error
UpsertLogoURL(ctx context.Context, value string) error
UpsertNotificationBanners(ctx context.Context, value string) error
UpsertOAuthSigningKey(ctx context.Context, value string) error
UpsertProvisionerDaemon(ctx context.Context, arg UpsertProvisionerDaemonParams) (ProvisionerDaemon, error)
UpsertTailnetAgent(ctx context.Context, arg UpsertTailnetAgentParams) (TailnetAgent, error)
+21 -21
View File
@@ -5727,6 +5727,17 @@ func (q *sqlQuerier) UpsertCustomRole(ctx context.Context, arg UpsertCustomRoleP
return i, err
}
const getAnnouncementBanners = `-- name: GetAnnouncementBanners :one
SELECT value FROM site_configs WHERE key = 'announcement_banners'
`
func (q *sqlQuerier) GetAnnouncementBanners(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getAnnouncementBanners)
var value string
err := row.Scan(&value)
return value, err
}
const getAppSecurityKey = `-- name: GetAppSecurityKey :one
SELECT value FROM site_configs WHERE key = 'app_signing_key'
`
@@ -5823,17 +5834,6 @@ func (q *sqlQuerier) GetLogoURL(ctx context.Context) (string, error) {
return value, err
}
const getNotificationBanners = `-- name: GetNotificationBanners :one
SELECT value FROM site_configs WHERE key = 'notification_banners'
`
func (q *sqlQuerier) GetNotificationBanners(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getNotificationBanners)
var value string
err := row.Scan(&value)
return value, err
}
const getOAuthSigningKey = `-- name: GetOAuthSigningKey :one
SELECT value FROM site_configs WHERE key = 'oauth_signing_key'
`
@@ -5863,6 +5863,16 @@ func (q *sqlQuerier) InsertDeploymentID(ctx context.Context, value string) error
return err
}
const upsertAnnouncementBanners = `-- name: UpsertAnnouncementBanners :exec
INSERT INTO site_configs (key, value) VALUES ('announcement_banners', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'announcement_banners'
`
func (q *sqlQuerier) UpsertAnnouncementBanners(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertAnnouncementBanners, value)
return err
}
const upsertAppSecurityKey = `-- name: UpsertAppSecurityKey :exec
INSERT INTO site_configs (key, value) VALUES ('app_signing_key', $1)
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'app_signing_key'
@@ -5936,16 +5946,6 @@ func (q *sqlQuerier) UpsertLogoURL(ctx context.Context, value string) error {
return err
}
const upsertNotificationBanners = `-- name: UpsertNotificationBanners :exec
INSERT INTO site_configs (key, value) VALUES ('notification_banners', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'notification_banners'
`
func (q *sqlQuerier) UpsertNotificationBanners(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertNotificationBanners, value)
return err
}
const upsertOAuthSigningKey = `-- name: UpsertOAuthSigningKey :exec
INSERT INTO site_configs (key, value) VALUES ('oauth_signing_key', $1)
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'oauth_signing_key'
+5 -5
View File
@@ -36,12 +36,12 @@ ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'last_update
-- name: GetLastUpdateCheck :one
SELECT value FROM site_configs WHERE key = 'last_update_check';
-- name: UpsertNotificationBanners :exec
INSERT INTO site_configs (key, value) VALUES ('notification_banners', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'notification_banners';
-- name: UpsertAnnouncementBanners :exec
INSERT INTO site_configs (key, value) VALUES ('announcement_banners', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'announcement_banners';
-- name: GetNotificationBanners :one
SELECT value FROM site_configs WHERE key = 'notification_banners';
-- name: GetAnnouncementBanners :one
SELECT value FROM site_configs WHERE key = 'announcement_banners';
-- name: UpsertLogoURL :exec
INSERT INTO site_configs (key, value) VALUES ('logo_url', $1)
+4 -4
View File
@@ -2105,18 +2105,18 @@ func (c *Client) DeploymentStats(ctx context.Context) (DeploymentStats, error) {
type AppearanceConfig struct {
ApplicationName string `json:"application_name"`
LogoURL string `json:"logo_url"`
// Deprecated: ServiceBanner has been replaced by NotificationBanners.
// Deprecated: ServiceBanner has been replaced by AnnouncementBanners.
ServiceBanner BannerConfig `json:"service_banner"`
NotificationBanners []BannerConfig `json:"notification_banners"`
AnnouncementBanners []BannerConfig `json:"announcement_banners"`
SupportLinks []LinkConfig `json:"support_links,omitempty"`
}
type UpdateAppearanceConfig struct {
ApplicationName string `json:"application_name"`
LogoURL string `json:"logo_url"`
// Deprecated: ServiceBanner has been replaced by NotificationBanners.
// Deprecated: ServiceBanner has been replaced by AnnouncementBanners.
ServiceBanner BannerConfig `json:"service_banner"`
NotificationBanners []BannerConfig `json:"notification_banners"`
AnnouncementBanners []BannerConfig `json:"announcement_banners"`
}
// Deprecated: ServiceBannerConfig has been renamed to BannerConfig.
+9 -9
View File
@@ -19,15 +19,15 @@ curl -X GET http://coder-server:8080/api/v2/appearance \
```json
{
"application_name": "string",
"logo_url": "string",
"notification_banners": [
"announcement_banners": [
{
"background_color": "string",
"enabled": true,
"message": "string"
}
],
"application_name": "string",
"logo_url": "string",
"service_banner": {
"background_color": "string",
"enabled": true,
@@ -69,15 +69,15 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \
```json
{
"application_name": "string",
"logo_url": "string",
"notification_banners": [
"announcement_banners": [
{
"background_color": "string",
"enabled": true,
"message": "string"
}
],
"application_name": "string",
"logo_url": "string",
"service_banner": {
"background_color": "string",
"enabled": true,
@@ -98,15 +98,15 @@ curl -X PUT http://coder-server:8080/api/v2/appearance \
```json
{
"application_name": "string",
"logo_url": "string",
"notification_banners": [
"announcement_banners": [
{
"background_color": "string",
"enabled": true,
"message": "string"
}
],
"application_name": "string",
"logo_url": "string",
"service_banner": {
"background_color": "string",
"enabled": true,
+10 -10
View File
@@ -749,15 +749,15 @@
```json
{
"application_name": "string",
"logo_url": "string",
"notification_banners": [
"announcement_banners": [
{
"background_color": "string",
"enabled": true,
"message": "string"
}
],
"application_name": "string",
"logo_url": "string",
"service_banner": {
"background_color": "string",
"enabled": true,
@@ -777,10 +777,10 @@
| Name | Type | Required | Restrictions | Description |
| ---------------------- | ------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------- |
| `announcement_banners` | array of [codersdk.BannerConfig](#codersdkbannerconfig) | false | | |
| `application_name` | string | false | | |
| `logo_url` | string | false | | |
| `notification_banners` | array of [codersdk.BannerConfig](#codersdkbannerconfig) | false | | |
| `service_banner` | [codersdk.BannerConfig](#codersdkbannerconfig) | false | | Deprecated: ServiceBanner has been replaced by NotificationBanners. |
| `service_banner` | [codersdk.BannerConfig](#codersdkbannerconfig) | false | | Deprecated: ServiceBanner has been replaced by AnnouncementBanners. |
| `support_links` | array of [codersdk.LinkConfig](#codersdklinkconfig) | false | | |
## codersdk.ArchiveTemplateVersionsRequest
@@ -5301,15 +5301,15 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
```json
{
"application_name": "string",
"logo_url": "string",
"notification_banners": [
"announcement_banners": [
{
"background_color": "string",
"enabled": true,
"message": "string"
}
],
"application_name": "string",
"logo_url": "string",
"service_banner": {
"background_color": "string",
"enabled": true,
@@ -5322,10 +5322,10 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
| Name | Type | Required | Restrictions | Description |
| ---------------------- | ------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------- |
| `announcement_banners` | array of [codersdk.BannerConfig](#codersdkbannerconfig) | false | | |
| `application_name` | string | false | | |
| `logo_url` | string | false | | |
| `notification_banners` | array of [codersdk.BannerConfig](#codersdkbannerconfig) | false | | |
| `service_banner` | [codersdk.BannerConfig](#codersdkbannerconfig) | false | | Deprecated: ServiceBanner has been replaced by NotificationBanners. |
| `service_banner` | [codersdk.BannerConfig](#codersdkbannerconfig) | false | | Deprecated: ServiceBanner has been replaced by AnnouncementBanners. |
## codersdk.UpdateCheckResponse
+15 -15
View File
@@ -58,7 +58,7 @@ func (f *appearanceFetcher) Fetch(ctx context.Context) (codersdk.AppearanceConfi
var (
applicationName string
logoURL string
notificationBannersJSON string
announcementBannersJSON string
)
eg.Go(func() (err error) {
applicationName, err = f.database.GetApplicationName(ctx)
@@ -75,7 +75,7 @@ func (f *appearanceFetcher) Fetch(ctx context.Context) (codersdk.AppearanceConfi
return nil
})
eg.Go(func() (err error) {
notificationBannersJSON, err = f.database.GetNotificationBanners(ctx)
announcementBannersJSON, err = f.database.GetAnnouncementBanners(ctx)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return xerrors.Errorf("get notification banners: %w", err)
}
@@ -89,22 +89,22 @@ func (f *appearanceFetcher) Fetch(ctx context.Context) (codersdk.AppearanceConfi
cfg := codersdk.AppearanceConfig{
ApplicationName: applicationName,
LogoURL: logoURL,
NotificationBanners: []codersdk.BannerConfig{},
AnnouncementBanners: []codersdk.BannerConfig{},
SupportLinks: agpl.DefaultSupportLinks,
}
if notificationBannersJSON != "" {
err = json.Unmarshal([]byte(notificationBannersJSON), &cfg.NotificationBanners)
if announcementBannersJSON != "" {
err = json.Unmarshal([]byte(announcementBannersJSON), &cfg.AnnouncementBanners)
if err != nil {
return codersdk.AppearanceConfig{}, xerrors.Errorf(
"unmarshal notification banners json: %w, raw: %s", err, notificationBannersJSON,
"unmarshal announcement banners json: %w, raw: %s", err, announcementBannersJSON,
)
}
// Redundant, but improves compatibility with slightly mismatched agent versions.
// Maybe we can remove this after a grace period? -Kayla, May 6th 2024
if len(cfg.NotificationBanners) > 0 {
cfg.ServiceBanner = cfg.NotificationBanners[0]
if len(cfg.AnnouncementBanners) > 0 {
cfg.ServiceBanner = cfg.AnnouncementBanners[0]
}
}
if len(f.supportLinks) > 0 {
@@ -149,7 +149,7 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) {
return
}
for _, banner := range appearance.NotificationBanners {
for _, banner := range appearance.AnnouncementBanners {
if err := validateHexColor(banner.BackgroundColor); err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: fmt.Sprintf("Invalid color format: %q", banner.BackgroundColor),
@@ -159,22 +159,22 @@ func (api *API) putAppearance(rw http.ResponseWriter, r *http.Request) {
}
}
if appearance.NotificationBanners == nil {
appearance.NotificationBanners = []codersdk.BannerConfig{}
if appearance.AnnouncementBanners == nil {
appearance.AnnouncementBanners = []codersdk.BannerConfig{}
}
notificationBannersJSON, err := json.Marshal(appearance.NotificationBanners)
announcementBannersJSON, err := json.Marshal(appearance.AnnouncementBanners)
if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Unable to marshal notification banners",
Message: "Unable to marshal announcement banners",
Detail: err.Error(),
})
return
}
err = api.Database.UpsertNotificationBanners(ctx, string(notificationBannersJSON))
err = api.Database.UpsertAnnouncementBanners(ctx, string(announcementBannersJSON))
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Unable to set notification banners",
Message: "Unable to set announcement banners",
Detail: err.Error(),
})
return
+16 -16
View File
@@ -55,7 +55,7 @@ func TestCustomLogoAndCompanyName(t *testing.T) {
require.Equal(t, uac.LogoURL, got.LogoURL)
}
func TestNotificationBanners(t *testing.T) {
func TestAnnouncementBanners(t *testing.T) {
t.Parallel()
t.Run("User", func(t *testing.T) {
@@ -70,7 +70,7 @@ func TestNotificationBanners(t *testing.T) {
// Without a license, there should be no banners.
sb, err := basicUserClient.Appearance(ctx)
require.NoError(t, err)
require.Empty(t, sb.NotificationBanners)
require.Empty(t, sb.AnnouncementBanners)
coderdenttest.AddLicense(t, adminClient, coderdenttest.LicenseOptions{
Features: license.Features{
@@ -81,11 +81,11 @@ func TestNotificationBanners(t *testing.T) {
// Default state
sb, err = basicUserClient.Appearance(ctx)
require.NoError(t, err)
require.Empty(t, sb.NotificationBanners)
require.Empty(t, sb.AnnouncementBanners)
// Regular user should be unable to set the banner
uac := codersdk.UpdateAppearanceConfig{
NotificationBanners: []codersdk.BannerConfig{{Enabled: true}},
AnnouncementBanners: []codersdk.BannerConfig{{Enabled: true}},
}
err = basicUserClient.UpdateAppearance(ctx, uac)
require.Error(t, err)
@@ -96,7 +96,7 @@ func TestNotificationBanners(t *testing.T) {
// But an admin can
wantBanner := codersdk.UpdateAppearanceConfig{
NotificationBanners: []codersdk.BannerConfig{{
AnnouncementBanners: []codersdk.BannerConfig{{
Enabled: true,
Message: "The beep-bop will be boop-beeped on Saturday at 12AM PST.",
BackgroundColor: "#00FF00",
@@ -106,10 +106,10 @@ func TestNotificationBanners(t *testing.T) {
require.NoError(t, err)
gotBanner, err := adminClient.Appearance(ctx) //nolint:gocritic // we should assert at least once that the owner can get the banner
require.NoError(t, err)
require.Equal(t, wantBanner.NotificationBanners, gotBanner.NotificationBanners)
require.Equal(t, wantBanner.AnnouncementBanners, gotBanner.AnnouncementBanners)
// But even an admin can't give a bad color
wantBanner.NotificationBanners[0].BackgroundColor = "#bad color"
wantBanner.AnnouncementBanners[0].BackgroundColor = "#bad color"
err = adminClient.UpdateAppearance(ctx, wantBanner)
require.Error(t, err)
var sdkErr *codersdk.Error
@@ -139,7 +139,7 @@ func TestNotificationBanners(t *testing.T) {
},
})
cfg := codersdk.UpdateAppearanceConfig{
NotificationBanners: []codersdk.BannerConfig{{
AnnouncementBanners: []codersdk.BannerConfig{{
Enabled: true,
Message: "The beep-bop will be boop-beeped on Saturday at 12AM PST.",
BackgroundColor: "#00FF00",
@@ -155,35 +155,35 @@ func TestNotificationBanners(t *testing.T) {
agentClient := agentsdk.New(client.URL)
agentClient.SetSessionToken(r.AgentToken)
banners := requireGetNotificationBanners(ctx, t, agentClient)
require.Equal(t, cfg.NotificationBanners, banners)
banners := requireGetAnnouncementBanners(ctx, t, agentClient)
require.Equal(t, cfg.AnnouncementBanners, banners)
// Create an AGPL Coderd against the same database
agplClient := coderdtest.New(t, &coderdtest.Options{Database: store, Pubsub: ps})
agplAgentClient := agentsdk.New(agplClient.URL)
agplAgentClient.SetSessionToken(r.AgentToken)
banners = requireGetNotificationBanners(ctx, t, agplAgentClient)
banners = requireGetAnnouncementBanners(ctx, t, agplAgentClient)
require.Equal(t, []codersdk.BannerConfig{}, banners)
// No license means no banner.
err = client.DeleteLicense(ctx, lic.ID)
require.NoError(t, err)
banners = requireGetNotificationBanners(ctx, t, agentClient)
banners = requireGetAnnouncementBanners(ctx, t, agentClient)
require.Equal(t, []codersdk.BannerConfig{}, banners)
})
}
func requireGetNotificationBanners(ctx context.Context, t *testing.T, client *agentsdk.Client) []codersdk.BannerConfig {
func requireGetAnnouncementBanners(ctx context.Context, t *testing.T, client *agentsdk.Client) []codersdk.BannerConfig {
cc, err := client.ConnectRPC(ctx)
require.NoError(t, err)
defer func() {
_ = cc.Close()
}()
aAPI := proto.NewDRPCAgentClient(cc)
bannersProto, err := aAPI.GetNotificationBanners(ctx, &proto.GetNotificationBannersRequest{})
bannersProto, err := aAPI.GetAnnouncementBanners(ctx, &proto.GetAnnouncementBannersRequest{})
require.NoError(t, err)
banners := make([]codersdk.BannerConfig, 0, len(bannersProto.NotificationBanners))
for _, bannerProto := range bannersProto.NotificationBanners {
banners := make([]codersdk.BannerConfig, 0, len(bannersProto.AnnouncementBanners))
for _, bannerProto := range bannersProto.AnnouncementBanners {
banners = append(banners, agentsdk.BannerConfigFromProto(bannerProto))
}
return banners
+1 -1
View File
@@ -1581,7 +1581,7 @@ class ApiMethods {
return {
application_name: "",
logo_url: "",
notification_banners: [],
announcement_banners: [],
service_banner: {
enabled: false,
},
+2 -2
View File
@@ -49,7 +49,7 @@ export interface AppearanceConfig {
readonly application_name: string;
readonly logo_url: string;
readonly service_banner: BannerConfig;
readonly notification_banners: readonly BannerConfig[];
readonly announcement_banners: readonly BannerConfig[];
readonly support_links?: readonly LinkConfig[];
}
@@ -1309,7 +1309,7 @@ export interface UpdateAppearanceConfig {
readonly application_name: string;
readonly logo_url: string;
readonly service_banner: BannerConfig;
readonly notification_banners: readonly BannerConfig[];
readonly announcement_banners: readonly BannerConfig[];
}
// From codersdk/updatecheck.go
@@ -1,13 +1,13 @@
import type { Meta, StoryObj } from "@storybook/react";
import { NotificationBannerView } from "./NotificationBannerView";
import { AnnouncementBannerView } from "./AnnouncementBannerView";
const meta: Meta<typeof NotificationBannerView> = {
title: "modules/dashboard/NotificationBannerView",
component: NotificationBannerView,
const meta: Meta<typeof AnnouncementBannerView> = {
title: "modules/dashboard/AnnouncementBannerView",
component: AnnouncementBannerView,
};
export default meta;
type Story = StoryObj<typeof NotificationBannerView>;
type Story = StoryObj<typeof AnnouncementBannerView>;
export const Production: Story = {
args: {
@@ -3,12 +3,12 @@ import type { FC } from "react";
import { InlineMarkdown } from "components/Markdown/Markdown";
import { readableForegroundColor } from "utils/colors";
export interface NotificationBannerViewProps {
export interface AnnouncementBannerViewProps {
message?: string;
backgroundColor?: string;
}
export const NotificationBannerView: FC<NotificationBannerViewProps> = ({
export const AnnouncementBannerView: FC<AnnouncementBannerViewProps> = ({
message,
backgroundColor,
}) => {
@@ -1,10 +1,10 @@
import type { FC } from "react";
import { useDashboard } from "modules/dashboard/useDashboard";
import { NotificationBannerView } from "./NotificationBannerView";
import { AnnouncementBannerView } from "./AnnouncementBannerView";
export const NotificationBanners: FC = () => {
export const AnnouncementBanners: FC = () => {
const { appearance, entitlements } = useDashboard();
const notificationBanners = appearance.notification_banners;
const announcementBanners = appearance.announcement_banners;
const isEntitled =
entitlements.features.appearance.entitlement !== "not_entitled";
@@ -14,10 +14,10 @@ export const NotificationBanners: FC = () => {
return (
<>
{notificationBanners
{announcementBanners
.filter((banner) => banner.enabled)
.map((banner) => (
<NotificationBannerView
<AnnouncementBannerView
key={banner.message}
message={banner.message}
backgroundColor={banner.background_color}
@@ -6,8 +6,8 @@ import { type FC, type HTMLAttributes, Suspense } from "react";
import { Outlet } from "react-router-dom";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { AnnouncementBanners } from "modules/dashboard/AnnouncementBanners/AnnouncementBanners";
import { LicenseBanner } from "modules/dashboard/LicenseBanner/LicenseBanner";
import { NotificationBanners } from "modules/dashboard/NotificationBanners/NotificationBanners";
import { dashboardContentBottomPadding } from "theme/constants";
import { docs } from "utils/docs";
import { DeploymentBanner } from "./DeploymentBanner/DeploymentBanner";
@@ -22,7 +22,7 @@ export const DashboardLayout: FC = () => {
return (
<>
{canViewDeployment && <LicenseBanner />}
<NotificationBanners />
<AnnouncementBanners />
<div
css={{
@@ -1,10 +1,10 @@
import { action } from "@storybook/addon-actions";
import type { Meta, StoryObj } from "@storybook/react";
import { NotificationBannerDialog } from "./NotificationBannerDialog";
import { AnnouncementBannerDialog } from "./AnnouncementBannerDialog";
const meta: Meta<typeof NotificationBannerDialog> = {
title: "pages/DeploySettingsPage/NotificationBannerDialog",
component: NotificationBannerDialog,
const meta: Meta<typeof AnnouncementBannerDialog> = {
title: "pages/DeploySettingsPage/AnnouncementBannerDialog",
component: AnnouncementBannerDialog,
args: {
banner: {
enabled: true,
@@ -17,8 +17,8 @@ const meta: Meta<typeof NotificationBannerDialog> = {
};
export default meta;
type Story = StoryObj<typeof NotificationBannerDialog>;
type Story = StoryObj<typeof AnnouncementBannerDialog>;
const Example: Story = {};
export { Example as NotificationBannerDialog };
export { Example as AnnouncementBannerDialog };
@@ -7,16 +7,16 @@ import { BlockPicker } from "react-color";
import type { BannerConfig } from "api/typesGenerated";
import { Dialog, DialogActionButtons } from "components/Dialogs/Dialog";
import { Stack } from "components/Stack/Stack";
import { NotificationBannerView } from "modules/dashboard/NotificationBanners/NotificationBannerView";
import { AnnouncementBannerView } from "modules/dashboard/AnnouncementBanners/AnnouncementBannerView";
import { getFormHelpers } from "utils/formUtils";
interface NotificationBannerDialogProps {
interface AnnouncementBannerDialogProps {
banner: BannerConfig;
onCancel: () => void;
onUpdate: (banner: Partial<BannerConfig>) => Promise<void>;
}
export const NotificationBannerDialog: FC<NotificationBannerDialogProps> = ({
export const AnnouncementBannerDialog: FC<AnnouncementBannerDialogProps> = ({
banner,
onCancel,
onUpdate,
@@ -39,14 +39,14 @@ export const NotificationBannerDialog: FC<NotificationBannerDialogProps> = ({
<Dialog css={styles.dialogWrapper} open onClose={onCancel}>
{/* Banner preview */}
<div css={{ position: "fixed", top: 0, left: 0, right: 0 }}>
<NotificationBannerView
<AnnouncementBannerView
message={bannerForm.values.message}
backgroundColor={bannerForm.values.background_color}
/>
</div>
<div css={styles.dialogContent}>
<h3 css={styles.dialogTitle}>Notification banner</h3>
<h3 css={styles.dialogTitle}>Announcement banner</h3>
<Stack>
<div>
<h4 css={styles.settingName}>Message</h4>
@@ -12,7 +12,7 @@ import {
ThreeDotsButton,
} from "components/MoreMenu/MoreMenu";
interface NotificationBannerItemProps {
interface AnnouncementBannerItemProps {
enabled: boolean;
backgroundColor?: string;
message?: string;
@@ -21,7 +21,7 @@ interface NotificationBannerItemProps {
onDelete: () => void;
}
export const NotificationBannerItem: FC<NotificationBannerItemProps> = ({
export const AnnouncementBannerItem: FC<AnnouncementBannerItemProps> = ({
enabled,
backgroundColor = "#004852",
message,
@@ -13,20 +13,20 @@ import type { BannerConfig } from "api/typesGenerated";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { EmptyState } from "components/EmptyState/EmptyState";
import { Stack } from "components/Stack/Stack";
import { NotificationBannerDialog } from "./NotificationBannerDialog";
import { NotificationBannerItem } from "./NotificationBannerItem";
import { AnnouncementBannerDialog } from "./AnnouncementBannerDialog";
import { AnnouncementBannerItem } from "./AnnouncementBannerItem";
interface NotificationBannerSettingsProps {
interface AnnouncementBannersettingsProps {
isEntitled: boolean;
notificationBanners: readonly BannerConfig[];
announcementBanners: readonly BannerConfig[];
onSubmit: (banners: readonly BannerConfig[]) => Promise<void>;
}
export const NotificationBannerSettings: FC<
NotificationBannerSettingsProps
> = ({ isEntitled, notificationBanners, onSubmit }) => {
export const AnnouncementBannerSettings: FC<
AnnouncementBannersettingsProps
> = ({ isEntitled, announcementBanners, onSubmit }) => {
const theme = useTheme();
const [banners, setBanners] = useState(notificationBanners);
const [banners, setBanners] = useState(announcementBanners);
const [editingBannerId, setEditingBannerId] = useState<number | null>(null);
const [deletingBannerId, setDeletingBannerId] = useState<number | null>(null);
@@ -84,7 +84,7 @@ export const NotificationBannerSettings: FC<
fontWeight: 600,
}}
>
Notification Banners
Announcement Banners
</h3>
<Button
disabled={!isEntitled}
@@ -125,12 +125,12 @@ export const NotificationBannerSettings: FC<
<TableCell colSpan={999}>
<EmptyState
css={{ minHeight: 160 }}
message="No notification banners"
message="No announcement banners"
/>
</TableCell>
) : (
banners.map((banner, i) => (
<NotificationBannerItem
<AnnouncementBannerItem
key={banner.message}
enabled={banner.enabled && Boolean(banner.message)}
backgroundColor={banner.background_color}
@@ -172,7 +172,7 @@ export const NotificationBannerSettings: FC<
</div>
{editingBanner && (
<NotificationBannerDialog
<AnnouncementBannerDialog
banner={editingBanner}
onCancel={() => setEditingBannerId(null)}
onUpdate={async (banner) => {
@@ -13,7 +13,7 @@ const meta: Meta<typeof AppearanceSettingsPageView> = {
message: "",
background_color: "#00ff00",
},
notification_banners: [
announcement_banners: [
{
enabled: true,
message: "The beep-bop will be boop-beeped on Saturday at 12AM PST.",
@@ -19,7 +19,7 @@ import {
import { getFormHelpers } from "utils/formUtils";
import { Fieldset } from "../Fieldset";
import { Header } from "../Header";
import { NotificationBannerSettings } from "./NotificationBannerSettings";
import { AnnouncementBannerSettings } from "./AnnouncementBannerSettings";
export type AppearanceSettingsPageViewProps = {
appearance: UpdateAppearanceConfig;
@@ -144,11 +144,11 @@ export const AppearanceSettingsPageView: FC<
/>
</Fieldset>
<NotificationBannerSettings
<AnnouncementBannerSettings
isEntitled={isEntitled}
notificationBanners={appearance.notification_banners || []}
onSubmit={(notificationBanners) =>
onSaveAppearance({ notification_banners: notificationBanners })
announcementBanners={appearance.announcement_banners || []}
onSubmit={(announcementBanners) =>
onSaveAppearance({ announcement_banners: announcementBanners })
}
/>
</>
@@ -11,8 +11,8 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { Margins } from "components/Margins/Margins";
import { useEffectEvent } from "hooks/hookPolyfills";
import { AnnouncementBanners } from "modules/dashboard/AnnouncementBanners/AnnouncementBanners";
import { Navbar } from "modules/dashboard/Navbar/Navbar";
import { NotificationBanners } from "modules/dashboard/NotificationBanners/NotificationBanners";
import { useDashboard } from "modules/dashboard/useDashboard";
import { workspaceChecks, type WorkspacePermissions } from "./permissions";
import { WorkspaceReadyPage } from "./WorkspaceReadyPage";
@@ -106,7 +106,7 @@ export const WorkspacePage: FC = () => {
return (
<>
<NotificationBanners />
<AnnouncementBanners />
<div css={{ height: "100%", display: "flex", flexDirection: "column" }}>
<Navbar />
{pageError ? (
+1 -1
View File
@@ -2372,7 +2372,7 @@ export const MockAppearanceConfig: TypesGen.AppearanceConfig = {
service_banner: {
enabled: false,
},
notification_banners: [],
announcement_banners: [],
};
export const MockWorkspaceBuildParameter1: TypesGen.WorkspaceBuildParameter = {