refactor(app): add App type
This commit is contained in:
parent
4b464c680d
commit
d7f8fb49eb
@ -18,8 +18,19 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// RunParams holds the parameters for running the application.
|
||||
type RunParams struct {
|
||||
// App is an instance of the app.
|
||||
type App struct {
|
||||
configService *config.Service
|
||||
dockerClient container.DockerClient
|
||||
screen *terminal.Screen // Screen may be nil.
|
||||
clipboardAvailable bool
|
||||
configFilePath string
|
||||
buildInfo domain.BuildInfo
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// Params holds the parameters for running the application.
|
||||
type Params struct {
|
||||
ConfigService *config.Service
|
||||
DockerClient container.DockerClient
|
||||
Screen *terminal.Screen // Screen may be nil.
|
||||
@ -29,14 +40,25 @@ type RunParams struct {
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
func New(params Params) *App {
|
||||
return &App{
|
||||
configService: params.ConfigService,
|
||||
dockerClient: params.DockerClient,
|
||||
screen: params.Screen,
|
||||
clipboardAvailable: params.ClipboardAvailable,
|
||||
configFilePath: params.ConfigFilePath,
|
||||
buildInfo: params.BuildInfo,
|
||||
logger: params.Logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Run starts the application, and blocks until it exits.
|
||||
func Run(ctx context.Context, params RunParams) error {
|
||||
logger := params.Logger
|
||||
eventBus := event.NewBus(logger.With("component", "event_bus"))
|
||||
func (a *App) Run(ctx context.Context) error {
|
||||
eventBus := event.NewBus(a.logger.With("component", "event_bus"))
|
||||
|
||||
// cfg is the current configuration of the application, as reflected in the
|
||||
// config file.
|
||||
cfg := params.ConfigService.Current()
|
||||
cfg := a.configService.Current()
|
||||
|
||||
// state is the current state of the application, as reflected in the UI.
|
||||
state := new(domain.AppState)
|
||||
@ -49,11 +71,11 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
|
||||
ui, err := terminal.StartUI(ctx, terminal.StartParams{
|
||||
EventBus: eventBus,
|
||||
Screen: params.Screen,
|
||||
ClipboardAvailable: params.ClipboardAvailable,
|
||||
ConfigFilePath: params.ConfigFilePath,
|
||||
BuildInfo: params.BuildInfo,
|
||||
Logger: logger.With("component", "ui"),
|
||||
Screen: a.screen,
|
||||
ClipboardAvailable: a.clipboardAvailable,
|
||||
ConfigFilePath: a.configFilePath,
|
||||
BuildInfo: a.buildInfo,
|
||||
Logger: a.logger.With("component", "ui"),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("start terminal user interface: %w", err)
|
||||
@ -71,7 +93,7 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
// non-test code is pretty low.
|
||||
emptyUI := func() { ui.SetState(domain.AppState{}) }
|
||||
|
||||
containerClient, err := container.NewClient(ctx, params.DockerClient, logger.With("component", "container_client"))
|
||||
containerClient, err := container.NewClient(ctx, a.dockerClient, a.logger.With("component", "container_client"))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("create container client: %w", err)
|
||||
|
||||
@ -106,7 +128,7 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
TLSKeyPath: tlsKeyPath,
|
||||
StreamKey: mediaserver.StreamKey(cfg.Sources.MediaServer.StreamKey),
|
||||
ContainerClient: containerClient,
|
||||
Logger: logger.With("component", "mediaserver"),
|
||||
Logger: a.logger.With("component", "mediaserver"),
|
||||
})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("create mediaserver: %w", err)
|
||||
@ -120,7 +142,7 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
repl := replicator.StartActor(ctx, replicator.StartActorParams{
|
||||
SourceURL: srv.RTMPInternalURL(),
|
||||
ContainerClient: containerClient,
|
||||
Logger: logger.With("component", "replicator"),
|
||||
Logger: a.logger.With("component", "replicator"),
|
||||
})
|
||||
defer repl.Close()
|
||||
|
||||
@ -146,16 +168,16 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
|
||||
eventBus.Send(event.MediaServerStartedEvent{RTMPURL: srv.RTMPURL(), RTMPSURL: srv.RTMPSURL()})
|
||||
}
|
||||
case <-params.ConfigService.C():
|
||||
case <-a.configService.C():
|
||||
// No-op, config updates are handled synchronously for now.
|
||||
case cmd, ok := <-ui.C():
|
||||
if !ok {
|
||||
// TODO: keep UI open until all containers have closed
|
||||
logger.Info("UI closed")
|
||||
a.logger.Info("UI closed")
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Debug("Command received", "cmd", cmd.Name())
|
||||
a.logger.Debug("Command received", "cmd", cmd.Name())
|
||||
switch c := cmd.(type) {
|
||||
case domain.CommandAddDestination:
|
||||
newCfg := cfg
|
||||
@ -163,8 +185,8 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
Name: c.DestinationName,
|
||||
URL: c.URL,
|
||||
})
|
||||
if err := params.ConfigService.SetConfig(newCfg); err != nil {
|
||||
logger.Error("Config update failed", "err", err)
|
||||
if err := a.configService.SetConfig(newCfg); err != nil {
|
||||
a.logger.Error("Config update failed", "err", err)
|
||||
ui.ConfigUpdateFailed(err)
|
||||
continue
|
||||
}
|
||||
@ -177,8 +199,8 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
newCfg.Destinations = slices.DeleteFunc(newCfg.Destinations, func(dest config.Destination) bool {
|
||||
return dest.URL == c.URL
|
||||
})
|
||||
if err := params.ConfigService.SetConfig(newCfg); err != nil {
|
||||
logger.Error("Config update failed", "err", err)
|
||||
if err := a.configService.SetConfig(newCfg); err != nil {
|
||||
a.logger.Error("Config update failed", "err", err)
|
||||
ui.ConfigUpdateFailed(err)
|
||||
continue
|
||||
}
|
||||
@ -200,11 +222,11 @@ func Run(ctx context.Context, params RunParams) error {
|
||||
case <-uiUpdateT.C:
|
||||
updateUI()
|
||||
case serverState := <-srv.C():
|
||||
logger.Debug("Server state received", "state", serverState)
|
||||
a.logger.Debug("Server state received", "state", serverState)
|
||||
applyServerState(serverState, state)
|
||||
updateUI()
|
||||
case replState := <-repl.C():
|
||||
logger.Debug("Replicator state received", "state", replState)
|
||||
a.logger.Debug("Replicator state received", "state", replState)
|
||||
destErrors := applyReplicatorState(replState, state)
|
||||
|
||||
for _, destError := range destErrors {
|
||||
|
@ -31,10 +31,10 @@ func buildAppParams(
|
||||
screen tcell.SimulationScreen,
|
||||
screenCaptureC chan<- terminal.ScreenCapture,
|
||||
logger *slog.Logger,
|
||||
) app.RunParams {
|
||||
) app.Params {
|
||||
t.Helper()
|
||||
|
||||
return app.RunParams{
|
||||
return app.Params{
|
||||
ConfigService: configService,
|
||||
DockerClient: dockerClient,
|
||||
Screen: &terminal.Screen{
|
||||
|
@ -132,7 +132,7 @@ func testIntegration(t *testing.T, mediaServerConfig config.MediaServerSource) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
err := app.Run(ctx, app.RunParams{
|
||||
require.NoError(t, app.New(app.Params{
|
||||
ConfigService: configService,
|
||||
DockerClient: dockerClient,
|
||||
Screen: &terminal.Screen{
|
||||
@ -144,8 +144,7 @@ func testIntegration(t *testing.T, mediaServerConfig config.MediaServerSource) {
|
||||
ClipboardAvailable: false,
|
||||
BuildInfo: domain.BuildInfo{Version: "0.0.1", GoVersion: "go1.16.3"},
|
||||
Logger: logger,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}).Run(ctx))
|
||||
}()
|
||||
|
||||
require.EventuallyWithT(
|
||||
@ -320,7 +319,7 @@ func TestIntegrationCustomHost(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
@ -391,7 +390,7 @@ func TestIntegrationCustomTLSCerts(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
require.EventuallyWithT(
|
||||
@ -472,7 +471,7 @@ func TestIntegrationRestartDestination(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
require.EventuallyWithT(
|
||||
@ -609,7 +608,7 @@ func TestIntegrationStartDestinationFailed(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
require.EventuallyWithT(
|
||||
@ -682,7 +681,7 @@ func TestIntegrationDestinationValidations(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
require.EventuallyWithT(
|
||||
@ -824,7 +823,7 @@ func TestIntegrationStartupCheck(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
require.EventuallyWithT(
|
||||
@ -893,7 +892,7 @@ func TestIntegrationMediaServerError(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
require.EventuallyWithT(
|
||||
@ -934,7 +933,7 @@ func TestIntegrationDockerClientError(t *testing.T) {
|
||||
|
||||
require.EqualError(
|
||||
t,
|
||||
app.Run(ctx, buildAppParams(t, configService, &dockerClient, screen, screenCaptureC, logger)),
|
||||
app.New(buildAppParams(t, configService, &dockerClient, screen, screenCaptureC, logger)).Run(ctx),
|
||||
"create container client: network create: boom",
|
||||
)
|
||||
}()
|
||||
@ -974,7 +973,7 @@ func TestIntegrationDockerConnectionError(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
err := app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger))
|
||||
err := app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx)
|
||||
require.ErrorContains(t, err, "dial tcp: lookup docker.example.com")
|
||||
require.ErrorContains(t, err, "no such host")
|
||||
}()
|
||||
@ -1070,7 +1069,7 @@ func TestIntegrationCopyURLs(t *testing.T) {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
require.NoError(t, app.Run(ctx, buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)))
|
||||
require.NoError(t, app.New(buildAppParams(t, configService, dockerClient, screen, screenCaptureC, logger)).Run(ctx))
|
||||
}()
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
29
main.go
29
main.go
@ -97,22 +97,21 @@ func run(ctx context.Context) error {
|
||||
return fmt.Errorf("read build info: %w", err)
|
||||
}
|
||||
|
||||
return app.Run(
|
||||
ctx,
|
||||
app.RunParams{
|
||||
ConfigService: configService,
|
||||
DockerClient: dockerClient,
|
||||
ClipboardAvailable: clipboardAvailable,
|
||||
ConfigFilePath: configService.Path(),
|
||||
BuildInfo: domain.BuildInfo{
|
||||
GoVersion: buildInfo.GoVersion,
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
Date: date,
|
||||
},
|
||||
Logger: logger,
|
||||
app := app.New(app.Params{
|
||||
ConfigService: configService,
|
||||
DockerClient: dockerClient,
|
||||
ClipboardAvailable: clipboardAvailable,
|
||||
ConfigFilePath: configService.Path(),
|
||||
BuildInfo: domain.BuildInfo{
|
||||
GoVersion: buildInfo.GoVersion,
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
Date: date,
|
||||
},
|
||||
)
|
||||
Logger: logger,
|
||||
})
|
||||
|
||||
return app.Run(ctx)
|
||||
}
|
||||
|
||||
// editConfigFile opens the config file in the user's editor.
|
||||
|
Loading…
x
Reference in New Issue
Block a user