mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
[v3] Service API cleanup and comments (#4024)
* Gather and document service API * Update changelog * Add NewServiceWithOptions * Revert static analyser change * Remove infinite loop in NewService[WithOptions] * Fix compiler warning in bindings command * Add test for NewServiceWithOptions * Update changelog * Fix service example --------- Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
parent
547e30f025
commit
16ce1d3448
12 changed files with 113 additions and 49 deletions
|
|
@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Add `window-call` example to demonstrate how to know which window is calling a service by [@leaanthony](https://github.com/leaanthony)
|
||||
- Better panic handling by [@leaanthony](https://github.com/leaanthony)
|
||||
- New Menu guide by [@leaanthony](https://github.com/leaanthony)
|
||||
- Add doc comments for Service API by [@fbbdev](https://github.com/fbbdev) in [#4024](https://github.com/wailsapp/wails/pull/4024)
|
||||
- Add function `application.NewServiceWithOptions` to initialise services with additional configuration by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
@ -52,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Removed `application.WindowIDKey` and `application.WindowNameKey` (replaced by `application.WindowKey`) by [@leaanthony](https://github.com/leaanthony)
|
||||
- In JS/TS bindings, class fields of fixed-length array types are now initialized with their expected length instead of being empty by [@fbbdev](https://github.com/fbbdev) in [#4001](https://github.com/wailsapp/wails/pull/4001)
|
||||
- ContextMenuData now returns a string instead of any by [@leaanthony](https://github.com/leaanthony)
|
||||
- `application.NewService` does not accept options as an optional parameter anymore (use `application.NewServiceWithOptions` instead) by [@leaanthony](https://github.com/leaanthony) in [#4024](https://github.com/wailsapp/wails/pull/4024)
|
||||
|
||||
## v3.0.0-alpha.9 - 2025-01-13
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,24 @@ app := application.New(application.Options{
|
|||
})
|
||||
```
|
||||
|
||||
This registers the `NewMyService` function as a service with the application.
|
||||
|
||||
Services may also be registered with additional options:
|
||||
|
||||
```go
|
||||
app := application.New(application.Options{
|
||||
Services: []application.Service{
|
||||
application.NewServiceWithOptions(NewMyService(), application.ServiceOptions{
|
||||
// ...
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
ServiceOptions has the following fields:
|
||||
- Name - Specify a custom name for the Service
|
||||
- Route - A route to bind the Service to the frontend (more on this below)
|
||||
|
||||
## Optional Methods
|
||||
|
||||
Services can implement optional methods to hook into the application lifecycle.
|
||||
|
|
@ -67,8 +85,10 @@ bindings generated for a service, so they are not exposed to your frontend.
|
|||
func (s *Service) ServiceName() string
|
||||
```
|
||||
|
||||
This method returns the name of the service. It is used for logging purposes
|
||||
only.
|
||||
This method returns the name of the service. By default, it will the struct name of the Service but can be
|
||||
overridden with the `Name` field of the `ServiceOptions`.
|
||||
|
||||
It is used for logging purposes only.
|
||||
|
||||
### ServiceStartup
|
||||
|
||||
|
|
@ -101,7 +121,7 @@ your service to act as an HTTP handler. The route of the handler is defined in
|
|||
the service options:
|
||||
|
||||
```go
|
||||
application.NewService(fileserver.New(&fileserver.Config{
|
||||
application.NewServiceWithOptions(fileserver.New(&fileserver.Config{
|
||||
RootPath: rootPath,
|
||||
}), application.ServiceOptions{
|
||||
Route: "/files",
|
||||
|
|
@ -144,7 +164,7 @@ We can now use this service in our application:
|
|||
```go
|
||||
app := application.New(application.Options{
|
||||
Services: []application.Service{
|
||||
application.NewService(fileserver.New(&fileserver.Config{
|
||||
application.NewServiceWithOptions(fileserver.New(&fileserver.Config{
|
||||
RootPath: rootPath,
|
||||
}), application.ServiceOptions{
|
||||
Route: "/files",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func main() {
|
|||
AutoSave: true,
|
||||
})),
|
||||
application.NewService(log.New()),
|
||||
application.NewService(fileserver.New(&fileserver.Config{
|
||||
application.NewServiceWithOptions(fileserver.New(&fileserver.Config{
|
||||
RootPath: rootPath,
|
||||
}), application.ServiceOptions{
|
||||
Route: "/files",
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ func GenerateBindings(options *flags.GenerateBindingsOptions, patterns []string)
|
|||
if spinner != nil {
|
||||
spinner.Info(resultMessage)
|
||||
} else {
|
||||
term.Infofln(resultMessage)
|
||||
term.Infofln("%s", resultMessage)
|
||||
}
|
||||
|
||||
// Report output directory.
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ import (
|
|||
// ErrNoContextPackage indicates that
|
||||
// the canonical path for the standard context package
|
||||
// did not match any actual package.
|
||||
var ErrNoContextPackage = errors.New("standard context package not found at canonical import path ('context'): is the Wails v3 module properly installed?")
|
||||
var ErrNoContextPackage = errors.New("standard context package not found at canonical import path ('context'): is the Wails v3 module properly installed? ")
|
||||
|
||||
// ErrNoApplicationPackage indicates that
|
||||
// the canonical path for the Wails application package
|
||||
// did not match any actual package.
|
||||
var ErrNoApplicationPackage = errors.New("Wails application package not found at canonical import path ('" + config.WailsAppPkgPath + "'): is the Wails v3 module properly installed?")
|
||||
var ErrNoApplicationPackage = errors.New("Wails application package not found at canonical import path ('" + config.WailsAppPkgPath + "'): is the Wails v3 module properly installed? ")
|
||||
|
||||
// ErrBadApplicationPackage indicates that
|
||||
// the Wails application package has invalid content.
|
||||
var ErrBadApplicationPackage = errors.New("package " + config.WailsAppPkgPath + ": function NewService has wrong signature: is the Wails v3 module properly installed?")
|
||||
var ErrBadApplicationPackage = errors.New("package " + config.WailsAppPkgPath + ": function NewService has wrong signature: is the Wails v3 module properly installed? ")
|
||||
|
||||
// ErrNoPackages is returned by [Generator.Generate]
|
||||
// when [LoadPackages] returns no error and no packages.
|
||||
|
|
@ -43,7 +43,7 @@ type ErrorReport struct {
|
|||
errors map[string]bool
|
||||
}
|
||||
|
||||
// NewError report initialises an ErrorReport instance
|
||||
// NewErrorReport report initialises an ErrorReport instance
|
||||
// with the provided Logger implementation.
|
||||
//
|
||||
// If logger is nil, messages will be accumulated but not logged.
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@
|
|||
".Service10",
|
||||
".Service11",
|
||||
".Service12",
|
||||
"/other.Service13"
|
||||
".Service13",
|
||||
"/other.Service14"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package main
|
|||
|
||||
import "github.com/wailsapp/wails/v3/pkg/application"
|
||||
|
||||
func ServiceInitialiser[T any]() func(*T, ...application.ServiceOptions) application.Service {
|
||||
func ServiceInitialiser[T any]() func(*T) application.Service {
|
||||
return application.NewService[T]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type Service9 struct{}
|
|||
type Service10 struct{}
|
||||
type Service11 struct{}
|
||||
type Service12 struct{}
|
||||
type Service13 struct{}
|
||||
|
||||
func main() {
|
||||
factory := NewFactory[Service1, Service2]()
|
||||
|
|
@ -36,6 +37,7 @@ func main() {
|
|||
ServiceInitialiser[Service6]()(&Service6{}),
|
||||
other.CustomNewService(Service7{}),
|
||||
other.ServiceInitialiser[Service8]()(&Service8{}),
|
||||
application.NewServiceWithOptions(&Service13{}, application.ServiceOptions{Name: "custom name"}),
|
||||
other.LocalService,
|
||||
},
|
||||
CustomNewServices[Service9, Service10]()...),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ func CustomNewService[T any](srv T) application.Service {
|
|||
return application.NewService(&srv)
|
||||
}
|
||||
|
||||
func ServiceInitialiser[T any]() func(*T, ...application.ServiceOptions) application.Service {
|
||||
func ServiceInitialiser[T any]() func(*T) application.Service {
|
||||
return application.NewService[T]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ package other
|
|||
|
||||
import "github.com/wailsapp/wails/v3/pkg/application"
|
||||
|
||||
type Service13 int
|
||||
type Service14 int
|
||||
|
||||
var LocalService = application.NewService(new(Service13))
|
||||
var LocalService = application.NewService(new(Service14))
|
||||
|
|
|
|||
|
|
@ -8,40 +8,6 @@ import (
|
|||
"github.com/wailsapp/wails/v3/internal/assetserver"
|
||||
)
|
||||
|
||||
// Service wraps a bound type instance.
|
||||
// The zero value of Service is invalid.
|
||||
// Valid values may only be obtained by calling [NewService].
|
||||
type Service struct {
|
||||
instance any
|
||||
options ServiceOptions
|
||||
}
|
||||
|
||||
type ServiceOptions struct {
|
||||
// Name can be set to override the name of the service
|
||||
// This is useful for logging and debugging purposes
|
||||
Name string
|
||||
// Route is the path to the assets
|
||||
Route string
|
||||
}
|
||||
|
||||
var DefaultServiceOptions = ServiceOptions{
|
||||
Route: "",
|
||||
}
|
||||
|
||||
// NewService returns a Service value wrapping the given pointer.
|
||||
// If T is not a named type, the returned value is invalid.
|
||||
// The prefix is used if Service implements a http.Handler only one allowed
|
||||
func NewService[T any](instance *T, options ...ServiceOptions) Service {
|
||||
if len(options) == 1 {
|
||||
return Service{instance, options[0]}
|
||||
}
|
||||
return Service{instance, DefaultServiceOptions}
|
||||
}
|
||||
|
||||
func (s Service) Instance() any {
|
||||
return s.instance
|
||||
}
|
||||
|
||||
// Options contains the options for the application
|
||||
type Options struct {
|
||||
// Name is the name of the application (used in the default about box)
|
||||
|
|
|
|||
|
|
@ -5,14 +5,86 @@ import (
|
|||
"reflect"
|
||||
)
|
||||
|
||||
// Service wraps a bound type instance.
|
||||
// The zero value of Service is invalid.
|
||||
// Valid values may only be obtained by calling [NewService].
|
||||
type Service struct {
|
||||
instance any
|
||||
options ServiceOptions
|
||||
}
|
||||
|
||||
// ServiceOptions provides optional parameters for calls to [NewService].
|
||||
type ServiceOptions struct {
|
||||
// Name can be set to override the name of the service
|
||||
// for logging and debugging purposes.
|
||||
//
|
||||
// If empty, it will default
|
||||
// either to the value obtained through the [ServiceName] interface,
|
||||
// or to the type name.
|
||||
Name string
|
||||
|
||||
// If the service instance implements [http.Handler],
|
||||
// it will be mounted on the internal asset server
|
||||
// at the prefix specified by Route.
|
||||
Route string
|
||||
}
|
||||
|
||||
// DefaultServiceOptions specifies the default values of service options,
|
||||
// used when no [ServiceOptions] instance is provided to [NewService].
|
||||
var DefaultServiceOptions = ServiceOptions{}
|
||||
|
||||
// NewService returns a Service value wrapping the given pointer.
|
||||
// If T is not a concrete named type, the returned value is invalid.
|
||||
func NewService[T any](instance *T) Service {
|
||||
return Service{instance, DefaultServiceOptions}
|
||||
}
|
||||
|
||||
// NewServiceWithOptions returns a Service value wrapping the given pointer
|
||||
// and specifying the given service options.
|
||||
// If T is not a concrete named type, the returned value is invalid.
|
||||
func NewServiceWithOptions[T any](instance *T, options ServiceOptions) Service {
|
||||
service := NewService(instance) // Delegate to NewService so that the static analyser may detect T. Do not remove this call.
|
||||
service.options = options
|
||||
return service
|
||||
}
|
||||
|
||||
// Instance returns the service instance provided to [NewService].
|
||||
func (s Service) Instance() any {
|
||||
return s.instance
|
||||
}
|
||||
|
||||
// ServiceName returns the name of the service
|
||||
//
|
||||
// This is an *optional* method that may be implemented by service instances.
|
||||
// It is used for logging and debugging purposes.
|
||||
//
|
||||
// If a non-empty name is provided with [ServiceOptions],
|
||||
// it takes precedence over the one returned by the ServiceName method.
|
||||
type ServiceName interface {
|
||||
ServiceName() string
|
||||
}
|
||||
|
||||
// ServiceStartup is an *optional* method that may be implemented by service instances.
|
||||
//
|
||||
// This method will be called during application startup and will receive a copy of the options
|
||||
// specified at creation time. It can be used for initialising resources.
|
||||
//
|
||||
// The context will be valid as long as the application is running,
|
||||
// and will be canceled right before shutdown.
|
||||
//
|
||||
// If the return value is non-nil, it is logged along with the service name,
|
||||
// the startup process aborts and the application quits.
|
||||
// When that happens, service instances that have been already initialised
|
||||
// receive a shutdown notification.
|
||||
type ServiceStartup interface {
|
||||
ServiceStartup(ctx context.Context, options ServiceOptions) error
|
||||
}
|
||||
|
||||
// ServiceShutdown is an *optional* method that may be implemented by service instances.
|
||||
//
|
||||
// This method will be called during application shutdown. It can be used for cleaning up resources.
|
||||
//
|
||||
// If the return value is non-nil, it is logged along with the service name.
|
||||
type ServiceShutdown interface {
|
||||
ServiceShutdown() error
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue