Skip to content

Commit 47af6d6

Browse files
authored
Merge pull request #3725 from ActiveState/CP-1087
Allow configuration of notifications URL
2 parents 7483c85 + 020402c commit 47af6d6

5 files changed

Lines changed: 139 additions & 12 deletions

File tree

cmd/state-svc/internal/notifications/notifications.go

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/ActiveState/cli/internal/config"
1212
"github.com/ActiveState/cli/internal/constants"
1313
"github.com/ActiveState/cli/internal/errs"
14-
"github.com/ActiveState/cli/internal/fileutils"
1514
"github.com/ActiveState/cli/internal/graph"
1615
"github.com/ActiveState/cli/internal/httputil"
1716
"github.com/ActiveState/cli/internal/logging"
@@ -21,8 +20,14 @@ import (
2120
auth "github.com/ActiveState/cli/pkg/platform/authentication"
2221
"github.com/ActiveState/cli/pkg/sysinfo"
2322
"github.com/blang/semver"
23+
24+
configMediator "github.com/ActiveState/cli/internal/mediators/config"
2425
)
2526

27+
func init() {
28+
configMediator.RegisterOption(constants.NotificationsURLConfig, configMediator.String, "")
29+
}
30+
2631
const ConfigKeyLastReport = "notifications.last_reported"
2732

2833
type Notifications struct {
@@ -48,11 +53,11 @@ func New(cfg *config.Instance, auth *auth.Auth) (*Notifications, error) {
4853
defer func() {
4954
panics.LogAndPanic(recover(), debug.Stack())
5055
}()
51-
resp, err := fetch()
56+
resp, err := fetch(cfg)
5257
return resp, err
5358
})
5459

55-
return &Notifications{
60+
notifications := &Notifications{
5661
baseParams: &ConditionParams{
5762
OS: sysinfo.OS().String(),
5863
OSVersion: NewVersionFromSysinfo(osVersion),
@@ -62,7 +67,20 @@ func New(cfg *config.Instance, auth *auth.Auth) (*Notifications, error) {
6267
cfg: cfg,
6368
auth: auth,
6469
poll: poll,
65-
}, nil
70+
}
71+
72+
configMediator.AddListener(constants.NotificationsURLConfig, func() {
73+
notifications.poll.Close()
74+
notifications.poll = poller.New(10*time.Minute, func() (interface{}, error) {
75+
defer func() {
76+
panics.LogAndPanic(recover(), debug.Stack())
77+
}()
78+
resp, err := fetch(cfg)
79+
return resp, err
80+
})
81+
})
82+
83+
return notifications, nil
6684
}
6785

6886
func (m *Notifications) Close() error {
@@ -168,8 +186,7 @@ func check(params *ConditionParams, notifications []*graph.NotificationInfo, las
168186
// Check if message is within date range
169187
inRange, err := notificationInDateRange(notification, baseTime)
170188
if err != nil {
171-
logging.Warning("Could not check if notification %s is in date range: %v", notification.ID, err)
172-
continue
189+
return nil, errs.Wrap(err, "Could not check if notification %s is in date range", notification.ID)
173190
}
174191
if !inRange {
175192
logging.Debug("Skipping notification %s as it is outside of its date range", notification.ID)
@@ -198,17 +215,36 @@ func check(params *ConditionParams, notifications []*graph.NotificationInfo, las
198215
return filteredNotifications, nil
199216
}
200217

201-
func fetch() ([]*graph.NotificationInfo, error) {
218+
func fetch(cfg *config.Instance) ([]*graph.NotificationInfo, error) {
202219
var body []byte
203220
var err error
204221

205-
if v := os.Getenv(constants.NotificationsOverrideEnvVarName); v != "" {
206-
body, err = fileutils.ReadFile(v)
222+
var (
223+
notificationsURL string
224+
225+
envURL = os.Getenv(constants.NotificationsOverrideEnvVarName)
226+
configURL = cfg.GetString(constants.NotificationsURLConfig)
227+
)
228+
229+
switch {
230+
case envURL != "":
231+
notificationsURL = envURL
232+
case configURL != "":
233+
notificationsURL = configURL
234+
default:
235+
notificationsURL = constants.NotificationsInfoURL
236+
}
237+
238+
logging.Debug("Fetching notifications from %s", notificationsURL)
239+
// Check if this is a local file path (when using environment override)
240+
if envURL != "" {
241+
body, err = os.ReadFile(notificationsURL)
207242
if err != nil {
208-
return nil, errs.Wrap(err, "Could not read notifications override file")
243+
return nil, errs.Wrap(err, "Could not read notifications file")
209244
}
210245
} else {
211-
body, err = httputil.Get(constants.NotificationsInfoURL)
246+
// Use HTTP client for remote URLs
247+
body, err = httputil.Get(notificationsURL)
212248
if err != nil {
213249
return nil, errs.Wrap(err, "Could not fetch notifications information")
214250
}

cmd/state/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func main() {
9797
// Configuration options
9898
// This should only be used if the config option is not exclusive to one package.
9999
configMediator.RegisterOption(constants.OptinBuildscriptsConfig, configMediator.Bool, false)
100+
configMediator.RegisterOption(constants.NotificationsURLConfig, configMediator.String, "")
100101

101102
// Set up our output formatter/writer
102103
outFlags := parseOutputFlags(os.Args)

internal/constants/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ const AnalyticsPixelOverrideConfig = "report.analytics.endpoint"
418418
// UpdateEndpointConfig is the config key used to determine the update endpoint to use
419419
const UpdateEndpointConfig = "update.endpoint"
420420

421+
// NotificationsURLConfig is the config key used to determine the notifications url to use
422+
const NotificationsURLConfig = "notifications.endpoint"
423+
421424
// APIHostConfig is the config key used to determine the api host
422425
const APIHostConfig = "api.host"
423426

internal/testhelpers/e2e/session.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ type Session struct {
6666
ignoreLogErrors bool
6767
cfg *config.Instance
6868
cache keyCache
69-
cfg *config.Instance
7069
}
7170

7271
type keyCache map[string]string

test/integration/notification_int_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package integration
22

33
import (
44
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
58
"testing"
69
"time"
710

@@ -269,6 +272,91 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptE
269272
ts.IgnoreLogErrors()
270273
}
271274

275+
func (suite *NotificationIntegrationTestSuite) TestNotificationEndpoint_SetBeforeInvocation() {
276+
suite.OnlyRunForTags(tagsuite.Notifications)
277+
278+
ts := e2e.New(suite.T(), false)
279+
defer ts.Close()
280+
281+
notificationsURL := "https://test.example.com/notifications"
282+
ts.SetConfig(constants.NotificationsURLConfig, notificationsURL)
283+
suite.Assert().Equal(ts.GetConfig(constants.NotificationsURLConfig), notificationsURL)
284+
285+
cp := ts.Spawn("--version")
286+
cp.ExpectExitCode(0)
287+
288+
foundConfigURL := false
289+
foundDefaultURL := false
290+
logDir := filepath.Join(ts.Dirs.Config, "logs")
291+
files, err := os.ReadDir(logDir)
292+
suite.Require().NoError(err)
293+
for _, file := range files {
294+
if strings.Contains(file.Name(), "state-svc") {
295+
logPath := filepath.Join(logDir, file.Name())
296+
contents := string(fileutils.ReadFileUnsafe(logPath))
297+
if strings.Contains(contents, "test.example.com") {
298+
foundConfigURL = true
299+
}
300+
if strings.Contains(contents, "state-tool.s3.amazonaws.com") {
301+
foundDefaultURL = true
302+
}
303+
}
304+
}
305+
suite.Assert().True(foundConfigURL, "Log file should contain the configured notifications endpoint '%s'", notificationsURL)
306+
suite.Assert().False(foundDefaultURL, "Log file should not contain the default notifications endpoint '%s'", constants.NotificationsInfoURL)
307+
308+
// Clean up - remove the config setting
309+
cp = ts.Spawn("config", "set", constants.NotificationsURLConfig, "")
310+
cp.Expect("Successfully")
311+
cp.ExpectExitCode(0)
312+
}
313+
314+
func (suite *NotificationIntegrationTestSuite) TestNotificationEndpoint() {
315+
suite.OnlyRunForTags(tagsuite.Notifications)
316+
317+
ts := e2e.New(suite.T(), false)
318+
defer ts.Close()
319+
320+
msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(`[{
321+
"ID": "config-test",
322+
"Message": "This is a [NOTICE]config test[/RESET] notification"
323+
}]`), 0755)
324+
suite.Require().NoError(err)
325+
326+
notificationsURL := "https://test.example.com/notifications"
327+
cp := ts.Spawn("config", "set", constants.NotificationsURLConfig, notificationsURL)
328+
cp.Expect("Successfully set config key")
329+
cp.ExpectExitCode(0)
330+
331+
cp = ts.SpawnWithOpts(
332+
e2e.OptArgs("--version"),
333+
e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile),
334+
e2e.OptAppendEnv("VERBOSE=true"),
335+
)
336+
cp.ExpectExitCode(0)
337+
338+
correctHostFound := false
339+
incorrectHostFound := false
340+
for _, path := range ts.LogFiles() {
341+
contents := string(fileutils.ReadFileUnsafe(path))
342+
if strings.Contains(contents, notificationsURL) {
343+
correctHostFound = true
344+
break
345+
}
346+
if strings.Contains(contents, constants.NotificationsInfoURL) {
347+
incorrectHostFound = true
348+
break
349+
}
350+
}
351+
suite.Assert().True(correctHostFound, "Log file should contain the configured notifications endpoint 'example.com'")
352+
suite.Assert().False(incorrectHostFound, "Log file should not contain the default notifications endpoint 'state-tool.s3.amazonaws.com'")
353+
354+
// Clean up - remove the config setting
355+
cp = ts.Spawn("config", "set", constants.NotificationsURLConfig, "")
356+
cp.Expect("Successfully")
357+
cp.ExpectExitCode(0)
358+
}
359+
272360
func TestNotificationIntegrationTestSuite(t *testing.T) {
273361
suite.Run(t, new(NotificationIntegrationTestSuite))
274362
}

0 commit comments

Comments
 (0)