Deployment time configuration
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()
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)
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))
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))
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)
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)
Results
If we selected the MySQL database, we will see the following message:
Alternatively, if we selected the PostgreSQL database, we will see the following message:
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.