Skip to content

pip-services3-gox/pip-services3-components-gox

Repository files navigation

Pip.Services Logo
Component definitions for Golang

This module is a part of the Pip.Services polyglot microservices toolkit.

The Components module contains standard component definitions that can be used to build applications and services.

The module contains the following packages:

  • Auth - authentication credential stores
  • Build - factories
  • Cache - distributed cache
  • Component - the root package
  • Config - configuration readers
  • Connect - connection discovery services
  • Count - performance counters
  • Info - context info
  • Lock - distributed locks
  • Log - logging components
  • Test - test components

Quick links:

Use

Get the package from the Github repository:

go get -u github.com/pip-services3-gox/pip-services3-components-gox@latest

Example how to use Logging and Performance counters. Here we are going to use CompositeLogger and CompositeCounters components. They will pass through calls to loggers and counters that are set in references.

import (
	"context"

	"github.com/pip-services3-gox/pip-services3-commons-gox/config"
	"github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	"github.com/pip-services3-gox/pip-services3-components-gox/count"
	"github.com/pip-services3-gox/pip-services3-components-gox/log"
)

type MyComponent struct {
	logger   *log.CompositeLogger
	counters *count.CompositeCounters
}

func (c *MyComponent) Configure(ctx context.Context, config *config.ConfigParams) {
	c.logger.Configure(ctx, config)
}

func (c *MyComponent) SetReferences(ctx context.Context, references refer.IReferences) {
	c.logger.SetReferences(ctx, references)
	c.counters.SetReferences(ctx, references)
}

func (c *MyComponent) MyMethod(ctx context.Context, correlationId string, param1 any) {
	c.logger.Trace(ctx, correlationId, "Executed method mycomponent.mymethod")
	c.counters.Increment(ctx, "mycomponent.mymethod.exec_count", 1)
	timing := c.counters.BeginTiming(ctx, "mycomponent.mymethod.exec_time")
	defer timing.EndTiming(ctx)
	// ....

	if err != nil {
		c.logger.Error(ctx, correlationId, err, "Failed to execute mycomponent.mymethod")
		c.counters.Increment(ctx, "mycomponent.mymethod.error_count", 1)
	}
}

Example how to get connection parameters and credentials using resolvers. The resolvers support "discovery_key" and "store_key" configuration parameters to retrieve configuration from discovery services and credential stores respectively.

package main

import (
	"context"

	"github.com/pip-services3-gox/pip-services3-commons-gox/config"
	"github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	"github.com/pip-services3-gox/pip-services3-components-gox/auth"
	"github.com/pip-services3-gox/pip-services3-components-gox/connect"
)

func main() {
	// Using the component
	myComponent := NewMyComponent()

	myComponent.Configure(context.Background(), config.NewConfigParamsFromTuples(
		"connection.host", "localhost",
		"connection.port", 1234,
		"credential.username", "anonymous",
		"credential.password", "pass123",
	))

	err := myComponent.Open(context.Background(), "123")
}

type MyComponent struct {
	connectionResolver *connect.ConnectionResolver
	credentialResolver *auth.CredentialResolver
}

func NewMyComponent() *MyComponent {
	return &MyComponent{
		connectionResolver: connect.NewEmptyConnectionResolver(),
		credentialResolver: auth.NewEmptyCredentialResolver(),
	}
}

func (c *MyComponent) Configure(ctx context.Context, config *config.ConfigParams) {
	c.connectionResolver.Configure(ctx, config)
	c.credentialResolver.Configure(ctx, config)
}

func (c *MyComponent) SetReferences(ctx context.Context, references refer.IReferences) {
	c.connectionResolver.SetReferences(ctx, references)
	c.credentialResolver.SetReferences(ctx, references)
}

// ...

func (c *MyComponent) IsOpen() bool {
	panic("not implemented") // TODO: Implement
}

func (c *MyComponent) Open(ctx context.Context, correlationId string) error {
	connection, err := c.connectionResolver.Resolve(correlationId)
	credential, err := c.credentialResolver.Lookup(ctx, correlationId)

	host := connection.Host()
	port := connection.Port()
	user := credential.Username()
	pass := credential.Password()
}

func (c *MyComponent) Close(ctx context.Context, correlationId string) error {
	panic("not implemented") // TODO: Implement
}

Example how to use caching and locking. Here we assume that references are passed externally.

package main

import (
	"context"

	"github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	"github.com/pip-services3-gox/pip-services3-components-gox/cache"
	"github.com/pip-services3-gox/pip-services3-components-gox/lock"
)

func main() {
	// Use the component
	myComponent := NewMyComponent()

	myComponent.SetReferences(context.Background(), refer.NewReferencesFromTuples(context.Background(),
		refer.NewDescriptor("pip-services", "cache", "memory", "default", "1.0"), cache.NewMemoryCache[any](),
		refer.NewDescriptor("pip-services", "lock", "memory", "default", "1.0"), lock.NewMemoryLock(),
	))

	result, err := myComponent.MyMethod(context.Background(), "123", "my_param")
}

type MyComponent struct {
	cache cache.ICache[any]
	lock  lock.ILock
}

func NewMyComponent() *MyComponent {
	return &MyComponent{}
}

func (c *MyComponent) SetReferences(ctx context.Context, references refer.IReferences) {
	res, errDescr := references.GetOneRequired(refer.NewDescriptor("*", "cache", "*", "*", "1.0"))
	if errDescr != nil {
		panic(errDescr)
	}
	c.cache = res.(cache.ICache[any])

	res, errDescr = references.GetOneRequired(refer.NewDescriptor("*", "lock", "*", "*", "1.0"))
	if errDescr != nil {
		panic(errDescr)
	}
	c.lock = res.(lock.ILock)
}

func (c *MyComponent) MyMethod(ctx context.Context, correlationId string, param1 any) (any, error) {
	// First check cache for result
	result, err := c.cache.Retrieve(ctx, correlationId, "mykey")
	if result != nil || err != nil {
		return result, err
	}

	// Lock..
	err = c.lock.AcquireLock(ctx, correlationId, "mykey", 1000, 1000)
	if err != nil {
		return result, err
	}

	// Do processing
	// ...

	// Store result to cache async
	_, err = c.cache.Store(ctx, correlationId, "mykey", result, 3600000)
	if err != nil {
		return result, err
	}

	// Release lock async
	err = c.lock.ReleaseLock(ctx, correlationId, "mykey")
	if err != nil {
		return result, err
	}
	return result, nil
}

If you need to create components using their locators (descriptors) implement component factories similar to the example below.

package main

import (
	"github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	"github.com/pip-services3-gox/pip-services3-components-gox/build"
)

var MyComponentDescriptor = refer.NewDescriptor("myservice", "mycomponent", "default", "*", "1.0")

func NewMyFactory() *build.Factory {
	factory := build.NewFactory()

	factory.RegisterType(MyComponentDescriptor, NewMyComponent)

	return factory
}

func main() {
	// Using the factory
	myFactory := NewMyFactory()
	myComponent1, err = myFactory.Create(refer.NewDescriptor("myservice", "mycomponent", "default", "myComponent1", "1.0"))
	myComponent2, err := myFactory.Create(refer.NewDescriptor("myservice", "mycomponent", "default", "myComponent2", "1.0"))
}

Develop

For development you shall install the following prerequisites:

  • Golang v1.18+
  • Visual Studio Code or another IDE of your choice
  • Docker
  • Git

Run automated tests:

go test -v ./test/...

Generate API documentation:

./docgen.ps1

Before committing changes run dockerized test as:

./test.ps1
./clear.ps1

Contacts

The library is created and maintained by Sergey Seroukhov.

The documentation is written by Levichev Dmitry.