-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin.go
118 lines (100 loc) · 2.53 KB
/
plugin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package echo
import (
"context"
"fmt"
"net/http"
"sync"
"github.com/caarlos0/env/v8"
"github.com/labstack/echo/v4"
"github.com/bongnv/sen/pkg/sen"
)
// Bundle is a sen.Plugin that provides both Config and echo.Echo for convenience.
//
// # Usage
//
// app.With(echo.Module())
func Bundle() sen.Plugin {
return sen.Module(
&ConfigProvider{},
&Plugin{},
)
}
// Config includes configuration to initialize an echo server.
type Config struct {
Port string `env:"PORT,required" envDefault:"1323"`
}
// ConfigProvider is a plugin that provides Config for initializing echo.
//
// # Usage
//
// app.With(&echo.ConfigProvider{})
type ConfigProvider struct {
Hub sen.Hub `inject:"hub"`
}
// Initialize loads Config from environment variables and
// registers it to the application.
func (p ConfigProvider) Initialize() error {
cfg := &Config{}
if err := env.Parse(cfg); err != nil {
return err
}
return p.Hub.Register("echo.config", cfg)
}
// Plugin is a plugin that provides an instance of echo.Echo.
// The plugin requires Config is registered in advance.
//
// # Usage
//
// app.With(&echo.Plugin{
// Middlewares: middlewaresFuncs,
// })
type Plugin struct {
Middlewares []echo.MiddlewareFunc
// will be injected
LC sen.Lifecycle `inject:"lifecycle"`
Hub sen.Hub `inject:"hub"`
Cfg *Config `inject:"echo.config"`
}
// Initialize initializes and registers the echo.Echo instance with the provided middlewares.
func (p Plugin) Initialize() error {
e := echo.New()
e.Use(p.Middlewares...)
shutdownFn := runOnce(e.Shutdown)
p.LC.OnRun(func(ctx context.Context) error {
// since echo doesn't take context, we will need to handle it manually
// in case the context is cancelled, e.Shutdown() will be called.
go func() {
<-ctx.Done()
_ = shutdownFn(context.Background())
}()
err := e.Start(fmt.Sprintf(":%s", p.Cfg.Port))
if err != http.ErrServerClosed {
return err
}
return nil
})
p.LC.OnShutdown(func(ctx context.Context) error {
return shutdownFn(ctx)
})
return p.Hub.Register("echo", e)
}
// runOnce allows creates a function that will call fn only once.
// It's different from sync.Once that, all calls will be blocked and returns
// the error from the single call of fn.
func runOnce(fn func(ctx context.Context) error) func(ctx context.Context) error {
var err error
once := &sync.Once{}
done := make(chan struct{})
return func(ctx context.Context) error {
once.Do(func() {
err = fn(ctx)
close(done)
})
select {
case <-done:
return err
case <-ctx.Done():
return ctx.Err()
}
}
}