 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			365 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2019 Uber Technologies, Inc.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included in
 | |
| // all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
| // THE SOFTWARE.
 | |
| 
 | |
| package fx
 | |
| 
 | |
| import "go.uber.org/dig"
 | |
| 
 | |
| // In can be embedded in a constructor's parameter struct to take advantage of
 | |
| // advanced dependency injection features.
 | |
| //
 | |
| // Modules should take a single parameter struct that embeds an In in order to
 | |
| // provide a forward-compatible API: since adding fields to a struct is
 | |
| // backward-compatible, modules can then add optional dependencies in minor
 | |
| // releases.
 | |
| //
 | |
| // # Parameter Structs
 | |
| //
 | |
| // Fx constructors declare their dependencies as function parameters. This can
 | |
| // quickly become unreadable if the constructor has a lot of dependencies.
 | |
| //
 | |
| //	func NewHandler(users *UserGateway, comments *CommentGateway, posts *PostGateway, votes *VoteGateway, authz *AuthZGateway) *Handler {
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // To improve the readability of constructors like this, create a struct that
 | |
| // lists all the dependencies as fields and change the function to accept that
 | |
| // struct instead. The new struct is called a parameter struct.
 | |
| //
 | |
| // Fx has first class support for parameter structs: any struct embedding
 | |
| // fx.In gets treated as a parameter struct, so the individual fields in the
 | |
| // struct are supplied via dependency injection. Using a parameter struct, we
 | |
| // can make the constructor above much more readable:
 | |
| //
 | |
| //	type HandlerParams struct {
 | |
| //	  fx.In
 | |
| //
 | |
| //	  Users    *UserGateway
 | |
| //	  Comments *CommentGateway
 | |
| //	  Posts    *PostGateway
 | |
| //	  Votes    *VoteGateway
 | |
| //	  AuthZ    *AuthZGateway
 | |
| //	}
 | |
| //
 | |
| //	func NewHandler(p HandlerParams) *Handler {
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // Though it's rarely a good idea, constructors can receive any combination of
 | |
| // parameter structs and parameters.
 | |
| //
 | |
| //	func NewHandler(p HandlerParams, l *log.Logger) *Handler {
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // # Optional Dependencies
 | |
| //
 | |
| // Constructors often have optional dependencies on some types: if those types are
 | |
| // missing, they can operate in a degraded state. Fx supports optional
 | |
| // dependencies via the `optional:"true"` tag to fields on parameter structs.
 | |
| //
 | |
| //	type UserGatewayParams struct {
 | |
| //	  fx.In
 | |
| //
 | |
| //	  Conn  *sql.DB
 | |
| //	  Cache *redis.Client `optional:"true"`
 | |
| //	}
 | |
| //
 | |
| // If an optional field isn't available in the container, the constructor
 | |
| // receives the field's zero value.
 | |
| //
 | |
| //	func NewUserGateway(p UserGatewayParams, log *log.Logger) (*UserGateway, error) {
 | |
| //	  if p.Cache == nil {
 | |
| //	    log.Print("Caching disabled")
 | |
| //	  }
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // Constructors that declare optional dependencies MUST gracefully handle
 | |
| // situations in which those dependencies are absent.
 | |
| //
 | |
| // The optional tag also allows adding new dependencies without breaking
 | |
| // existing consumers of the constructor.
 | |
| //
 | |
| // # Named Values
 | |
| //
 | |
| // Some use cases require the application container to hold multiple values of
 | |
| // the same type. For details on producing named values, see the documentation
 | |
| // for the Out type.
 | |
| //
 | |
| // Fx allows functions to consume named values via the `name:".."` tag on
 | |
| // parameter structs. Note that both the name AND type of the fields on the
 | |
| // parameter struct must match the corresponding result struct.
 | |
| //
 | |
| //	type GatewayParams struct {
 | |
| //	  fx.In
 | |
| //
 | |
| //	  WriteToConn  *sql.DB `name:"rw"`
 | |
| //	  ReadFromConn *sql.DB `name:"ro"`
 | |
| //	}
 | |
| //
 | |
| // The name tag may be combined with the optional tag to declare the
 | |
| // dependency optional.
 | |
| //
 | |
| //	type GatewayParams struct {
 | |
| //	  fx.In
 | |
| //
 | |
| //	  WriteToConn  *sql.DB `name:"rw"`
 | |
| //	  ReadFromConn *sql.DB `name:"ro" optional:"true"`
 | |
| //	}
 | |
| //
 | |
| //	func NewCommentGateway(p GatewayParams, log *log.Logger) (*CommentGateway, error) {
 | |
| //	  if p.ReadFromConn == nil {
 | |
| //	    log.Print("Warning: Using RW connection for reads")
 | |
| //	    p.ReadFromConn = p.WriteToConn
 | |
| //	  }
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // # Value Groups
 | |
| //
 | |
| // To make it easier to produce and consume many values of the same type, Fx
 | |
| // supports named, unordered collections called value groups. For details on
 | |
| // producing value groups, see the documentation for the Out type.
 | |
| //
 | |
| // Functions can depend on a value group by requesting a slice tagged with
 | |
| // `group:".."`. This will execute all constructors that provide a value to
 | |
| // that group in an unspecified order, then collect all the results into a
 | |
| // single slice. Keep in mind that this makes the types of the parameter and
 | |
| // result struct fields different: if a group of constructors each returns
 | |
| // type T, parameter structs consuming the group must use a field of type []T.
 | |
| //
 | |
| //	type ServerParams struct {
 | |
| //	  fx.In
 | |
| //
 | |
| //	  Handlers []Handler `group:"server"`
 | |
| //	}
 | |
| //
 | |
| //	func NewServer(p ServerParams) *Server {
 | |
| //	  server := newServer()
 | |
| //	  for _, h := range p.Handlers {
 | |
| //	    server.Register(h)
 | |
| //	  }
 | |
| //	  return server
 | |
| //	}
 | |
| //
 | |
| // Note that values in a value group are unordered. Fx makes no guarantees
 | |
| // about the order in which these values will be produced.
 | |
| //
 | |
| // # Soft Value Groups
 | |
| //
 | |
| // A soft value group can be thought of as a best-attempt at populating the
 | |
| // group with values from constructors that have already run. In other words,
 | |
| // if a constructor's output type is only consumed by a soft value group,
 | |
| // it will not be run.
 | |
| //
 | |
| // Note that Fx does not guarantee precise execution order of constructors
 | |
| // or invokers, which means that the change in code that affects execution
 | |
| // ordering of other constructors or functions will affect the values
 | |
| // populated in this group.
 | |
| //
 | |
| // To declare a soft relationship between a group and its constructors, use
 | |
| // the `soft` option on the group tag (`group:"[groupname],soft"`).
 | |
| // This option is only valid for input parameters.
 | |
| //
 | |
| //	type Params struct {
 | |
| //	  fx.In
 | |
| //
 | |
| //	  Handlers []Handler `group:"server,soft"`
 | |
| //	  Logger   *zap.Logger
 | |
| //	}
 | |
| //
 | |
| //	NewHandlerAndLogger := func() (Handler, *zap.Logger) { ... }
 | |
| //	NewHandler := func() Handler { ... }
 | |
| //	Foo := func(Params) { ... }
 | |
| //
 | |
| //	app := fx.New(
 | |
| //	  fx.Provide(fx.Annotate(NewHandlerAndLogger, fx.ResultTags(`group:"server"`))),
 | |
| //	  fx.Provide(fx.Annotate(NewHandler, fx.ResultTags(`group::"server"`))),
 | |
| //	  fx.Invoke(Foo),
 | |
| //	)
 | |
| //
 | |
| // The only constructor called is `NewHandlerAndLogger`, because this also provides
 | |
| // `*zap.Logger` needed in the `Params` struct received by `Foo`. The Handlers
 | |
| // group will be populated with a single Handler returned by `NewHandlerAndLogger`.
 | |
| //
 | |
| // In the next example, the slice `s` isn't populated as the provider would be
 | |
| // called only because `strings` soft group value is its only consumer.
 | |
| //
 | |
| //	 app := fx.New(
 | |
| //	   fx.Provide(
 | |
| //	     fx.Annotate(
 | |
| //	       func() (string, int) { return "hello", 42 },
 | |
| //	       fx.ResultTags(`group:"strings"`),
 | |
| //	     ),
 | |
| //	   ),
 | |
| //	   fx.Invoke(
 | |
| //	     fx.Annotate(func(s []string) {
 | |
| //	       // s will be an empty slice
 | |
| //	     }, fx.ParamTags(`group:"strings,soft"`)),
 | |
| //	   ),
 | |
| //	)
 | |
| //
 | |
| // In the next example, the slice `s` will be populated because there is a
 | |
| // consumer for the same type which is not a `soft` dependency.
 | |
| //
 | |
| //	 app := fx.New(
 | |
| //	   fx.Provide(
 | |
| //	     fx.Annotate(
 | |
| //	       func() string { "hello" },
 | |
| //	       fx.ResultTags(`group:"strings"`),
 | |
| //	     ),
 | |
| //	   ),
 | |
| //	   fx.Invoke(
 | |
| //	     fx.Annotate(func(b []string) {
 | |
| //	       // b is []string{"hello"}
 | |
| //	     }, fx.ParamTags(`group:"strings"`)),
 | |
| //	   ),
 | |
| //	   fx.Invoke(
 | |
| //	     fx.Annotate(func(s []string) {
 | |
| //	       // s is []string{"hello"}
 | |
| //	     }, fx.ParamTags(`group:"strings,soft"`)),
 | |
| //	   ),
 | |
| //	)
 | |
| //
 | |
| // # Unexported fields
 | |
| //
 | |
| // By default, a type that embeds fx.In may not have any unexported fields. The
 | |
| // following will return an error if used with Fx.
 | |
| //
 | |
| //	type Params struct {
 | |
| //	  fx.In
 | |
| //
 | |
| //	  Logger *zap.Logger
 | |
| //	  mu     sync.Mutex
 | |
| //	}
 | |
| //
 | |
| // If you have need of unexported fields on such a type, you may opt-into
 | |
| // ignoring unexported fields by adding the ignore-unexported struct tag to the
 | |
| // fx.In. For example,
 | |
| //
 | |
| //	type Params struct {
 | |
| //	  fx.In `ignore-unexported:"true"`
 | |
| //
 | |
| //	  Logger *zap.Logger
 | |
| //	  mu     sync.Mutex
 | |
| //	}
 | |
| type In = dig.In
 | |
| 
 | |
| // Out is the inverse of In: it can be embedded in result structs to take
 | |
| // advantage of advanced features.
 | |
| //
 | |
| // Modules should return a single result struct that embeds an Out in order to
 | |
| // provide a forward-compatible API: since adding fields to a struct is
 | |
| // backward-compatible, minor releases can provide additional types.
 | |
| //
 | |
| // # Result Structs
 | |
| //
 | |
| // Result structs are the inverse of parameter structs (discussed in the In
 | |
| // documentation). These structs represent multiple outputs from a
 | |
| // single function as fields. Fx treats all structs embedding fx.Out as result
 | |
| // structs, so other constructors can rely on the result struct's fields
 | |
| // directly.
 | |
| //
 | |
| // Without result structs, we sometimes have function definitions like this:
 | |
| //
 | |
| //	func SetupGateways(conn *sql.DB) (*UserGateway, *CommentGateway, *PostGateway, error) {
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // With result structs, we can make this both more readable and easier to
 | |
| // modify in the future:
 | |
| //
 | |
| //	type Gateways struct {
 | |
| //	  fx.Out
 | |
| //
 | |
| //	  Users    *UserGateway
 | |
| //	  Comments *CommentGateway
 | |
| //	  Posts    *PostGateway
 | |
| //	}
 | |
| //
 | |
| //	func SetupGateways(conn *sql.DB) (Gateways, error) {
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // # Named Values
 | |
| //
 | |
| // Some use cases require the application container to hold multiple values of
 | |
| // the same type. For details on consuming named values, see the documentation
 | |
| // for the In type.
 | |
| //
 | |
| // A constructor that produces a result struct can tag any field with
 | |
| // `name:".."` to have the corresponding value added to the graph under the
 | |
| // specified name. An application may contain at most one unnamed value of a
 | |
| // given type, but may contain any number of named values of the same type.
 | |
| //
 | |
| //	type ConnectionResult struct {
 | |
| //	  fx.Out
 | |
| //
 | |
| //	  ReadWrite *sql.DB `name:"rw"`
 | |
| //	  ReadOnly  *sql.DB `name:"ro"`
 | |
| //	}
 | |
| //
 | |
| //	func ConnectToDatabase(...) (ConnectionResult, error) {
 | |
| //	  // ...
 | |
| //	  return ConnectionResult{ReadWrite: rw, ReadOnly:  ro}, nil
 | |
| //	}
 | |
| //
 | |
| // # Value Groups
 | |
| //
 | |
| // To make it easier to produce and consume many values of the same type, Fx
 | |
| // supports named, unordered collections called value groups. For details on
 | |
| // consuming value groups, see the documentation for the In type.
 | |
| //
 | |
| // Constructors can send values into value groups by returning a result struct
 | |
| // tagged with `group:".."`.
 | |
| //
 | |
| //	type HandlerResult struct {
 | |
| //	  fx.Out
 | |
| //
 | |
| //	  Handler Handler `group:"server"`
 | |
| //	}
 | |
| //
 | |
| //	func NewHelloHandler() HandlerResult {
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| //	func NewEchoHandler() HandlerResult {
 | |
| //	  // ...
 | |
| //	}
 | |
| //
 | |
| // Any number of constructors may provide values to this named collection, but
 | |
| // the ordering of the final collection is unspecified. Keep in mind that
 | |
| // value groups require parameter and result structs to use fields with
 | |
| // different types: if a group of constructors each returns type T, parameter
 | |
| // structs consuming the group must use a field of type []T.
 | |
| //
 | |
| // To provide multiple values for a group from a result struct, produce a
 | |
| // slice and use the `,flatten` option on the group tag. This indicates that
 | |
| // each element in the slice should be injected into the group individually.
 | |
| //
 | |
| //	type IntResult struct {
 | |
| //	  fx.Out
 | |
| //
 | |
| //	  Handler []int `group:"server"`         // Consume as [][]int
 | |
| //	  Handler []int `group:"server,flatten"` // Consume as []int
 | |
| //	}
 | |
| type Out = dig.Out
 |