Deployment time configuration

How to set up and change your configurations at deployment time.

Key takeaways

Factories Allow us to code class instantiation without having to specify all the configuration parameters at coding time.
YAML configuration file Allows us to configure components and provide one or more alternatives at deployment time.
Environment variables Allow us to choose between different component alternatives at deployment time.

Introduction

In this tutorial, you will learn how to configure at deployment time an application based on Pip.Services components. For this, we will use the example already developed in Three tier architecture and expand it to consider two different databases. Then, we will see how to select one of these databases at deployment time.

Basic concepts

Two of the main concepts behind Pip.Services are inversion of control and the locator pattern, which are used to manage object creation and object binding.

Inversion of control is applied through the use of factories to create objects. The advantage here is that we can specify our class in a generic way, for example, by indicating that it is a persistence unit, but without having to specify connection parameters and other aspects.

The locator pattern is implemented via the use of a YAML configuration file. This feature allows us to make configuration choices at deployment time. For example, we can change connection parameters, such as IP addresses or database names, and we can provide more than one option for a type of component.

The sections and example that follow explain in detail how this is done .

Brief description of the example

In this tutorial, we will expand on the example presented in the tutorial Three-tier architecture and available through this website.

That example comprised an application that selects a random name from a database and shows a message saying “Hello {random name}!” on a browser. The database used was MySQL.

Our code expansion will consist of including a second database (PostgreSQL) containing a similar table and adding the capacity to choose between the two at deployment time via environmental variables.

Changes to our example

In this example, we consider two databases, one MySQL and another PostgreSQL and we select one of them at deployment time for the application.

For this, we create an interface that defines a common naming and permits to shift between both databases.

We also create a class per database.

Common interface

One of the principles behind Pip.Services is symmetric implementation. This means that different libraries use similar method names and signatures. As a result, we can create an interface that defines the CRUD methods implemented by one or more databases. This will allow us to have a common central point from which we can define the operations that we need.

import { FilterParams } from "pip-services3-commons-nodex";

export interface IMyDataPersistence {
    getOneRandom(correlationId: string, filter: FilterParams): Promise<MyFriend>;
    create(correlationId: string, item: MyFriend): Promise<MyFriend>;
}


using System.Threading.Tasks;
using PipServices3.Commons.Data;

public interface IMyDataPersistence
{
    Task<MyFriend> GetOneRandomAsync(string correlationId, FilterParams filter);
    Task<MyFriend> CreateAsync(string correlationId, MyFriend item)
}


import (
	"context"

	cdata "github.com/pip-services3-gox/pip-services3-commons-gox/data"
)

type IMyDataPersistence[T any] interface {
	GetOneRandom(ctx context.Context, correlationId string, filter cdata.FilterParams) (item T, err error)
	Create(ctx context.Context, correlationId string, item T) (result T, err error)
}

import 'package:pip_services3_commons/pip_services3_commons.dart';

abstract class IMyDataPersistence {
  Future<MyFriend?> getOneRandom(String? correlationId, FilterParams filter);
  Future<MyFriend?> create(String? correlationId, MyFriend item);
}

from abc import ABC
from typing import Optional
from pip_services3_commons.data import FilterParams

class IMyDataPersistence(ABC):

    # CRUD operations
    def get_one_random(self, correlation_id: Optional[str], filter: FilterParams) -> MyFriend:
        raise NotImplemented()
    
    def create(self, correlation_id: Optional[str], item: MyFriend) -> MyFriend:
        raise NotImplemented()
Not available

Controller

Our controller is similar to the one defined in the tutorial “Three tier architecture”. The only difference is that we define the _persistence variable as of type IMyDataPersistence. This will allow us to refer to our database independently of the specific cases.

import { IConfigurable, IReferenceable } from "pip-services3-commons-nodex";


export class HelloFriendController implements IConfigurable, IReferenceable {
    private defaultName: string;
    private persistence: IMyDataPersistence;

    public constructor() {
        this.defaultName = "Pip User";
    }

    public configure(config: ConfigParams): void {
        this.defaultName = config.getAsStringWithDefault("default_name", this.defaultName);
    }

    public setReferences(references: IReferences): void {
        this.persistence = references.getOneRequired(new Descriptor("hello-friend", "persistence", "*", "*", "1.0"));
    }

    public async greeting(): Promise<string> {
        let filter = FilterParams.fromTuples("type", "friend");
        let selectedFilter = await this.persistence.getOneRandom(null, filter);
        let name = selectedFilter != null ? selectedFilter.name : null;

        return `Hello, ${name} !`;
    }

    public async create(correlationId: string, item: MyFriend): Promise<MyFriend>  {
        let res = await this.persistence.create(correlationId, item);
        return res;
    }
}

using PipServices3.Commons.Config;
using PipServices3.Commons.Refer;


public class HelloFriendController : IConfigurable, IReferenceable
{
    private string defaultName;

    private IMyDataPersistence persistence;

    public HelloFriendController()
    {
        defaultName = "Pip User";
    }

    public void Configure(ConfigParams config)
    {
        defaultName = config.GetAsStringWithDefault("default_name", defaultName);
    }

    public void SetReferences(IReferences references)
    {
        persistence = references.GetOneRequired<IMyDataPersistence>(new Descriptor("hello-friend", "persistence", "*", "*", "1.0"));
    }

    public async Task<string> Greeting()
    {
        var filter = FilterParams.FromTuples("type", "friend");
        var selectedFilter = await persistence.GetOneRandomAsync(null, filter);
        var name = selectedFilter!= null ? selectedFilter.Name : null;

        return $"Hello, {name} !";
    }

    public async Task<MyFriend> CreateAsync(string correlationId, MyFriend item)
    {
        var res = await persistence.CreateAsync(correlationId, item);

        return res;
    }
}


import (
	"context"

	cconf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
	cdata "github.com/pip-services3-gox/pip-services3-commons-gox/data"
	cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
)

type HelloFriendController struct {
	defaultName string
	persistence IMyDataPersistence[MyFriend]
}

func NewHelloFriendController() *HelloFriendController {
	c := &HelloFriendController{
		defaultName: "Pip User",
	}

	return c
}

func (c *HelloFriendController) Configure(ctx context.Context, config *cconf.ConfigParams) {
	c.defaultName = config.GetAsStringWithDefault("default_name", c.defaultName)
}

func (c *HelloFriendController) SetReferences(ctx context.Context, references cref.IReferences) {
	res, descrErr := references.GetOneRequired(cref.NewDescriptor("hello-friend", "persistence", "*", "*", "1.0"))
	if descrErr != nil {
		panic(descrErr)
	}

	c.persistence = res.(IMyDataPersistence[MyFriend])
}

func (c *HelloFriendController) Greeting(ctx context.Context) (string, error) {
	filter := cdata.NewFilterParamsFromTuples("type", "friend")
	selectedFilter, err := c.persistence.GetOneRandom(ctx, "123", *filter)
	if err != nil {
		return "", err
	}

	return "Hello, " + selectedFilter.Name + " !", nil
}

func (c *HelloFriendController) Create(ctx context.Context, correlationId string, item MyFriend) (result MyFriend, err error) {
	return c.persistence.Create(ctx, correlationId, item)
}
import 'package:pip_services3_commons/pip_services3_commons.dart';

class HelloFriendController implements IConfigurable, IReferenceable {
  String defaultName;
  IMyDataPersistence? persistence;

  HelloFriendController() : defaultName = 'Pip User';

  @override
  void configure(ConfigParams config) {
    defaultName = config.getAsStringWithDefault('default_name', defaultName);
  }

  @override
  void setReferences(IReferences references) {
    persistence = references.getOneRequired(
        Descriptor('hello-friend', 'persistence', '*', '*', '1.0'));
  }

  Future<String> greeting() async {
    var filter = FilterParams.fromTuples(['type', 'friend']);
    var selectedFilter = await persistence!.getOneRandom(null, filter);
    var name = selectedFilter?.name ?? '';

    return 'Hello, ' + name + ' !';
  }

  Future<MyFriend?> create(String? correlationId, MyFriend? item) async {
    var res = await persistence!.create(correlationId, item);
    return res;
  }
}

abstract class IMyDataPersistence {
  Future<MyFriend?> getOneRandom(String? correlationId, FilterParams filter);
  Future<MyFriend?> create(String? correlationId, MyFriend? item);
}

from pip_services3_commons.config import IConfigurable
from pip_services3_commons.refer import IReferences, IReferenceable


class HelloFriendRestService(RestService):

    def __init__(self):
        super(HelloFriendRestService, self).__init__()

        self._base_route = "/hello_friend"
        self._controller: IMyDataPersistence = None

    def configure(self, config):
        super().configure(config)

    def set_references(self, references):
        super(HelloFriendRestService, self).set_references(references)
        self._controller = references.get_one_required(Descriptor('hello-friend', 'controller', '*', '*', '1.0'))

    def register(self):
        self.register_route(method="GET", route="/greeting", schema=Schema(), handler=self.greeting)
        self.register_route(method="GET", route="/greeting_create", schema=Schema(), handler=self.create)

    def greeting(self):
        result = self._controller.greeting()
        return self.send_result(result)

    def create(self):
        correlation_id = self._get_correlation_id()
        item = MyFriend(
            bottle.request.query["id"],
            bottle.request.query["type"],
            bottle.request.query["name"]
        )
        result = self._controller.create(correlation_id, item)

        return self.send_result(result)
Not available

MySQL

This class adds the common interface as a parent. As the getOneRandom() method is protected, we override it and add the composeFilter() method to create a filter that considers the specifics of MySQL.

import { IdentifiableMySqlPersistence } from "pip-services3-mysql-nodex";


export class HelloFriendPersistence1 extends IdentifiableMySqlPersistence<MyFriend, string> implements IMyDataPersistence {
    public constructor() {
        super("myfriends3");
    }

    protected defineSchema(): void {
        this.clearSchema();
        this.ensureSchema('CREATE TABLE IF NOT EXISTS `' + this._tableName + '` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)');
    }

    private composeFilter(filter: FilterParams): string {
        filter ??= new FilterParams();
        let type = filter.getAsNullableString("type");
        let name = filter.getAsNullableString("name");

        let filterCondition = "";
        if (type != null)
            filterCondition += "type='" + type + "'";
        if (name != null)
            filterCondition += "name='" + name + "'";

        return filterCondition;
    }

    public getOneRandom(correlationId: string, filter: FilterParams): Promise<MyFriend> {
        return super.getOneRandom(correlationId, this.composeFilter(filter));
    }
}

using PipServices3.MySql.Persistence;


public class HelloFriendPersistence1: IdentifiableMySqlPersistence<MyFriend, string>, IMyDataPersistence
{
    public HelloFriendPersistence1() :base("myfriends3") { }

    protected override void DefineSchema()
    {
        ClearSchema();
        EnsureSchema($"CREATE TABLE IF NOT EXISTS {_tableName} (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)");
    }

    private static string ComposeFilter(FilterParams filter)
    {
        filter ??= new FilterParams();
        var type = filter.GetAsNullableString("type");
        var name = filter.GetAsNullableString("name");

        var filterCondition = "";
        if (type != null)
            filterCondition += "`type`='" + type + "'";
        if (name != null)
            filterCondition += "`name`='" + name + "'";

        return filterCondition;
    }

    public Task<MyFriend> GetOneRandomAsync(string correlationId, FilterParams filter)
    {
        return base.GetOneRandomAsync(correlationId, ComposeFilter(filter));
    }
}

import mysqlpersist "github.com/pip-services3-gox/pip-services3-mysql-gox/persistence"


type HelloFriendPersistence1 struct {
	*mysqlpersist.IdentifiableMySqlPersistence[MyFriend, string]
}

func NewHelloFriendPersistence1() *HelloFriendPersistence1 {
	c := &HelloFriendPersistence1{}
	c.IdentifiableMySqlPersistence = mysqlpersist.InheritIdentifiableMySqlPersistence[MyFriend, string](c, "myfriends3")
	return c
}

func (c *HelloFriendPersistence1) DefineSchema() {
	c.ClearSchema()
	c.EnsureSchema("CREATE TABLE `" + c.TableName + "` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)")
}

func (c *HelloFriendPersistence1) composeFilter(filter cdata.FilterParams) string {
	typee, typeOk := filter.GetAsNullableString("type")
	name, nameOk := filter.GetAsNullableString("name")

	filterObj := ""
	if typeOk && typee != "" {
		filterObj += "`type`='" + typee + "'"
	}
	if nameOk && name != "" {
		filterObj += "`name`='" + name + "'"
	}

	return filterObj
}

func (c *HelloFriendPersistence1) GetOneRandom(ctx context.Context, correlationId string, filter cdata.FilterParams) (item MyFriend, err error) {
	return c.MySqlPersistence.GetOneRandom(ctx, correlationId, c.composeFilter(filter))
}

import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_mysql/pip_services3_mysql.dart';


class HelloFriendPersistence1
    extends IdentifiableMySqlPersistence<MyFriend, String>
    implements IMyDataPersistence {
  HelloFriendPersistence1() : super('myfriends3', null);

  @override
  void defineSchema_() {
    clearSchema();
    ensureSchema_('CREATE TABLE IF NOT EXISTS `' +
        tableName_! +
        '` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)');
  }

  String _composeFilter(FilterParams? filter) {
    filter ??= FilterParams();
    var type = filter.getAsNullableString('type');
    var name = filter.getAsNullableString('name');

    var filterCondition = '';
    if (type != null) {
      filterCondition += "type='" + type + "'";
    }
    if (name != null) {
      filterCondition += "name='" + name + "'";
    }

    return filterCondition;
  }

  @override
  Future<MyFriend?> getOneRandom(
      String? correlationId, FilterParams filter) async {
    return await getOneRandom_(correlationId, _composeFilter(filter));
  }
}
from pip_services3_mysql.persistence import IdentifiableMySqlPersistence


class HelloFriendPersistence1(IdentifiableMySqlPersistence, IMyDataPersistence):

    def __init__(self):
        super(HelloFriendPersistence, self).__init__('myfriends3')

    def _define_schema(self):
        self._clear_schema()
        self._ensure_schema(
            'CREATE TABLE IF NOT EXISTS `' + self._table_name + '` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)')
    
    def _compose_filter(self, filter: FilterParams):
        filter = filter or FilterParams()
        type = filter.get_as_nullable_string('type')
        name = filter.get_as_nullable_string('name')

        filter_condition = ''
        if type is not None:
            filter_condition += "`type`='" + type + "'"
        if name is not None:
            filter_condition += "`name`='" + name + "'"

        return filter_condition
    
    def get_one_random(self, correlation_id: Optional[str], filter: FilterParams) -> MyFriend:
        return super().get_one_random(correlation_id, self._compose_filter(filter)) 
Not available

PostgreSQL

As we are working with two databases, we need to create a class similar to the one we created for MySQL. In this case, it inherits from IdentifiablePostgrePersistence and our common interface IMyDataPersistence.

Similar to our MySQL class, we also override the getOneRandom() method and add a composeFilter() method to adapt our filter to the specifics of PostgreSQL.

import { IdentifiablePostgresPersistence } from "pip-services3-postgres-nodex";

export class HelloFriendPersistence2 extends IdentifiablePostgresPersistence<MyFriend, string> implements IMyDataPersistence {
    public constructor() {
        super("myfriends3");
    }

    protected defineSchema(): void {
        this.clearSchema();
        this.ensureSchema('CREATE TABLE IF NOT EXISTS ' + this._tableName + ' (id TEXT PRIMARY KEY, type TEXT, name TEXT)');
    }

    private composeFilter(filter: FilterParams): string {
        filter ??= new FilterParams();
        let type = filter.getAsNullableString("type");
        let content = filter.getAsNullableString("content");

        let filterCondition = "";
        if (type != null)
            filterCondition += "type='" + type + "'";
        if (content != null)
            filterCondition += "content='" + content + "'";

        return filterCondition;
    }

    public getOneRandom(correlationId: string, filter: FilterParams): Promise<MyFriend> {
        return super.getOneRandom(correlationId, this.composeFilter(filter));
    }
}

using PipServices3.Postgres.Persistence;

public class HelloFriendPersistence2 : IdentifiablePostgresPersistence<MyFriend, string>, IMyDataPersistence
{
    public HelloFriendPersistence2() : base("myfriends3") { }

    protected override void DefineSchema()
    {
        ClearSchema();
        EnsureSchema($"CREATE TABLE IF NOT EXISTS {_tableName} (id TEXT PRIMARY KEY, type TEXT, name TEXT)");
    }

    private static string ComposeFilter(FilterParams filter)
    {
        filter ??= new FilterParams();
        var type = filter.GetAsNullableString("type");
        var name = filter.GetAsNullableString("name");

        var filterCondition = "";
        if (type != null)
            filterCondition += "`type`='" + type + "'";
        if (name != null)
            filterCondition += "`name`='" + name + "'";

        return filterCondition;
    }

    public Task<MyFriend> GetOneRandomAsync(string correlationId, FilterParams filter)
    {
        return base.GetOneRandomAsync(correlationId, ComposeFilter(filter));
    }
}

import postgrespersist "github.com/pip-services3-gox/pip-services3-postgres-gox/persistence"

type HelloFriendPersistence2 struct {
	*postgrespersist.IdentifiablePostgresPersistence[MyFriend, string]
}

func NewHelloFriendPersistence2() *HelloFriendPersistence2 {
	c := &HelloFriendPersistence2{}
	c.IdentifiablePostgresPersistence = postgrespersist.InheritIdentifiablePostgresPersistence[MyFriend, string](c, "myfriends3")
	return c
}

func (c *HelloFriendPersistence2) DefineSchema() {
	c.ClearSchema()
	c.EnsureSchema("CREATE TABLE IF NOT EXISTS " + c.QuotedTableName() + " (\"id\" TEXT PRIMARY KEY, \"type\" TEXT, \"name\" TEXT)")
}

func (c *HelloFriendPersistence2) composeFilter(filter cdata.FilterParams) string {
	typee, typeOk := filter.GetAsNullableString("type")
	name, nameOk := filter.GetAsNullableString("name")

	filterObj := ""
	if typeOk && typee != "" {
		filterObj += "`type`='" + typee + "'"
	}
	if nameOk && name != "" {
		filterObj += "`name`='" + name + "'"
	}

	return filterObj
}

func (c *HelloFriendPersistence2) GetOneRandom(ctx context.Context, correlationId string, filter cdata.FilterParams) (item MyFriend, err error) {
	return c.PostgresPersistence.GetOneRandom(ctx, correlationId, c.composeFilter(filter))
}

import 'package:pip_services3_postgres/pip_services3_postgres.dart';

class HelloFriendPersistence2
    extends IdentifiablePostgresPersistence<MyFriend, String>
    implements IMyDataPersistence {
  HelloFriendPersistence2() : super('myfriends3', null);

  @override
  void defineSchema_() {
    clearSchema();
    ensureSchema_('CREATE TABLE IF NOT EXISTS ' +
        tableName_! +
        ' (id TEXT PRIMARY KEY, type TEXT, name TEXT)');
  }

  String _composeFilter(FilterParams? filter) {
    filter ??= FilterParams();
    var type = filter.getAsNullableString('type');
    var content = filter.getAsNullableString('content');

    var filterCondition = '';
    if (type != null) {
      filterCondition += "type='" + type + "'";
    }
    if (content != null) {
      filterCondition += "content='" + content + "'";
    }

    return filterCondition;
  }

  @override
  Future<MyFriend?> getOneRandom(
      String? correlationId, FilterParams filter) async {
    return super.getOneRandom_(correlationId, _composeFilter(filter));
  }
}

from pip_services3_postgres.persistence import IdentifiablePostgresPersistence

class HelloFriendPersistence2(IdentifiablePostgresPersistence, IMyDataPersistence):

    def __init__(self):
        super().__init__('myfriends3')

    def _define_schema(self):
        self._clear_schema()
        self._ensure_schema('CREATE TABLE IF NOT EXISTS' + self._table_name + ' (id TEXT PRIMARY KEY, type TEXT, name TEXT)')

    def _compose_filter(self, filter: FilterParams):
        filter = filter or FilterParams()
        key = filter.get_as_nullable_string('key')
        content = filter.get_as_nullable_string('content')

        filter_condition = ''
        if key is not None:
            filter_condition += "key='" + key + "'"
        if content is not None:
            filter_condition += "content='" + content + "'"

        return filter_condition

    def get_one_random(self, correlation_id: Optional[str], filter: FilterParams) -> MyFriend:
        return super().get_one_random(correlation_id, self._compose_filter(filter))
Not available

Configuration file

In order to be able to select between both databases, we need to include both configurations in our config file. In addition, we need to include a conditional IF statement that will check on the environment variable and select the correct database.

Our file will look something like this:

---
# Container context
- descriptor: "pip-services:context-info:default:default:1.0"
  name: "hello-friend"
  description: "HelloFriend microservice"

# Console logger
- descriptor: "pip-services:logger:console:default:1.0"
  level: "trace"

# Performance counter that post values to log
- descriptor: "pip-services:counters:log:default:1.0"

# Controller
- descriptor: "hello-friend:controller:default:default:1.0"
  default_name: "Friend"

# Shared HTTP Endpoint
- descriptor: "pip-services:endpoint:http:default:1.0"
  connection:
    protocol: http
    host: 0.0.0.0
    port: 8080

# HTTP Service V1
- descriptor: "hello-friend:service:http:default:1.0"

# Heartbeat service
- descriptor: "pip-services:heartbeat-service:http:default:1.0"

# Status service
- descriptor: "pip-services:status-service:http:default:1.0"


{{#if MYSQL_ENABLED}}
# Persistence - MySQL
- descriptor: "hello-friend:persistence:mysql:default:1.0"
  connection:
    host: 'localhost'
    port: '3306'
    database: 'pip'
  credential:
    username: 'root'
    password: ''
{{/if}}

{{#if POSTGRES_ENABLED}}
# Persistence - PostgreSQL
- descriptor: "hello-friend:persistence:postgres:default:1.0"
  connection:
    host: 'localhost'
    port: '5432'
    database: 'pip'
  credential:
    username: 'postgres'
    password: 'admin'
{{/if}}

Final code

Now that we have completed all the necessary additions and modifications in our program, we can join all the parts and get the final application. The code is:

import { 
    ConfigParams, Descriptor, FilterParams, 
    IConfigurable, IReferenceable, IReferences, 
    IStringIdentifiable 
} from "pip-services3-commons-nodex";

import { Factory } from "pip-services3-components-nodex";
import { ProcessContainer } from "pip-services3-container-nodex";
import { IdentifiableMySqlPersistence } from "pip-services3-mysql-nodex";
import { IdentifiablePostgresPersistence } from "pip-services3-postgres-nodex";
import { DefaultRpcFactory, RestService } from "pip-services3-rpc-nodex";



export class MyFriend implements IStringIdentifiable {
    public id: string;
    public type: string;
    public name: string;
}



export class HelloFriendRestService extends RestService {
    protected controller: HelloFriendController;

    public constructor() {
        super()
        this._baseRoute = "/hello_friend";
    }

    public configure(config: ConfigParams): void {
        super.configure(config);
    }

    public setReferences(references: IReferences): void {
        super.setReferences(references);
        this.controller = references.getOneRequired(new Descriptor("hello-friend", "controller", "*", "*", "1.0"));
    }

    private async greeting(req: any, res: any): Promise<void> {
        let result = await this.controller.greeting();
        await this.sendResult(req, res, result);
    }

    private async create(req: any, res: any): Promise<void> {
        let correlationId = this.getCorrelationId(req);
        let friend: MyFriend = req.query;
        let result = await this.controller.create(correlationId, friend);

        await this.sendResult(req, res, result);
    }

    public register(): void {
        this.registerRoute("GET", "/greeting", null, this.greeting);
        this.registerRoute("GET", "/greeting_create", null, this.create);
    }
}



export class HelloFriendController implements IConfigurable, IReferenceable {
    private defaultName: string;
    private persistence: IMyDataPersistence;

    public constructor() {
        this.defaultName = "Pip User";
    }

    public configure(config: ConfigParams): void {
        this.defaultName = config.getAsStringWithDefault("default_name", this.defaultName);
    }

    public setReferences(references: IReferences): void {
        this.persistence = references.getOneRequired(new Descriptor("hello-friend", "persistence", "*", "*", "1.0"));
    }

    public async greeting(): Promise<string> {
        let filter = FilterParams.fromTuples("type", "friend");
        let selectedFilter = await this.persistence.getOneRandom(null, filter);
        let name = selectedFilter != null ? selectedFilter.name : null;

        return `Hello, ${name} !`;
    }

    public async create(correlationId: string, item: MyFriend): Promise<MyFriend>  {
        let res = await this.persistence.create(correlationId, item);
        return res;
    }
}

    
    
export interface IMyDataPersistence {
    getOneRandom(correlationId: string, filter: FilterParams): Promise<MyFriend>;
    create(correlationId: string, item: MyFriend): Promise<MyFriend>;
}
        


export class HelloFriendPersistence1 extends IdentifiableMySqlPersistence<MyFriend, string> implements IMyDataPersistence {
    public constructor() {
        super("myfriends3");
    }

    protected defineSchema(): void {
        this.clearSchema();
        this.ensureSchema('CREATE TABLE IF NOT EXISTS `' + this._tableName + '` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)');
    }

    private composeFilter(filter: FilterParams): string {
        filter ??= new FilterParams();
        let type = filter.getAsNullableString("type");
        let name = filter.getAsNullableString("name");

        let filterCondition = "";
        if (type != null)
            filterCondition += "type='" + type + "'";
        if (name != null)
            filterCondition += "name='" + name + "'";

        return filterCondition;
    }

    public getOneRandom(correlationId: string, filter: FilterParams): Promise<MyFriend> {
        return super.getOneRandom(correlationId, this.composeFilter(filter));
    }
}


       
export class HelloFriendPersistence2 extends IdentifiablePostgresPersistence<MyFriend, string> implements IMyDataPersistence {
    public constructor() {
        super("myfriends3");
    }

    protected defineSchema(): void {
        this.clearSchema();
        this.ensureSchema('CREATE TABLE IF NOT EXISTS ' + this._tableName + ' (id TEXT PRIMARY KEY, type TEXT, name TEXT)');
    }

    private composeFilter(filter: FilterParams): string {
        filter ??= new FilterParams();
        let type = filter.getAsNullableString("type");
        let content = filter.getAsNullableString("content");

        let filterCondition = "";
        if (type != null)
            filterCondition += "type='" + type + "'";
        if (content != null)
            filterCondition += "content='" + content + "'";

        return filterCondition;
    }

    public getOneRandom(correlationId: string, filter: FilterParams): Promise<MyFriend> {
        return super.getOneRandom(correlationId, this.composeFilter(filter));
    }
}



export class HelloFriendServiceFactory extends Factory {
    public constructor() {
        super();
        let HttpServiceDescriptor = new Descriptor("hello-friend", "service", "http", "*", "1.0");      // View
        let ControllerDescriptor = new Descriptor("hello-friend", "controller", "default", "*", "1.0"); // Controller
        let PersistenceDescriptor1 = new Descriptor("hello-friend", "persistence", "mysql", "*", "1.0"); // Persistence
        let PersistenceDescriptor2 = new Descriptor("hello-friend", "persistence", "postgres", "*", "1.0"); // Persistence

        this.registerAsType(HttpServiceDescriptor, HelloFriendRestService); // View
        this.registerAsType(ControllerDescriptor,  HelloFriendController);  // Controller
        this.registerAsType(PersistenceDescriptor1, HelloFriendPersistence1); // Persistence
        this.registerAsType(PersistenceDescriptor2, HelloFriendPersistence2); // Persistence
    }
}



export class HelloFriendProcess extends ProcessContainer {
    public constructor() {
        super("hello-friend", "HelloFriend microservice");
        this._configPath = "./config.yaml"
        this._factories.add(new HelloFriendServiceFactory());
        this._factories.add(new DefaultRpcFactory());
    }
}



export async function main() { 
    try {
        // Step 1 - Database selection
        // process.env['MYSQL_ENABLED'] = 'true';
        process.env['POSTGRES_ENABLED'] = 'true';

        // Step 2 - The run() command
        let proc = new HelloFriendProcess();
        proc.run(process.argv);
    } catch (ex) {
        console.error(ex);
    }
}

using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;

using PipServices3.Commons.Config;
using PipServices3.Commons.Refer;
using PipServices3.Commons.Data;
using PipServices3.Rpc.Services;

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using PipServices3.MySql.Persistence;
using PipServices3.Components.Build;
using PipServices3.Container;
using PipServices3.Rpc.Build;
using PipServices3.Postgres.Persistence;


[DataContract]
public class MyFriend : IStringIdentifiable
{
    [DataMember(Name = "id")]
    public string Id { get; set; }

    [DataMember(Name = "type")]
    public string Type { get; set; }

    [DataMember(Name = "name")]
    public string Name { get; set; }
}



public class HelloFriendRestService: RestService
{
    protected HelloFriendController controller;

    public HelloFriendRestService() : base()
    {
        _baseRoute = "/hello_friend";
    }
    
    public override void Configure(ConfigParams config)
    {
        base.Configure(config);
    }

    public override void SetReferences(IReferences references)
    {
        base.SetReferences(references);
        controller = references.GetOneRequired<HelloFriendController>(new Descriptor("hello-friend", "controller", "*", "*", "1.0"));
    }

    private async Task Greeting(HttpRequest req, HttpResponse res, RouteData routeData)
    {
        var result = await controller.Greeting();
        await SendResultAsync(res, result);
    }

    private async Task CreateAsync(HttpRequest req, HttpResponse res, RouteData routeData)
    {
        var correlationId = this.GetCorrelationId(req);
        var friend = new MyFriend() { Id = req.Query["id"], Type = req.Query["type"], Name = req.Query["Name"] };
        var result = await controller.CreateAsync(correlationId, friend);
        await SendResultAsync(res, result);
    }

    public override void Register()
    {
        RegisterRoute(method: "GET", route: "/greeting", action: Greeting);
        RegisterRoute(method: "GET", route: "/greeting_create", action: CreateAsync);
    }
}



public class HelloFriendController : IConfigurable, IReferenceable
{
    private string defaultName;

    private IMyDataPersistence persistence;

    public HelloFriendController()
    {
        defaultName = "Pip User";
    }

    public void Configure(ConfigParams config)
    {
        defaultName = config.GetAsStringWithDefault("default_name", defaultName);
    }

    public void SetReferences(IReferences references)
    {
        persistence = references.GetOneRequired<IMyDataPersistence>(new Descriptor("hello-friend", "persistence", "*", "*", "1.0"));
    }

    public async Task<string> Greeting()
    {
        var filter = FilterParams.FromTuples("type", "friend");
        var selectedFilter = await persistence.GetOneRandomAsync(null, filter);
        var name = selectedFilter!= null ? selectedFilter.Name : null;

        return $"Hello, {name} !";
    }

    public async Task<MyFriend> CreateAsync(string correlationId, MyFriend item)
    {
        var res = await persistence.CreateAsync(correlationId, item);

        return res;
    }
}

    
    
public interface IMyDataPersistence
{
    Task<MyFriend> GetOneRandomAsync(string correlationId, FilterParams filter);
    Task<MyFriend> CreateAsync(string correlationId, MyFriend item);
}
        


public class HelloFriendPersistence1 : IdentifiableMySqlPersistence<MyFriend, string>, IMyDataPersistence
{
    public HelloFriendPersistence1() : base("myfriends3") { }

    protected override void DefineSchema()
    {
        ClearSchema();
        EnsureSchema($"CREATE TABLE IF NOT EXISTS {_tableName} (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)");
    }

    private static string ComposeFilter(FilterParams filter)
    {
        filter ??= new FilterParams();
        var type = filter.GetAsNullableString("type");
        var name = filter.GetAsNullableString("name");

        var filterCondition = "";
        if (type != null)
            filterCondition += "`type`='" + type + "'";
        if (name != null)
            filterCondition += "`name`='" + name + "'";

        return filterCondition;
    }

    public Task<MyFriend> GetOneRandomAsync(string correlationId, FilterParams filter)
    {
        return base.GetOneRandomAsync(correlationId, ComposeFilter(filter));
    }
}


       
public class HelloFriendPersistence2 : IdentifiablePostgresPersistence<MyFriend, string>, IMyDataPersistence
{
    public HelloFriendPersistence2() : base("myfriends3") { }

    protected override void DefineSchema()
    {
        ClearSchema();
        EnsureSchema($"CREATE TABLE IF NOT EXISTS {_tableName} (id TEXT PRIMARY KEY, type TEXT, name TEXT)");
    }

    private static string ComposeFilter(FilterParams filter)
    {
        filter ??= new FilterParams();
        var type = filter.GetAsNullableString("type");
        var name = filter.GetAsNullableString("content");

        var filterCondition = "";
        if (type != null)
            filterCondition += "type='" + type + "'";
        if (name != null)
            filterCondition += "name='" + name + "'";

        return filterCondition;
    }

    public Task<MyFriend> GetOneRandomAsync(string correlationId, FilterParams filter)
    {
        return base.GetOneRandomAsync(correlationId, ComposeFilter(filter));
    }
}



public class HelloFriendServiceFactory: Factory
{
    public HelloFriendServiceFactory(): base()
    {
        var HttpServiceDescriptor = new Descriptor("hello-friend", "service", "http", "*", "1.0");          // View
        var ControllerDescriptor = new Descriptor("hello-friend", "controller", "default", "*", "1.0");     // Controller
        var PersistenceDescriptor1 = new Descriptor("hello-friend", "persistence", "mysql", "*", "1.0");    // Persistence
        var PersistenceDescriptor2 = new Descriptor("hello-friend", "persistence", "postgres", "*", "1.0"); // Persistence

        RegisterAsType(HttpServiceDescriptor, typeof(HelloFriendRestService));   // View
        RegisterAsType(ControllerDescriptor, typeof(HelloFriendController));     // Controller
        RegisterAsType(PersistenceDescriptor1, typeof(HelloFriendPersistence1)); // Persistence
        RegisterAsType(PersistenceDescriptor2, typeof(HelloFriendPersistence2)); // Persistence
    }
}



public class HelloFriendProcess: ProcessContainer
{
    public HelloFriendProcess(): base("hello-friend", "HelloFriend microservice")
    {
        _configPath = "../../../config.yaml";

        _factories.Add(new HelloFriendServiceFactory());
        _factories.Add(new DefaultRpcFactory());
    }
}



class Program
{

    static void Main(string[] args)
    {
        try
        {
            # Step 1 - Database selection
            Environment.SetEnvironmentVariable("MYSQL_ENABLED", "true");
            //Environment.SetEnvironmentVariable("POSTGRES_ENABLED", "true");

            # Step 2 - The run() command
            var task = (new HelloFriendProcess()).RunAsync(args);
            task.Wait();
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine(ex);
        }
    }
}

package main

import (
	"context"
	"net/http"
	"os"

	cconf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
	cdata "github.com/pip-services3-gox/pip-services3-commons-gox/data"
	cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
	cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
	crun "github.com/pip-services3-gox/pip-services3-container-gox/container"
	mysqlpersist "github.com/pip-services3-gox/pip-services3-mysql-gox/persistence"
	postgrespersist "github.com/pip-services3-gox/pip-services3-postgres-gox/persistence"
	rpcbuild "github.com/pip-services3-gox/pip-services3-rpc-gox/build"
	cservices "github.com/pip-services3-gox/pip-services3-rpc-gox/services"
)

type MyFriend struct {
	Id   string `bson:"_id" json:"id"`
	Type string `bson:"type" json:"type"`
	Name string `bson:"name" json:"name"`
}

func (d *MyFriend) SetId(id string) {
	d.Id = id
}

func (d MyFriend) GetId() string {
	return d.Id
}

func (d MyFriend) Clone() MyFriend {
	return MyFriend{
		Id:   d.Id,
		Type: d.Type,
		Name: d.Name,
	}
}

type HelloFriendRestService struct {
	*cservices.RestService
	controller *HelloFriendController
}

func NewHelloFriendRestService() *HelloFriendRestService {
	c := &HelloFriendRestService{}
	c.RestService = cservices.InheritRestService(c)
	c.BaseRoute = "/hello_friend"
	return c
}

func (c *HelloFriendRestService) Configure(ctx context.Context, config *cconf.ConfigParams) {
	c.RestService.Configure(ctx, config)
}

func (c *HelloFriendRestService) SetReferences(ctx context.Context, references cref.IReferences) {
	c.RestService.SetReferences(ctx, references)
	res, err := references.GetOneRequired(cref.NewDescriptor("hello-friend", "controller", "*", "*", "1.0"))
	if err != nil {
		panic(err)
	}

	c.controller = res.(*HelloFriendController)
}

func (c *HelloFriendRestService) Greeting(res http.ResponseWriter, req *http.Request) {
	result, err := c.controller.Greeting(req.Context())
	c.SendResult(res, req, result, err)
}

func (c *HelloFriendRestService) Create(res http.ResponseWriter, req *http.Request) {
	friend := MyFriend{Id: "0", Type: "New type", Name: "New name"}
	c.SendResult(res, req, friend, nil)
}

func (c *HelloFriendRestService) Register() {
	c.RegisterRoute("GET", "/greeting", nil, c.Greeting)
	c.RegisterRoute("GET", "/greeting_create", nil, c.Create)
}

type HelloFriendController struct {
	defaultName string
	persistence IMyDataPersistence[MyFriend]
}

func NewHelloFriendController() *HelloFriendController {
	c := &HelloFriendController{
		defaultName: "Pip User",
	}

	return c
}

func (c *HelloFriendController) Configure(ctx context.Context, config *cconf.ConfigParams) {
	c.defaultName = config.GetAsStringWithDefault("default_name", c.defaultName)
}

func (c *HelloFriendController) SetReferences(ctx context.Context, references cref.IReferences) {
	res, descrErr := references.GetOneRequired(cref.NewDescriptor("hello-friend", "persistence", "*", "*", "1.0"))
	if descrErr != nil {
		panic(descrErr)
	}

	c.persistence = res.(IMyDataPersistence[MyFriend])
}

func (c *HelloFriendController) Greeting(ctx context.Context) (string, error) {
	filter := cdata.NewFilterParamsFromTuples("type", "friend")
	selectedFilter, err := c.persistence.GetOneRandom(ctx, "123", *filter)
	if err != nil {
		return "", err
	}

	return "Hello, " + selectedFilter.Name + " !", nil
}

func (c *HelloFriendController) Create(ctx context.Context, correlationId string, item MyFriend) (result MyFriend, err error) {
	return c.persistence.Create(ctx, correlationId, item)
}

type IMyDataPersistence[T any] interface {
	GetOneRandom(ctx context.Context, correlationId string, filter cdata.FilterParams) (item T, err error)
	Create(ctx context.Context, correlationId string, item T) (result T, err error)
}

type HelloFriendPersistence1 struct {
	*mysqlpersist.IdentifiableMySqlPersistence[MyFriend, string]
}

func NewHelloFriendPersistence1() *HelloFriendPersistence1 {
	c := &HelloFriendPersistence1{}
	c.IdentifiableMySqlPersistence = mysqlpersist.InheritIdentifiableMySqlPersistence[MyFriend, string](c, "myfriends3")
	return c
}

func (c *HelloFriendPersistence1) DefineSchema() {
	c.ClearSchema()
	c.EnsureSchema("CREATE TABLE `" + c.TableName + "` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)")
}

func (c *HelloFriendPersistence1) composeFilter(filter cdata.FilterParams) string {
	typee, typeOk := filter.GetAsNullableString("type")
	name, nameOk := filter.GetAsNullableString("name")

	filterObj := ""
	if typeOk && typee != "" {
		filterObj += "`type`='" + typee + "'"
	}
	if nameOk && name != "" {
		filterObj += "`name`='" + name + "'"
	}

	return filterObj
}

func (c *HelloFriendPersistence1) GetOneRandom(ctx context.Context, correlationId string, filter cdata.FilterParams) (item MyFriend, err error) {
	return c.MySqlPersistence.GetOneRandom(ctx, correlationId, c.composeFilter(filter))
}

type HelloFriendPersistence2 struct {
	*postgrespersist.IdentifiablePostgresPersistence[MyFriend, string]
}

func NewHelloFriendPersistence2() *HelloFriendPersistence2 {
	c := &HelloFriendPersistence2{}
	c.IdentifiablePostgresPersistence = postgrespersist.InheritIdentifiablePostgresPersistence[MyFriend, string](c, "myfriends3")
	return c
}

func (c *HelloFriendPersistence2) DefineSchema() {
	c.ClearSchema()
	c.EnsureSchema("CREATE TABLE IF NOT EXISTS " + c.QuotedTableName() + " (\"id\" TEXT PRIMARY KEY, \"type\" TEXT, \"name\" TEXT)")
}

func (c *HelloFriendPersistence2) composeFilter(filter cdata.FilterParams) string {
	typee, typeOk := filter.GetAsNullableString("type")
	name, nameOk := filter.GetAsNullableString("name")

	filterObj := ""
	if typeOk && typee != "" {
		filterObj += "`type`='" + typee + "'"
	}
	if nameOk && name != "" {
		filterObj += "`name`='" + name + "'"
	}

	return filterObj
}

func (c *HelloFriendPersistence2) GetOneRandom(ctx context.Context, correlationId string, filter cdata.FilterParams) (item MyFriend, err error) {
	return c.PostgresPersistence.GetOneRandom(ctx, correlationId, c.composeFilter(filter))
}

var HttpServiceDescriptor = cref.NewDescriptor("hello-friend", "service", "http", "*", "1.0")          // View
var ControllerDescriptor = cref.NewDescriptor("hello-friend", "controller", "default", "*", "1.0")     // Controller
var PersistenceDescriptor1 = cref.NewDescriptor("hello-friend", "persistence", "mysql", "*", "1.0")    // Persistence1
var PersistenceDescriptor2 = cref.NewDescriptor("hello-friend", "persistence", "postgres", "*", "1.0") // Persistence2

type HelloFriendServiceFactory struct {
	*cbuild.Factory
}

func NewHelloFriendServiceFactory() *HelloFriendServiceFactory {
	c := &HelloFriendServiceFactory{}
	c.Factory = cbuild.NewFactory()

	c.RegisterType(HttpServiceDescriptor, NewHelloFriendRestService)   // View
	c.RegisterType(ControllerDescriptor, NewHelloFriendController)     // Controller
	c.RegisterType(PersistenceDescriptor1, NewHelloFriendPersistence1) // Persistence1
	c.RegisterType(PersistenceDescriptor2, NewHelloFriendPersistence2) // Persistence2

	return c
}

// Containerization
type HelloFriendProcess struct {
	*crun.ProcessContainer
}

func NewHelloFriendProcess() *HelloFriendProcess {
	c := &HelloFriendProcess{}
	c.ProcessContainer = crun.NewProcessContainer("hello-friend", "HelloFriend microservice")
	c.SetConfigPath("./config.yaml")
	c.AddFactory(NewHelloFriendServiceFactory())
	c.AddFactory(rpcbuild.NewDefaultRpcFactory())

	return c
}

// Running the app
func main() {

	// Step 1 - Database selection
	// os.Setenv("MYSQL_ENABLED", "true")
	os.Setenv("POSTGRES_ENABLED", "true")

	// Step 2 - The run() command
	proc := NewHelloFriendProcess()
	proc.Run(context.Background(), os.Args)
}


import 'dart:async';
import 'dart:convert';

import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_components/pip_services3_components.dart';
import 'package:pip_services3_container/pip_services3_container.dart';
import 'package:pip_services3_mysql/pip_services3_mysql.dart';
import 'package:pip_services3_postgres/pip_services3_postgres.dart';
import 'package:pip_services3_rpc/pip_services3_rpc.dart';
import 'package:shelf/shelf.dart';

class MyFriend implements IStringIdentifiable, ICloneable {
  @override
  String? id;
  String? type;
  String? name;

  MyFriend();

  MyFriend.from(this.id, this.type, this.name);

  Map<String, dynamic> toJson() {
    return <String, dynamic>{'id': id, 'type': type, 'name': name};
  }

  static MyFriend fromJson(Map<String, dynamic> json) {
    var id = json['id'];
    var type = json['type'];
    var name = json['name'];

    return MyFriend.from(id, type, name);
  }

  @override
  MyFriend clone() {
    return MyFriend.from(id, type, name);
  }
}

class HelloFriendRestService extends RestService {
  HelloFriendController? controller;

  HelloFriendRestService() : super() {
    baseRoute = '/hello_friend';
  }

  @override
  void configure(ConfigParams config) {
    super.configure(config);
  }

  @override
  void setReferences(IReferences references) {
    super.setReferences(references);
    controller = references.getOneRequired(
        Descriptor('hello-friend', 'controller', '*', '*', '1.0'));
  }

  FutureOr<Response> greeting(Request req) async {
    var result = await controller?.greeting();
    return await sendResult(req, result);
  }

  FutureOr<Response> create(Request req) async {
    var correlationId = getCorrelationId(req);
    var friend = MyFriend.fromJson(json.decode(await req.readAsString()));
    var result = await controller?.create(correlationId, friend);

    return await sendResult(req, result);
  }

  @override
  void register() {
    registerRoute('GET', '/greeting', null, greeting);
    registerRoute('GET', '/greeting_create', null, create);
  }
}

class HelloFriendController implements IConfigurable, IReferenceable {
  String defaultName;
  IMyDataPersistence? persistence;

  HelloFriendController() : defaultName = 'Pip User';

  @override
  void configure(ConfigParams config) {
    defaultName = config.getAsStringWithDefault('default_name', defaultName);
  }

  @override
  void setReferences(IReferences references) {
    persistence = references.getOneRequired(
        Descriptor('hello-friend', 'persistence', '*', '*', '1.0'));
  }

  Future<String> greeting() async {
    var filter = FilterParams.fromTuples(['type', 'friend']);
    var selectedFilter = await persistence!.getOneRandom(null, filter);
    var name = selectedFilter?.name ?? '';

    return 'Hello, ' + name + ' !';
  }

  Future<MyFriend?> create(String? correlationId, MyFriend item) async {
    var res = await persistence!.create(correlationId, item);
    return res;
  }
}

abstract class IMyDataPersistence {
  Future<MyFriend?> getOneRandom(String? correlationId, FilterParams filter);
  Future<MyFriend?> create(String? correlationId, MyFriend item);
}

class HelloFriendPersistence1
    extends IdentifiableMySqlPersistence<MyFriend, String>
    implements IMyDataPersistence {
  HelloFriendPersistence1() : super('myfriends3', null);

  @override
  void defineSchema_() {
    clearSchema();
    ensureSchema_('CREATE TABLE IF NOT EXISTS `' +
        tableName_! +
        '` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)');
  }

  String _composeFilter(FilterParams? filter) {
    filter ??= FilterParams();
    var type = filter.getAsNullableString('type');
    var name = filter.getAsNullableString('name');

    var filterCondition = '';
    if (type != null) {
      filterCondition += "type='" + type + "'";
    }
    if (name != null) {
      filterCondition += "name='" + name + "'";
    }

    return filterCondition;
  }

  @override
  Future<MyFriend?> getOneRandom(
      String? correlationId, FilterParams filter) async {
    return await getOneRandom_(correlationId, _composeFilter(filter));
  }
}

class HelloFriendPersistence2
    extends IdentifiablePostgresPersistence<MyFriend, String>
    implements IMyDataPersistence {
  HelloFriendPersistence2() : super('myfriends3', null);

  @override
  void defineSchema_() {
    clearSchema();
    ensureSchema_('CREATE TABLE IF NOT EXISTS ' +
        tableName_! +
        ' (id TEXT PRIMARY KEY, type TEXT, name TEXT)');
  }

  String _composeFilter(FilterParams? filter) {
    filter ??= FilterParams();
    var type = filter.getAsNullableString('type');
    var content = filter.getAsNullableString('content');

    var filterCondition = '';
    if (type != null) {
      filterCondition += "type='" + type + "'";
    }
    if (content != null) {
      filterCondition += "content='" + content + "'";
    }

    return filterCondition;
  }

  @override
  Future<MyFriend?> getOneRandom(
      String? correlationId, FilterParams filter) async {
    return super.getOneRandom_(correlationId, _composeFilter(filter));
  }
}

class HelloFriendServiceFactory extends Factory {
  HelloFriendServiceFactory() : super() {
    var HttpServiceDescriptor =
        Descriptor('hello-friend', 'service', 'http', '*', '1.0'); // View
    var ControllerDescriptor = Descriptor(
        'hello-friend', 'controller', 'default', '*', '1.0'); // Controller
    var PersistenceDescriptor1 = Descriptor(
        'hello-friend', 'persistence', 'mysql', '*', '1.0'); // Persistence
    var PersistenceDescriptor2 = Descriptor(
        'hello-friend', 'persistence', 'postgres', '*', '1.0'); // Persistence

    registerAsType(HttpServiceDescriptor, HelloFriendRestService); // View
    registerAsType(ControllerDescriptor, HelloFriendController); // Controller
    registerAsType(
        PersistenceDescriptor1, HelloFriendPersistence1); // Persistence
    registerAsType(
        PersistenceDescriptor2, HelloFriendPersistence2); // Persistence
  }
}

class HelloFriendProcess extends ProcessContainer {
  HelloFriendProcess() : super('hello-friend', 'HelloFriend microservice') {
    configPath = './config.yaml';
    factories.add(HelloFriendServiceFactory());
    factories.add(DefaultRpcFactory());
  }
}

void main(List<String> argument) async {
  // Step 1 - Database selection
  // Env:
  //  'MYSQL_ENABLED': 'true' 
  //    or
  //  'POSTGRES_ENABLED': 'true'

  // Step 2 - The run() command
  var proc = HelloFriendProcess();
  proc.run(argument);
}

from pip_services3_commons.data import IStringIdentifiable


class MyFriend(IStringIdentifiable):
    def __init__(self, id: str, type: str, name: str):
        self.id = id
        self.type = type
        self.name = name



import bottle
from pip_services3_commons.data import IStringIdentifiable
from pip_services3_commons.validate import Schema
from pip_services3_rpc.services import RestService


class HelloFriendRestService(RestService):

    def __init__(self):
        super(HelloFriendRestService, self).__init__()

        self._base_route = "/hello_friend"
        self._controller: HelloFriendController = None

    def configure(self, config):
        super().configure(config)

    def set_references(self, references):
        super(HelloFriendRestService, self).set_references(references)
        self._controller = references.get_one_required(Descriptor('hello-friend', 'controller', '*', '*', '1.0'))

    def register(self):
        self.register_route(method="GET", route="/greeting", schema=Schema(), handler=self.greeting)
        self.register_route(method="GET", route="/greeting_create", schema=Schema(), handler=self.create)

    def greeting(self):
        result = self._controller.greeting()
        return self.send_result(result)

    def create(self):
        correlation_id = self._get_correlation_id()
        item = MyFriend(
            bottle.request.query["id"],
            bottle.request.query["type"],
            bottle.request.query["name"]
        )
        result = self._controller.create(correlation_id, item)

        return self.send_result(result)



from pip_services3_commons.commands import ICommandable
from pip_services3_commons.config import IConfigurable
from pip_services3_commons.refer import IReferences, IReferenceable


class HelloFriendController(IConfigurable, IReferenceable):
    __defaultName = None
    __persistence: IMyDataPersistence = None

    def __init__(self):
        self.__defaultName = "Pip User"

    def configure(self, config):
        self.__defaultName = config.get_as_string_with_default("default_name", self.__defaultName)

    def set_references(self, references: IReferences):
        self.__persistence = references.get_one_required(Descriptor("hello-friend", "persistence", "*", "*", "1.0"))

    def greeting(self):
        filter_param = FilterParams.from_tuples("type", "friend")
        selected_friend = self.__persistence.get_one_random(None, filter_param)
        name2 = selected_friend.name

        return f"Hello, {name2} !"

    def create(self, correlation_id: Optional[str], item: MyFriend) -> MyFriend:
        res = self.__persistence.create(correlation_id, item)
        return res

    
    
from abc import ABC
from typing import Optional
from pip_services3_commons.data import FilterParams


class IMyDataPersistence(ABC):

    # CRUD operations
    def get_one_random(self, correlation_id: Optional[str], filter: FilterParams) -> MyFriend:
        raise NotImplemented()

    def create(self, correlation_id: Optional[str], item: MyFriend) -> MyFriend:
        raise NotImplemented()
        


from pip_services3_mysql.persistence import IdentifiableMySqlPersistence


class HelloFriendPersistence1(IdentifiableMySqlPersistence, IMyDataPersistence):

    def __init__(self):
        super(HelloFriendPersistence1, self).__init__('myfriends3')

    def _define_schema(self):
        self._clear_schema()
        self._ensure_schema(
            'CREATE TABLE IF NOT EXISTS `' + self._table_name + '` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)')

    def _compose_filter(self, filter: FilterParams):
        filter = filter or FilterParams()
        type = filter.get_as_nullable_string('type')
        name = filter.get_as_nullable_string('name')

        filter_condition = ''
        if type is not None:
            filter_condition += "`type`='" + type + "'"
        if name is not None:
            filter_condition += "`name`='" + name + "'"

        return filter_condition

    def get_one_random(self, correlation_id: str, filter: FilterParams) -> MyFriend:
        return super().get_one_random(correlation_id, self._compose_filter(filter))


       
from pip_services3_postgres.persistence import IdentifiablePostgresPersistence


class HelloFriendPersistence2(IdentifiablePostgresPersistence, IMyDataPersistence):

    def __init__(self):
        super().__init__('myfriends3')

    def _define_schema(self):
        self._clear_schema()
        self._ensure_schema('CREATE TABLE IF NOT EXISTS ' + self._table_name + ' (id TEXT PRIMARY KEY, type TEXT, name TEXT)')

    def _compose_filter(self, filter: FilterParams):
        filter = filter or FilterParams()
        key = filter.get_as_nullable_string('key')
        content = filter.get_as_nullable_string('content')

        filter_condition = ''
        if key is not None:
            filter_condition += "key='" + key + "'"
        if content is not None:
            filter_condition += "content='" + content + "'"

        return filter_condition

    def get_one_random(self, correlation_id: Optional[str], filter: FilterParams) -> MyFriend:
        return super().get_one_random(correlation_id, self._compose_filter(filter))



from pip_services3_commons.refer import Descriptor
from pip_services3_components.build import Factory


class HelloFriendServiceFactory(Factory):
    def __init__(self):
        super(HelloFriendServiceFactory, self).__init__()

        HttpServiceDescriptor = Descriptor('hello-friend', 'service', 'http', '*', '1.0')  # View
        ControllerDescriptor = Descriptor('hello-friend', 'controller', 'default', '*', '1.0')  # Controller
        PersistenceDescriptor1 = Descriptor('hello-friend', 'persistence', 'mysql', '*', '1.0')  # Persistence
        PersistenceDescriptor2 = Descriptor('hello-friend', 'persistence', 'postgres', '*', '1.0')  # Persistence

        self.register_as_type(HttpServiceDescriptor, HelloFriendRestService)  # View
        self.register_as_type(ControllerDescriptor, HelloFriendController)  # Controller
        self.register_as_type(PersistenceDescriptor1, HelloFriendPersistence1)  # Persistence
        self.register_as_type(PersistenceDescriptor2, HelloFriendPersistence2)  # Persistence



from pip_services3_container.ProcessContainer import ProcessContainer
from pip_services3_rpc.build import DefaultRpcFactory


class HelloFriendProcess(ProcessContainer):

    def __init__(self):
        super(HelloFriendProcess, self).__init__('hello-friend', 'HelloFriend microservice')
        self._config_path = './config.yaml'
        self._factories.add(HelloFriendServiceFactory())
        self._factories.add(DefaultRpcFactory())

# Step 1 - Database selection
import os

os.environ["MYSQL_ENABLED"] = "True"
# os.environ["POSTGRES_ENABLED"] = "True"

# Step 2 - The run() command
if __name__ == '__main__':
    runner = HelloFriendProcess()
    print("run")
    try:
        runner.run()
    except Exception as ex:
        print(ex)
Not available

Running our app

The fact that we can choose between two different databases brings an extra step to our process, which is, selecting the operating database.

This is done by assigning a value to the environmental variable representing the selected database. We can do this by setting our variable from Windows or through code. We can then deselect this database by assigning an empty string or by not assigning a value to its environment variable. The following example shows how to select our MySQL database and run the application.

export async function main() { 
    try {
        // Step 1 - Database selection
        // process.env['MYSQL_ENABLED'] = 'true';
        process.env['POSTGRES_ENABLED'] = 'true';

        // Step 2 - The run() command
        let proc = new HelloFriendProcess();
        proc.run(process.argv);
    } catch (ex) {
        console.error(ex);
    }
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Environment.SetEnvironmentVariable("MYSQL_ENABLED", "true");
            //Environment.SetEnvironmentVariable("POSTGRES_ENABLED", "true");
            var task = (new HelloFriendProcess()).RunAsync(args);
            task.Wait();
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine(ex);
        }
    }
}

func main() {
	// Step 1 - Database selection
	// os.Setenv("MYSQL_ENABLED", "true")
	os.Setenv("POSTGRES_ENABLED", "true")

	// Step 2 - The run() command
	proc := NewHelloFriendProcess()
	proc.Run(context.Background(), os.Args)
}

void main(List<String> argument) async {
  // Step 1 - Database selection
  // Env:
  //  'MYSQL_ENABLED': 'true' 
  //    or
  //  'POSTGRES_ENABLED': 'true'

  // Step 2 - The run() command
  var proc = HelloFriendProcess();
  proc.run(argument);
}

# Step 1 - Database selection
import os

os.environ["MYSQL_ENABLED"] = "True"
#os.environ["POSTGRES_ENABLED"] = "True"

# Step 2 - The run() command
if __name__ == '__main__':
    runner = HelloFriendProcess()
    print("run")
    try:
        runner.run()
    except Exception as ex:
        print(ex)
Not available

Results

If we selected the MySQL database, we will see the following message:

figure 1

Alternatively, if we selected the PostgreSQL database, we will see the following message:

figure 2

Wrapping up

In this tutorial, we have examined how to create a web application that allows us to select between two different databases at deployment time. Although this example is simple, it can be extended to consider many other scenarios, as the principles and concepts employed in its construction are the same.