Skip to content

Commit 4deee42

Browse files
committed
feat: [#783] Add bootstrap.Run(...facade) function to optimize the app running process
1 parent e78715c commit 4deee42

File tree

4 files changed

+233
-66
lines changed

4 files changed

+233
-66
lines changed

contracts/foundation/application.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ type Application interface {
4242
Boot()
4343
// Commands register the given commands with the console application.
4444
Commands([]console.Command)
45+
// Context gets the application context.
46+
Context() context.Context
4547
// GetJson get the JSON implementation.
4648
GetJson() Json
4749
// IsLocale get the current application locale.
@@ -50,6 +52,8 @@ type Application interface {
5052
Publishes(packageName string, paths map[string]string, groups ...string)
5153
// Refresh all modules after changing config, will call the Boot method simultaneously.
5254
Refresh()
55+
// Run runs modules.
56+
Run()
5357
// SetJson set the JSON implementation.
5458
SetJson(json Json)
5559
// SetLocale set the current application locale.

foundation/application.go

Lines changed: 146 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import (
66
"fmt"
77
"maps"
88
"os"
9+
"os/signal"
910
"path/filepath"
1011
"slices"
1112
"sort"
1213
"strings"
14+
"syscall"
1315

1416
"github.com/goravel/framework/config"
1517
frameworkconsole "github.com/goravel/framework/console"
@@ -36,8 +38,15 @@ func init() {
3638
setEnv()
3739
setRootPath()
3840

41+
ctx, cancel := context.WithCancel(context.Background())
42+
3943
app := &Application{
40-
Container: NewContainer(),
44+
Container: NewContainer(),
45+
46+
ctx: ctx,
47+
cancel: cancel,
48+
quit: make(chan os.Signal, 1),
49+
4150
publishes: make(map[string]map[string]string),
4251
publishGroups: make(map[string]map[string]string),
4352
}
@@ -50,6 +59,11 @@ func init() {
5059

5160
type Application struct {
5261
*Container
62+
63+
ctx context.Context
64+
cancel context.CancelFunc
65+
quit chan os.Signal
66+
5367
configuredServiceProviders []foundation.ServiceProvider
5468
publishes map[string]map[string]string
5569
publishGroups map[string]map[string]string
@@ -61,6 +75,10 @@ func NewApplication() foundation.Application {
6175
return App
6276
}
6377

78+
func (r *Application) About(section string, items []foundation.AboutItem) {
79+
console.AddAboutInformation(section, items...)
80+
}
81+
6482
func (r *Application) Boot() {
6583
r.configuredServiceProviders = r.configuredServiceProviders[:0]
6684
clear(r.publishes)
@@ -87,75 +105,132 @@ func (r *Application) Commands(commands []contractsconsole.Command) {
87105
r.registerCommands(commands)
88106
}
89107

90-
func (r *Application) Path(path ...string) string {
91-
return internals.Path(path...)
108+
func (r *Application) Context() context.Context {
109+
return r.ctx
92110
}
93111

94-
func (r *Application) BasePath(path ...string) string {
95-
return r.absPath(path...)
112+
func (r *Application) GetJson() foundation.Json {
113+
return r.json
96114
}
97115

98-
func (r *Application) ConfigPath(path ...string) string {
99-
path = append([]string{support.RelativePath, "config"}, path...)
100-
return r.absPath(path...)
116+
func (r *Application) IsLocale(ctx context.Context, locale string) bool {
117+
return r.CurrentLocale(ctx) == locale
101118
}
102119

103-
func (r *Application) DatabasePath(path ...string) string {
104-
path = append([]string{support.RelativePath, "database"}, path...)
105-
return r.absPath(path...)
120+
func (r *Application) Publishes(packageName string, paths map[string]string, groups ...string) {
121+
if _, exist := r.publishes[packageName]; !exist {
122+
r.publishes[packageName] = make(map[string]string)
123+
}
124+
maps.Copy(r.publishes[packageName], paths)
125+
for _, group := range groups {
126+
r.addPublishGroup(group, paths)
127+
}
106128
}
107129

108-
func (r *Application) ExecutablePath(path ...string) string {
109-
path = append([]string{support.RootPath}, path...)
110-
return r.absPath(path...)
130+
func (r *Application) Refresh() {
131+
r.Fresh()
132+
r.Boot()
111133
}
112134

113-
func (r *Application) FacadesPath(path ...string) string {
114-
return internals.FacadesPath(path...)
115-
}
135+
func (r *Application) Run() {
136+
signal.Notify(r.quit, syscall.SIGINT, syscall.SIGTERM)
116137

117-
func (r *Application) StoragePath(path ...string) string {
118-
path = append([]string{support.RelativePath, "storage"}, path...)
119-
return r.absPath(path...)
120-
}
138+
log := r.MakeLog()
139+
route := r.MakeRoute()
140+
grpc := r.MakeGrpc()
141+
queue := r.MakeQueue()
142+
schedule := r.MakeSchedule()
121143

122-
func (r *Application) Refresh() {
123-
r.Fresh()
124-
r.Boot()
144+
go func() {
145+
<-r.quit
146+
r.cancel()
147+
}()
148+
149+
go func() {
150+
if route != nil {
151+
if err := route.Run(); err != nil {
152+
log.Errorf("Route Run error: %v", err)
153+
}
154+
}
155+
156+
if grpc != nil {
157+
if err := grpc.Run(); err != nil {
158+
log.Errorf("Grpc Run error: %v", err)
159+
}
160+
}
161+
162+
if queue != nil {
163+
if err := queue.Worker().Run(); err != nil {
164+
log.Errorf("Queue run error: %v", err)
165+
}
166+
}
167+
168+
if schedule != nil {
169+
go schedule.Run()
170+
}
171+
}()
172+
173+
go func() {
174+
<-r.ctx.Done()
175+
176+
if route != nil {
177+
if err := route.Shutdown(); err != nil {
178+
log.Errorf("Route Shutdown error: %v", err)
179+
}
180+
}
181+
182+
if grpc != nil {
183+
if err := grpc.Shutdown(); err != nil {
184+
log.Errorf("Grpc Shutdown error: %v", err)
185+
}
186+
}
187+
188+
if queue != nil {
189+
if err := queue.Worker().Shutdown(); err != nil {
190+
log.Errorf("Queue Shutdown error: %v", err)
191+
}
192+
}
193+
194+
if schedule != nil {
195+
if err := schedule.Shutdown(); err != nil {
196+
log.Errorf("Schedule Shutdown error: %v", err)
197+
}
198+
}
199+
}()
125200
}
126201

127-
func (r *Application) ResourcePath(path ...string) string {
128-
path = append([]string{support.RelativePath, "resources"}, path...)
129-
return r.absPath(path...)
202+
func (r *Application) SetJson(j foundation.Json) {
203+
if j != nil {
204+
r.json = j
205+
}
130206
}
131207

132-
func (r *Application) LangPath(path ...string) string {
133-
defaultPath := "lang"
134-
if configFacade := r.MakeConfig(); configFacade != nil {
135-
defaultPath = configFacade.GetString("app.lang_path", defaultPath)
208+
func (r *Application) SetLocale(ctx context.Context, locale string) context.Context {
209+
lang := r.MakeLang(ctx)
210+
if lang == nil {
211+
color.Errorln("Lang facade not initialized.")
212+
return ctx
136213
}
137214

138-
path = append([]string{support.RelativePath, defaultPath}, path...)
139-
return r.absPath(path...)
215+
return lang.SetLocale(locale)
140216
}
141217

142-
func (r *Application) PublicPath(path ...string) string {
143-
path = append([]string{support.RelativePath, "public"}, path...)
218+
func (r *Application) Version() string {
219+
return support.Version
220+
}
221+
222+
func (r *Application) BasePath(path ...string) string {
144223
return r.absPath(path...)
145224
}
146225

147-
func (r *Application) Publishes(packageName string, paths map[string]string, groups ...string) {
148-
if _, exist := r.publishes[packageName]; !exist {
149-
r.publishes[packageName] = make(map[string]string)
150-
}
151-
maps.Copy(r.publishes[packageName], paths)
152-
for _, group := range groups {
153-
r.addPublishGroup(group, paths)
154-
}
226+
func (r *Application) ConfigPath(path ...string) string {
227+
path = append([]string{support.RelativePath, "config"}, path...)
228+
return r.absPath(path...)
155229
}
156230

157-
func (r *Application) Version() string {
158-
return support.Version
231+
func (r *Application) DatabasePath(path ...string) string {
232+
path = append([]string{support.RelativePath, "database"}, path...)
233+
return r.absPath(path...)
159234
}
160235

161236
func (r *Application) CurrentLocale(ctx context.Context) string {
@@ -168,32 +243,42 @@ func (r *Application) CurrentLocale(ctx context.Context) string {
168243
return lang.CurrentLocale()
169244
}
170245

171-
func (r *Application) SetLocale(ctx context.Context, locale string) context.Context {
172-
lang := r.MakeLang(ctx)
173-
if lang == nil {
174-
color.Errorln("Lang facade not initialized.")
175-
return ctx
176-
}
246+
func (r *Application) ExecutablePath(path ...string) string {
247+
path = append([]string{support.RootPath}, path...)
248+
return r.absPath(path...)
249+
}
177250

178-
return lang.SetLocale(locale)
251+
func (r *Application) FacadesPath(path ...string) string {
252+
return internals.FacadesPath(path...)
179253
}
180254

181-
func (r *Application) SetJson(j foundation.Json) {
182-
if j != nil {
183-
r.json = j
255+
func (r *Application) LangPath(path ...string) string {
256+
defaultPath := "lang"
257+
if configFacade := r.MakeConfig(); configFacade != nil {
258+
defaultPath = configFacade.GetString("app.lang_path", defaultPath)
184259
}
260+
261+
path = append([]string{support.RelativePath, defaultPath}, path...)
262+
return r.absPath(path...)
185263
}
186264

187-
func (r *Application) GetJson() foundation.Json {
188-
return r.json
265+
func (r *Application) Path(path ...string) string {
266+
return internals.Path(path...)
189267
}
190268

191-
func (r *Application) About(section string, items []foundation.AboutItem) {
192-
console.AddAboutInformation(section, items...)
269+
func (r *Application) PublicPath(path ...string) string {
270+
path = append([]string{support.RelativePath, "public"}, path...)
271+
return r.absPath(path...)
193272
}
194273

195-
func (r *Application) IsLocale(ctx context.Context, locale string) bool {
196-
return r.CurrentLocale(ctx) == locale
274+
func (r *Application) ResourcePath(path ...string) string {
275+
path = append([]string{support.RelativePath, "resources"}, path...)
276+
return r.absPath(path...)
277+
}
278+
279+
func (r *Application) StoragePath(path ...string) string {
280+
path = append([]string{support.RelativePath, "storage"}, path...)
281+
return r.absPath(path...)
197282
}
198283

199284
func (r *Application) absPath(paths ...string) string {

foundation/application_test.go

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@ package foundation
33
import (
44
"os"
55
"path/filepath"
6+
"syscall"
67
"testing"
8+
"time"
79

810
"github.com/stretchr/testify/assert"
911
"github.com/stretchr/testify/suite"
1012

1113
"github.com/goravel/framework/contracts/binding"
1214
"github.com/goravel/framework/contracts/foundation"
1315
mocksconfig "github.com/goravel/framework/mocks/config"
16+
mocksgrpc "github.com/goravel/framework/mocks/grpc"
17+
mockslog "github.com/goravel/framework/mocks/log"
18+
mocksqueue "github.com/goravel/framework/mocks/queue"
19+
mocksroute "github.com/goravel/framework/mocks/route"
20+
mocksschedule "github.com/goravel/framework/mocks/schedule"
1421
"github.com/goravel/framework/support"
15-
"github.com/goravel/framework/support/file"
1622
)
1723

1824
type ApplicationTestSuite struct {
@@ -21,11 +27,7 @@ type ApplicationTestSuite struct {
2127
}
2228

2329
func TestApplicationTestSuite(t *testing.T) {
24-
assert.Nil(t, file.PutContent(support.EnvFilePath, "APP_KEY=12345678901234567890123456789012"))
25-
2630
suite.Run(t, new(ApplicationTestSuite))
27-
28-
assert.Nil(t, file.Remove(support.EnvFilePath))
2931
}
3032

3133
func (s *ApplicationTestSuite) SetupTest() {
@@ -88,6 +90,50 @@ func (s *ApplicationTestSuite) TestExecutablePath() {
8890
s.Equal(filepath.Join(path, "test", "test2/test3"), executable3)
8991
}
9092

93+
func (s *ApplicationTestSuite) TestRun() {
94+
mockRoute := mocksroute.NewRoute(s.T())
95+
mockRoute.EXPECT().Run().Return(nil).Once()
96+
mockRoute.EXPECT().Shutdown().Return(nil).Once()
97+
s.app.Singleton(binding.Route, func(app foundation.Application) (any, error) {
98+
return mockRoute, nil
99+
})
100+
101+
mockGrpc := mocksgrpc.NewGrpc(s.T())
102+
mockGrpc.EXPECT().Run().Return(nil).Once()
103+
mockGrpc.EXPECT().Shutdown().Return(nil).Once()
104+
s.app.Singleton(binding.Grpc, func(app foundation.Application) (any, error) {
105+
return mockGrpc, nil
106+
})
107+
108+
mockQueue := mocksqueue.NewQueue(s.T())
109+
mockWorker := mocksqueue.NewWorker(s.T())
110+
mockQueue.EXPECT().Worker().Return(mockWorker).Twice()
111+
mockWorker.EXPECT().Run().Return(nil).Once()
112+
mockWorker.EXPECT().Shutdown().Return(nil).Once()
113+
s.app.Singleton(binding.Queue, func(app foundation.Application) (any, error) {
114+
return mockQueue, nil
115+
})
116+
117+
mockSchedule := mocksschedule.NewSchedule(s.T())
118+
mockSchedule.EXPECT().Run().Once()
119+
mockSchedule.EXPECT().Shutdown().Return(nil).Once()
120+
s.app.Singleton(binding.Schedule, func(app foundation.Application) (any, error) {
121+
return mockSchedule, nil
122+
})
123+
124+
mockLog := mockslog.NewLog(s.T())
125+
s.app.Singleton(binding.Log, func(app foundation.Application) (any, error) {
126+
return mockLog, nil
127+
})
128+
129+
s.app.Run()
130+
time.Sleep(100 * time.Millisecond) // Wait for goroutines to start
131+
132+
s.app.quit <- syscall.SIGINT
133+
134+
time.Sleep(100 * time.Millisecond) // Wait for goroutines to end
135+
}
136+
91137
func (s *ApplicationTestSuite) TestPublishes() {
92138
s.app.Publishes("github.com/goravel/sms", map[string]string{
93139
"config.go": "config.go",

0 commit comments

Comments
 (0)