Google Cloud Platform
Key takeaways
CloudFunctionService | Abstract service that receives remote calls via RPC or REST protocols in the Google Function. |
CommandableCloudFunctionService | Abstract service that receives remote calls via RPC or REST protocols in the Google Function and connects them to automatically generated actions for commands defined in ICommandable components. |
CloudFunction | Abstract Google Function that acts as a container. Instantiates and runs components, and exposes them via an external entry point. |
CommandableCloudFunction | Abstract Google Function that acts as a container. Instantiates and runs components, and exposes them via an external entry point. This container is a subclass of the CloudFunction class with added functionality that automatically generates actions for commands defined in ICommandable components. |
Introduction
In this tutorial, you will learn how to create a microservice and package it in a Google Cloud Platform (GCP) container. The content is divided into five sections:
- GCP containers and services: A basic explanation of the different containers and services available in the GCP module.
- Basic microservices: An explanation of how to build basic microservices using GCP containers and services.
- Combining basic microservices: An explanation of how to further expand the basic microservice examples, providing solutions for complex business scenarios.
- Testing: An explanation of how to test GCP-based microservices.
- Wrapping up: A summary of the learned concepts.
Upon completion of this tutorial, you will gain an understanding of the various possibilities that the containers and services available in the GCP module offer for microservice design.
GCP containers and services
The GCP module includes containers and services that conform to Google Cloud’s REST protocol. They are:
Containers
Containers are used to run microservices within a specific environment. They contain all the files, libraries and configuration files necessary for the microservice’s execution.
One such environment is Google Cloud Functions, which is a serverless execution environment for building and connecting cloud services.
The Pip.Services toolkit offers two types of containers for this environment, namely, CloudFunction and CommandableCloudFunction. The second is a subclass of the first and provides additional functionality that automatically generates actions for commands defined in ICommandable components. The following diagram shows the relations between these components:
Services
Additionally, the toolkit offers two types of services: the CloudFunctionService and the CommandableCloudFunctionService. Both are used to connect microservices to external resources. The second service is a subclass of the first and adds additional functionality that automatically generates actions for commands defined in ICommandable components. The following diagram shows their relation:
Basic microservices
A basic GCP microservice contains a controller, which can expose commands directly or be made to include a service layer. Additionally, actions can be registered in the container/service itself or, alternatively, a CommandSet component can be added to the microservice. This part of the tutorial explains these four basic cases.
Microservice doesn’t require a service layer
The following sections explain how to create microservices that expose commands directly, without using a service layer, and how to package them into a container. For this, the toolkit offers two options: either registering the actions in the container, or using a CommandSet component. In the examples below, actions are represented by the greetings() method, which, given a name, returns “Hello, <name>!”.
Registering actions in the container
In this case, the microservice basically consists of a controller packaged in a container. The microservice has the following characteristics:
- It registers the actions in the container via the register() method.
- The business logic of the actions is defined in the controller.
- The container uses a factory to create the controller.
There are two ways to create an instance of the controller. The first is by adding its descriptor to the configuration file so that the container can build it automatically via the factory:
And the second is by registering the controller as a dependency for the container:
When using a configuration file, the container looks like this:
// Controller's descriptor is defined in the configuration file
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
public constructor() {
super("mygroup", "MyGroup");
this._configPath = "./config.yaml"
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
}
private async action(req: any, res: any): Promise<void> {
let name = req.body.name ?? 'default name';
let result = await this._controller.greetings(name);
HttpResponseSender.sendResult(req, res, result);
}
protected register() {
this.registerAction('greetings', null, this.action);
}
}
// Controller's descriptor is defined in the configuration file
public class MyCloudFunction: CloudFunction
{
private MyController _controller;
public MyCloudFunction(): base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
this._controller = references.GetOneRequired<MyController>(new Descriptor("mygroup", "controller", "default", "controller", "*"));
}
private async Task Action(HttpContext context)
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller.Greetings(name);
await HttpResponseSender.SendResultAsync(result);
}
protected override void Register()
{
RegisterAction("greetings", null, Action);
}
}
// Controller's descriptor is defined in the configuration file
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunction(c)
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
res, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
func (c *MyCloudFunction) action(res http.ResponseWriter, req *http.Request) {
var body map[string]string
_ = gcputil.CloudFunctionRequestHelper.DecodeBody(req, &body)
defer req.Body.Close()
name := body["name"]
result, err := c.controller.Greetings(req.Context(), name)
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
}
func (c *MyCloudFunction) Register() {
c.RegisterAction("greetings", nil, c.action)
}
# Controller's descriptor is defined in the configuration file
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._controller: MyController = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
def __action(self, req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get("name", name)
return self._controller.greetings(name), 200
def register(self):
self._register_action("greetings", None, self.__action)
And the configuration file includes the controller’s descriptor:
# Controller
- descriptor: "mygroup:controller:default:controller:1.0"
When adding the controller as a dependency, the container looks like this:
// Controller is added as a dependency
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
public constructor() {
super("mygroup", "MyGroup");
this._configPath = "./config.yaml"
this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = this._dependencyResolver.getOneRequired('conroller');
}
private async action(req: any, res: any): Promise<void> {
let name = req.body.name ?? 'default name';
let result = await this._controller.greetings(name);
HttpResponseSender.sendResult(req, res, result);
}
protected register() {
this.registerAction('greetings', null, this.action);
}
}
// Controller is added as a dependency
public class MyCloudFunction: CloudFunction
{
private MyController _controller;
public MyCloudFunction(): base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = _dependencyResolver.GetOneRequired<MyController>("controller");
}
private async Task Action(HttpContext context)
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller.Greetings(name);
await HttpResponseSender.SendResultAsync(result);
}
protected override void Register()
{
RegisterAction("greetings", null, Action);
}
}
// Controller is added as a dependency
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunction(c)
c.SetConfigPath("./config.yaml")
c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
res, err := c.DependencyResolver.GetOneRequired("conroller")
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
func (c *MyCloudFunction) action(res http.ResponseWriter, req *http.Request) {
var body map[string]string
_ = gcputil.CloudFunctionRequestHelper.DecodeBody(req, &body)
defer req.Body.Close()
name := body["name"]
result, err := c.controller.Greetings(req.Context(), name)
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
}
func (c *MyCloudFunction) Register() {
c.RegisterAction("greetings", nil, c.action)
}
# Controller is added as a dependency
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._controller: MyController = None
self._config_path = './config.yaml'
self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._factories.add(MyFactory())
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = self._dependency_resolver.get_one_required('controller')
def __action(self, req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get("name", name)
return self._controller.greetings(name), 200
def register(self):
self._register_action("greetings", None, self.__action)
In both cases, implementation of the logic required from the actions will be stored in the controller:
export class MyController implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greetings(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
public class MyController : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
type MyController struct{}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
class MyController(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greetings(self, name: str):
return f"Hello, {name} !"
Which is then registered in the factory:
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
}
}
public class MyFactory: Factory
{
public MyFactory(): base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
}
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
return c
}
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
Finally, after grouping everything together, the resulting code is as follows:
import { Descriptor, IReferenceable, IReferences } from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";
import { CloudFunction } from "pip-services3-gcp-nodex";
import { HttpResponseSender } from "pip-services3-rpc-nodex";
export class MyController implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greetings(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
}
}
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
public constructor() {
super("mygroup", "MyGroup");
this._configPath = "./config.yaml"
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
}
private async action(req: any, res: any): Promise<void> {
let name = req.body.name ?? 'default name';
let result = await this._controller.greetings(name);
HttpResponseSender.sendResult(req, res, result);
}
protected register() {
this.registerAction('greetings', null, this.action);
}
}
using Microsoft.AspNetCore.Http;
using PipServices3.Commons.Refer;
using PipServices3.Components.Build;
using PipServices3.Gcp.Containers;
using PipServices3.Rpc.Services;
using System.Threading.Tasks;
namespace ExampleApp
{
public class MyController : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
public class MyFactory : Factory
{
public MyFactory() : base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
}
}
public class MyCloudFunction: CloudFunction
{
private MyController _controller;
public MyCloudFunction(): base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
//_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// _controller = _dependencyResolver.GetOneRequired<MyController>("controller");
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
_controller = references.GetOneRequired<MyController>(new Descriptor("mygroup", "controller", "default", "controller", "*"));
}
private async Task Action(HttpContext context)
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller.GreetingsAsync(name);
await HttpResponseSender.SendResultAsync(context.Response, result);
}
protected override void Register()
{
RegisterAction("greetings", null, Action);
}
}
}
import (
"context"
"net/http"
cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
gcpcont "github.com/pip-services3-gox/pip-services3-gcp-gox/containers"
gcputil "github.com/pip-services3-gox/pip-services3-gcp-gox/utils"
rpcserv "github.com/pip-services3-gox/pip-services3-rpc-gox/services"
)
type MyController struct{}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
return c
}
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunction(c)
c.SetConfigPath("./config.yaml")
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// res, err := c.DependencyResolver.GetOneRequired("conroller")
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
res, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
func (c *MyCloudFunction) action(res http.ResponseWriter, req *http.Request) {
var body map[string]string
_ = gcputil.CloudFunctionRequestHelper.DecodeBody(req, &body)
defer req.Body.Close()
name := body["name"]
result, err := c.controller.Greetings(req.Context(), name)
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
}
func (c *MyCloudFunction) Register() {
c.RegisterAction("greetings", nil, c.action)
}
import flask
from pip_services3_gcp.containers import CloudFunction
from pip_services3_commons.refer import IReferences
from pip_services3_commons.refer import Descriptor, IReferences, IReferenceable
from pip_services3_components.build import Factory
class MyController(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greetings(self, name: str):
return f"Hello, {name} !"
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._controller: MyController = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
# Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
# self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
def set_references(self, references: IReferences):
super().set_references(references)
# Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
# self._controller = self._dependency_resolver.get_one_required('controller')
# Comment out the next line of code when using the dependency resolver, uncomment for configuration file
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
def __action(self, req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get("name", name)
return self._controller.greetings(name), 200
def register(self):
self._register_action("greetings", None, self.__action)
Using a CommandSet component
Similar to the previous case, the microservice will still contain just a controller packaged in a container, but will use a CommandSet component to define the actions instead. The main characteristics of this approach are:
- The actions are defined in the command set.
- The controller links to the CommandSet via the getCommandSet() method.
- The business logic of the actions is still defined in the controller.
- The container uses a factory to create the controller.
As in the previous case, the container can create the controller via a configuration file:
or by adding it as a dependency:
In both cases, the container is of the CommandableCloudFunction type, which allows it to utilize the Commandable pattern.
When using a configuration file to create the controller, the container looks like this:
// Controller's descriptor is defined in the configuration file
export class MyCommandableCloudFunction extends CommandableCloudFunction {
private _controller: MyController;
public constructor() {
super("mygroup", "MyGroup");
this._configPath = "./config.yaml";
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
}
}
// Controller's descriptor is defined in the configuration file
public class MyCommandableCloudFunction: CommandableCloudFunction
{
private MyController _controller;
public MyCommandableCloudFunction(): base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = references.GetOneRequired<MyController>(new Descriptor("mygroup", "controller", "default", "controller", "*"));
}
}
// Controller's descriptor is defined in the configuration file
type MyCommandableCloudFunction struct {
*gcpcont.CommandableCloudFunction
controller *MyController
}
func NewMyCommandableCloudFunction() *MyCommandableCloudFunction {
c := &MyCommandableCloudFunction{}
c.CommandableCloudFunction = gcpcont.NewCommandableCloudFunction()
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCommandableCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CommandableCloudFunction.SetReferences(ctx, references)
res, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
# Controller's descriptor is defined in the configuration file
class MyCommandableCloudFunction(CommandableCloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._controller: MyController = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
And, when adding the controller as a dependency, it looks like this:
// Controller is added as a dependency
export class MyCommandableCloudFunction extends CommandableCloudFunction {
private _controller: MyController;
public constructor() {
super("mygroup", "MyGroup");
this._configPath = "./config.yaml";
this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = this._dependencyResolver.getOneRequired('controller');
}
}
// Controller is added as a dependency
public class MyCommandableCloudFunction: CommandableCloudFunction
{
private MyController _controller;
public MyCommandableCloudFunction(): base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = _dependencyResolver.GetOneRequired<MyController>("controller");
}
}
// Controller is added as a dependency
type MyCommandableCloudFunction struct {
*gcpcont.CommandableCloudFunction
controller *MyController
}
func NewMyCommandableCloudFunction() *MyCommandableCloudFunction {
c := &MyCommandableCloudFunction{}
c.CommandableCloudFunction = gcpcont.NewCommandableCloudFunction()
c.SetConfigPath("./config.yaml")
c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCommandableCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CommandableCloudFunction.SetReferences(ctx, references)
res, err := c.DependencyResolver.GetOneRequired("controller")
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
# Controller is added as a dependency
class MyCommandableCloudFunction(CommandableCloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._controller: MyController = None
self._config_path = './config.yaml'
self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._factories.add(MyFactory())
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = self._dependency_resolver.get_one_required('controller')
In both cases, the controller is registered in the factory:
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController);
}
}
public class MyFactory : Factory
{
public MyFactory(): base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
}
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
return c
}
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
Since we’re going to use the Commandable pattern, the controller needs to implement the ICommandable interface, which declares the getCommandSet method. Additionally, the controller must include the implementation of the actions’ business logic. Such a controller’s code will look like this:
export class MyController implements IReferenceable, ICommandable {
private commandSet: CommandSet;
public setReferences(references: IReferences): void {
}
public getCommandSet(): CommandSet {
if (this.commandSet == null) {
this.commandSet = new MyCommandSet(this);
}
return this.commandSet;
}
public async greetings(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
public class MyController: IReferenceable, ICommandable
{
private CommandSet commandSet;
public void SetReferences(IReferences references)
{
}
public CommandSet GetCommandSet()
{
if (commandSet == null)
commandSet = new MyCommandSet(this);
return commandSet;
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
type MyController struct {
commandSet *MyCommandSet
}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) GetCommandSet() *MyCommandSet {
if c.commandSet == nil {
c.commandSet = NewMyCommandSet(c)
}
return c.commandSet
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
class MyController(IReferenceable, ICommandable):
def __init__(self):
super().__init__()
self.__command_set = None
def set_references(self, references):
pass
def get_command_set(self):
if self.__command_set is None:
self.__command_set = MyCommandSet(self)
return self.__command_set
def greeting(self, name):
return f"Hello, {name} !"
The CommandSet in this case would be:
export class MyCommandSet extends CommandSet {
private _controller: MyController;
public constructor(controller: MyController) {
super();
this._controller = controller;
this.addCommand(this.makeGreeting());
}
private makeGreeting(): Command {
return new Command(
"greetings",
new ObjectSchema(true).withRequiredProperty("name", TypeCode.String),
async (correlationId: string, args: Parameters) => {
let name = args.getAsString("name");
return await this._controller.greetings(name);
}
);
}
}
public class MyCommandSet: CommandSet
{
private MyController _controller;
public MyCommandSet(MyController controller): base()
{
_controller = controller;
AddCommand(MakeGreeting());
}
private Command MakeGreeting()
{
return new Command(
"greetings",
new ObjectSchema().WithRequiredProperty("name", TypeCode.String),
async (string correlationId, Parameters args) =>
{
var name = args.GetAsString("name");
return await _controller.GreetingsAsync(name);
}
);
}
}
type MyCommandSet struct {
*ccomand.CommandSet
controller *MyController
}
func NewMyCommandSet(controller *MyController) *MyCommandSet {
c := &MyCommandSet{}
c.controller = controller
c.AddCommand(c.MakeGreeting())
return c
}
func (c *MyCommandSet) MakeGreeting() *ccomand.Command {
return ccomand.NewCommand(
"greetings",
cvalid.NewObjectSchema().WithRequiredProperty("name", cconv.String),
func(ctx context.Context, correlationId string, args *crun.Parameters) (any, error) {
name := args.GetAsString("name")
return c.controller.Greetings(ctx, name)
},
)
}
class MyCommandSet(CommandSet):
_controller: 'MyController'
def __init__(self, controller):
super().__init__()
self._controller = controller
self.add_command(self._make_greeting())
def _make_greeting(self):
def handler(correlation_id: Optional[str], args: Parameters):
name = args.get_as_string("name")
res = self._controller.greeting(name)
return res
return Command(
"greetings",
ObjectSchema(True).with_required_property("name", TypeCode.String),
handler
)
After combining everything, the final code is:
import { Command, CommandSet, Descriptor, ICommandable, IReferenceable, IReferences, ObjectSchema, TypeCode, Parameters } from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";
import { CommandableCloudFunction } from "pip-services3-gcp-nodex";
export class MyCommandSet extends CommandSet {
private _controller: MyController;
public constructor(controller: MyController) {
super();
this._controller = controller;
this.addCommand(this.makeGreeting());
}
private makeGreeting(): Command {
return new Command(
"greetings",
new ObjectSchema(true).withRequiredProperty("name", TypeCode.String),
async (correlationId: string, args: Parameters) => {
let name = args.getAsString("name");
return await this._controller.greetings(name);
}
);
}
}
export class MyController implements IReferenceable, ICommandable {
private commandSet: CommandSet;
public setReferences(references: IReferences): void {
}
public getCommandSet(): CommandSet {
if (this.commandSet == null) {
this.commandSet = new MyCommandSet(this);
}
return this.commandSet;
}
public async greetings(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController);
}
}
export class MyCommandableCloudFunction extends CommandableCloudFunction {
private _controller: MyController;
public constructor() {
super("mygroup", "MyGroup");
this._configPath = "./config.yaml";
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// this._controller = this._dependencyResolver.getOneRequired('controller');
// Comment out the next line of code when using dependency resolver, uncomment for configuration file
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
}
}
using PipServices3.Commons.Commands;
using PipServices3.Commons.Refer;
using PipServices3.Commons.Run;
using PipServices3.Commons.Validate;
using PipServices3.Components.Build;
using PipServices3.Gcp.Containers;
using System.Threading.Tasks;
using TypeCode = PipServices3.Commons.Convert.TypeCode;
namespace ExampleApp.Commandable
{
public class MyCommandSet : CommandSet
{
private MyController _controller;
public MyCommandSet(MyController controller) : base()
{
_controller = controller;
AddCommand(MakeGreeting());
}
private Command MakeGreeting()
{
return new Command(
"greetings",
new ObjectSchema().WithRequiredProperty("name", TypeCode.String),
async (string correlationId, Parameters args) =>
{
var name = args.GetAsString("name");
return await _controller.GreetingsAsync(name);
}
);
}
}
public class MyController : IReferenceable, ICommandable
{
private CommandSet commandSet;
public void SetReferences(IReferences references)
{
}
public CommandSet GetCommandSet()
{
if (commandSet == null)
commandSet = new MyCommandSet(this);
return commandSet;
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
public class MyFactory : Factory
{
public MyFactory() : base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
}
}
public class MyCommandableCloudFunction : CommandableCloudFunction
{
private MyController _controller;
public MyCommandableCloudFunction() : base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
//_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// _controller = _dependencyResolver.GetOneRequired<MyController>("controller");
// Comment out the next line of code when using dependency resolver, uncomment for configuration file
_controller = references.GetOneRequired<MyController>(new Descriptor("mygroup", "controller", "default", "controller", "*"));
}
}
}
import (
"context"
ccomand "github.com/pip-services3-gox/pip-services3-commons-gox/commands"
cconv "github.com/pip-services3-gox/pip-services3-commons-gox/convert"
cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
crun "github.com/pip-services3-gox/pip-services3-commons-gox/run"
cvalid "github.com/pip-services3-gox/pip-services3-commons-gox/validate"
cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
gcpcont "github.com/pip-services3-gox/pip-services3-gcp-gox/containers"
)
type MyCommandSet struct {
*ccomand.CommandSet
controller *MyController
}
func NewMyCommandSet(controller *MyController) *MyCommandSet {
c := &MyCommandSet{}
c.controller = controller
c.AddCommand(c.MakeGreeting())
return c
}
func (c *MyCommandSet) MakeGreeting() *ccomand.Command {
return ccomand.NewCommand(
"greetings",
cvalid.NewObjectSchema().WithRequiredProperty("name", cconv.String),
func(ctx context.Context, correlationId string, args *crun.Parameters) (any, error) {
name := args.GetAsString("name")
return c.controller.Greetings(ctx, name)
},
)
}
type MyController struct {
commandSet *MyCommandSet
}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) GetCommandSet() *MyCommandSet {
if c.commandSet == nil {
c.commandSet = NewMyCommandSet(c)
}
return c.commandSet
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
return c
}
type MyCommandableCloudFunction struct {
*gcpcont.CommandableCloudFunction
controller *MyController
}
func NewMyCommandableCloudFunction() *MyCommandableCloudFunction {
c := &MyCommandableCloudFunction{}
c.CommandableCloudFunction = gcpcont.NewCommandableCloudFunction()
c.SetConfigPath("./config.yaml")
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCommandableCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CommandableCloudFunction.SetReferences(ctx, references)
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// res, err := c.DependencyResolver.GetOneRequired("controller")
// Comment out the next line of code when using dependency resolver, uncomment for configuration file
res, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
from typing import Optional
from pip_services3_commons.commands import Command, CommandSet
from pip_services3_commons.convert import TypeCode
from pip_services3_commons.run import Parameters
from pip_services3_commons.validate import ObjectSchema
from pip_services3_commons.refer import IReferenceable, IReferences, Descriptor
from pip_services3_commons.commands import ICommandable
from pip_services3_components.build import Factory
from pip_services3_gcp.containers import CommandableCloudFunction
class MyCommandSet(CommandSet):
_controller: 'MyController'
def __init__(self, controller):
super().__init__()
self._controller = controller
self.add_command(self._make_greeting())
def _make_greeting(self):
def handler(correlation_id: Optional[str], args: Parameters):
name = args.get_as_string("name")
res = self._controller.greeting(name)
return res
return Command(
"greetings",
ObjectSchema(True).with_required_property("name", TypeCode.String),
handler
)
class MyController(IReferenceable, ICommandable):
def __init__(self):
super().__init__()
self.__command_set = None
def set_references(self, references):
pass
def get_command_set(self):
if self.__command_set is None:
self.__command_set = MyCommandSet(self)
return self.__command_set
def greeting(self, name):
return f"Hello, {name} !"
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
class MyCommandableCloudFunction(CommandableCloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._controller: MyController = None
self._config_path = './config.yaml'
# Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
# self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._factories.add(MyFactory())
def set_references(self, references: IReferences):
super().set_references(references)
# Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
# self._controller = self._dependency_resolver.get_one_required('controller')
# Comment out the next line of code when using dependency resolver, uncomment for configuration file
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
Microservice requires a service layer
This section expands on the previous two cases by adding a service layer to expose the actions contained in the microservice. As a result, actions can now be registered in the service (instead of the container) or defined in a CommandSet component. As in the previous examples, actions are represented by the greetings()
method.
Registering actions in the service
In this case, the microservice has a service and a controller, both of which get packaged into the container. As such, it has the following characteristics:
- Actions are registered in the service.
- The business logic of the actions is defined in the controller.
- The container uses a factory to create the controller and the service.
Similar to the previous examples, the service and the controller can be instantiated by the container via a factory that obtains information from a configuration file:
or by defining them as the container’s dependencies:
In the first case, the container looks like this:
// Controller's and service’s descriptors are defined in the configuration file
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
private _service: MyCloudService;
public constructor() {
super("mygroup", "Mygroup service");
this._configPath = "./config.yaml";
this._factories.add(MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
this._service = references.getOneRequired(new Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'));
}
}
// Controller's and service’s descriptors are defined in the configuration file
public class MyCloudFunction : CloudFunction
{
private MyController _controller;
private MyCloudService _service;
public MyCloudFunction() : base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = references.GetOneRequired<MyController>(new Descriptor("mygroup", "controller", "default", "controller", "*"));
_service = references.GetOneRequired<MyCloudService>(new Descriptor("mygroup", "service", "gcp-function", "function", "*"));
}
}
// Controller's and service’s descriptors are defined in the configuration file
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
service *MyCloudService
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup service")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
ctrl, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
serv, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "service", "gcp-function", "controller", "*"))
if err != nil {
panic("Service is not found")
}
c.controller = ctrl.(*MyController)
c.service = serv.(*MyCloudService)
}
# Controller's and service’s descriptors are defined in the configuration file
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "Mygroup service")
self._controller: MyController = None
self._service: MyCloudService = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._service = references.get_one_required(Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'))
with the configuration file containing the descriptors of the service and controller:
# Console logger
- descriptor: "pip-services:logger:console:default:1.0"
level: "trace"
# Controller
- descriptor: "mygroup:controller:default:controller:1.0"
# Service
- descriptor: "mygroup:service:gcp-function:function:1.0"
And, in the second case, the container adds the dependencies via the dependency resolver:
// Controller and service are added as dependencies
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
private _service: MyCloudService;
public constructor() {
super("mygroup", "Mygroup service");
this._configPath = "./config.yaml";
this._factories.add(new MyFactory());
this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
this._dependencyResolver.put('service', new Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'))
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = this._dependencyResolver.getOneRequired('controller');
this._service = this._dependencyResolver.getOneRequired('service');
}
}
// Controller and service are added as dependencies
public class MyCloudFunction : CloudFunction
{
private MyController _controller;
private MyCloudService _service;
public MyCloudFunction() : base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
_dependencyResolver.Put("service", new Descriptor("mygroup", "service", "gcp-function", "function", "*"));
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = _dependencyResolver.GetOneRequired<MyController>("controller");
_service = _dependencyResolver.GetOneRequired<MyController>("service");
}
}
// Controller and service are added as dependencies
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
service *MyCloudService
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup service")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
c.DependencyResolver.Put(context.Background(), "service", cref.NewDescriptor("mygroup", "service", "gcp-function", "function", "*"))
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
ctrl, err := c.DependencyResolver.GetOneRequired("controller")
if err != nil {
panic("Controller is not found")
}
serv, err := c.DependencyResolver.GetOneRequired("service")
if err != nil {
panic("Service is not found")
}
c.controller = ctrl.(*MyController)
c.service = serv.(*MyCloudService)
}
# Controller and service are added as dependencies
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "Mygroup service")
self._controller: MyController = None
self._service: MyCloudService = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._dependency_resolver.put('service', Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'))
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = self._dependency_resolver.get_one_required('controller')
self._service = self._dependency_resolver.get_one_required('service')
In either case, both components must be registered in the factory:
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController);
this.registerAsType(new Descriptor("mygroup", "service", "gcp-function", "function", "1.0"), MyCloudService);
}
}
public class MyFactory: Factory
{
public MyFactory(): base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
RegisterAsType(new Descriptor("mygroup", "service", "gcp-function", "function", "1.0"), typeof(MyCloudService));
}
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
c.RegisterType(cref.NewDescriptor("mygroup", "service", "gcp-function", "function", "1.0"), NewMyCloudService)
return c
}
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
self.register_as_type(Descriptor("mygroup", "service", "gcp-function", "function", "1.0"), MyCloudService)
The actions are registered in the service, which also adds the controller as a dependency:
{
export class MyCloudService extends CloudFunctionService {
private _controller: MyController;
private _headers = {
'Content-Type': 'application/json'
}
public constructor() {
super();
this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'));
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = this._dependencyResolver.getOneRequired('controller');
}
protected register(): void {
this.registerAction(
'greetings',
new ObjectSchema(true)
.withRequiredProperty('body',
new ObjectSchema()
.withRequiredProperty('name', TypeCode.String)
),
async (req: Request, res: Response) => {
let params = CloudFunctionRequestHelper.getParameters(req);
let name = params.getAsStringWithDefault('name', 'default name');
let result = await this._controller.greeting(name);
for (let key of Object.keys(this._headers))
res.headers.append(key, this._headers[key]);
HttpResponseSender.sendResult(req, res, result);
}
);
}
}
public class MyCloudService : CloudFunctionService
{
private MyController _controller;
private IDictionary<string, string> _headers = new Dictionary<string, string>() { { "Content-Type", "application/json" } };
public MyCloudService() : base()
{
_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "1.0"));
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = _dependencyResolver.GetOneRequired<MyController>("controller");
}
protected override void Register()
{
RegisterAction(
"greetings",
new ObjectSchema()
.WithRequiredProperty("body",
new ObjectSchema()
.WithRequiredProperty("name", TypeCode.String)
),
async (HttpContext context) =>
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller.GreetingsAsync(name);
foreach (var key in _headers.Keys)
context.Response.Headers.Add(key, _headers[key]);
await HttpResponseSender.SendResultAsync(context.Response, result);
}
);
}
}
type MyCloudService struct {
*gcpserv.CloudFunctionService
controller *MyController
headers map[string]string
}
func NewMyCloudService() *MyCloudService {
c := &MyCloudService{}
c.CloudFunctionService = gcpserv.NewCloudFunctionService("")
c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"))
return c
}
func (c *MyCloudService) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunctionService.SetReferences(ctx, references)
res, err := c.DependencyResolver.GetOneRequired("controller")
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
func (c *MyCloudService) Register() {
c.RegisterAction(
"greetings",
cvalid.NewObjectSchema().WithRequiredProperty("body", cvalid.NewObjectSchema().WithRequiredProperty("name", cconv.String)).Schema,
func(res http.ResponseWriter, req *http.Request) {
params := gcputil.CloudFunctionRequestHelper.GetParameters(req)
name := params.GetAsStringWithDefault("name", "default name")
result, err := c.controller.Greetings(req.Context(), name)
for key, value := range c.headers {
res.Header().Add(key, value)
}
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
},
)
}
class MyCloudService(CloudFunctionService):
def __init__(self):
super().__init__('mygroup')
self._dependency_resolver.put('controller',
Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'))
self._controller: MyController = None
self._headers = {
'Content-Type': 'application/json'
}
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = self._dependency_resolver.get_one_required('controller')
def register(self):
def handler(req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get('name', name)
res = self._controller.greeting(name)
return res, 200
self._register_action(
'greetings',
ObjectSchema(True).with_required_property("body",
ObjectSchema().with_required_property("name", TypeCode.String)),
handler
)
And the actions’ business logic is defined in the controller:
export class MyController implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greetings(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
public class MyController : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
type MyController struct{}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
class MyController(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greeting(self, name):
return f"Hello, {name} !"
After grouping everything together, the final code is:
import { Descriptor, IReferenceable, IReferences, ObjectSchema, TypeCode } from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";
import { CloudFunction, CloudFunctionRequestHelper, CloudFunctionService } from "pip-services3-gcp-nodex";
import { HttpResponseSender } from "pip-services3-rpc-nodex";
export class MyCloudService extends CloudFunctionService {
private _controller: MyController;
private _headers = {
'Content-Type': 'application/json'
}
public constructor() {
super();
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'));
}
public setReferences(references: IReferences): void {
super.setReferences(references);
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// this._controller = this._dependencyResolver.getOneRequired('controller');
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'))
}
protected register(): void {
this.registerAction(
'greetings',
new ObjectSchema(true)
.withRequiredProperty('body',
new ObjectSchema()
.withRequiredProperty('name', TypeCode.String)
),
async (req: Request, res: Response) => {
let params = CloudFunctionRequestHelper.getParameters(req);
let name = params.getAsStringWithDefault('name', 'default name');
let result = await this._controller.greeting(name);
for (let key of Object.keys(this._headers))
res.headers.append(key, this._headers[key]);
HttpResponseSender.sendResult(req, res, result);
}
);
}
}
export class MyController implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greeting(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController);
this.registerAsType(new Descriptor("mygroup", "service", "gcp-function", "function", "1.0"), MyCloudService);
}
}
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
private _service: MyCloudService;
public constructor() {
super("mygroup", "Mygroup service");
this._configPath = "./config.yaml";
this._factories.add(new MyFactory());
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
// this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
// this._dependencyResolver.put('service', new Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'))
}
public setReferences(references: IReferences): void {
super.setReferences(references);
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
// this._controller = this._dependencyResolver.getOneRequired('controller');
// this._service = this._dependencyResolver.getOneRequired('service');
// Comment out the next two lines of code when using the dependency resolver, uncomment for configuration file
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
this._service = references.getOneRequired(new Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'));
}
}
using Microsoft.AspNetCore.Http;
using PipServices3.Commons.Refer;
using PipServices3.Commons.Validate;
using PipServices3.Components.Build;
using PipServices3.Gcp.Containers;
using PipServices3.Gcp.Services;
using PipServices3.Rpc.Services;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ExampleApp
{
public class MyCloudService : CloudFunctionService
{
private MyController _controller;
private IDictionary<string, string> _headers = new Dictionary<string, string>() { { "Content-Type", "application/json" } };
public MyCloudService() : base()
{
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
//_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "1.0"));
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
//_controller = _dependencyResolver.GetOneRequired<MyController>("controller");
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
_controller = references.GetOneRequired<MyController>("controller");
}
protected override void Register()
{
RegisterAction(
"greetings",
new ObjectSchema()
.WithRequiredProperty("body",
new ObjectSchema()
.WithRequiredProperty("name", TypeCode.String)
),
async (HttpContext context) =>
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller.GreetingsAsync(name);
foreach (var key in _headers.Keys)
context.Response.Headers.Add(key, _headers[key]);
await HttpResponseSender.SendResultAsync(context.Response, result);
}
);
}
}
public class MyController : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
public class MyFactory : Factory
{
public MyFactory() : base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
RegisterAsType(new Descriptor("mygroup", "service", "gcp-function", "function", "1.0"), typeof(MyCloudService));
}
}
public class MyCloudFunction : CloudFunction
{
private MyController _controller;
private MyCloudService _service;
public MyCloudFunction() : base("mygroup", "MyGroup")
{
_configPath = "./config.yaml";
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
//_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
//_dependencyResolver.Put("service", new Descriptor("mygroup", "service", "gcp-function", "function", "*"));
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// _controller = _dependencyResolver.GetOneRequired<MyController>("controller");
// _service = _dependencyResolver.GetOneRequired<MyController>("service");
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
_controller = references.GetOneRequired<MyController>(new Descriptor("mygroup", "controller", "default", "controller", "*"));
_service = references.GetOneRequired<MyCloudService>(new Descriptor("mygroup", "service", "gcp-function", "function", "*"));
}
}
}
import (
"context"
"net/http"
cconv "github.com/pip-services3-gox/pip-services3-commons-gox/convert"
cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
cvalid "github.com/pip-services3-gox/pip-services3-commons-gox/validate"
cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
gcpcont "github.com/pip-services3-gox/pip-services3-gcp-gox/containers"
gcpserv "github.com/pip-services3-gox/pip-services3-gcp-gox/services"
gcputil "github.com/pip-services3-gox/pip-services3-gcp-gox/utils"
rpcserv "github.com/pip-services3-gox/pip-services3-rpc-gox/services"
)
type MyCloudService struct {
*gcpserv.CloudFunctionService
controller *MyController
headers map[string]string
}
func NewMyCloudService() *MyCloudService {
c := &MyCloudService{}
c.CloudFunctionService = gcpserv.NewCloudFunctionService("")
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"))
return c
}
func (c *MyCloudService) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunctionService.SetReferences(ctx, references)
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// res, err := c.DependencyResolver.GetOneRequired("controller")
res, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"))
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController)
}
func (c *MyCloudService) Register() {
c.RegisterAction(
"greetings",
cvalid.NewObjectSchema().WithRequiredProperty("body", cvalid.NewObjectSchema().WithRequiredProperty("name", cconv.String)).Schema,
func(res http.ResponseWriter, req *http.Request) {
params := gcputil.CloudFunctionRequestHelper.GetParameters(req)
name := params.GetAsString("name")
result, err := c.controller.Greetings(req.Context(), name)
for key, value := range c.headers {
res.Header().Add(key, value)
}
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
},
)
}
type MyController struct{}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
c.RegisterType(cref.NewDescriptor("mygroup", "service", "gcp-function", "function", "1.0"), NewMyCloudService)
return c
}
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
service *MyCloudService
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup service")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
// c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
// c.DependencyResolver.Put(context.Background(), "service", cref.NewDescriptor("mygroup", "service", "gcp-function", "function", "*"))
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// ctrl, err := c.DependencyResolver.GetOneRequired("controller")
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
ctrl, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// serv, err := c.DependencyResolver.GetOneRequired("service")
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
import flask
from pip_services3_gcp.containers import CloudFunction
from pip_services3_gcp.services import CloudFunctionService
from pip_services3_commons.refer import Descriptor, IReferences, IReferenceable
from pip_services3_components.build import Factory
from pip_services3_commons.convert import TypeCode
from pip_services3_commons.run import Parameters
from pip_services3_commons.validate import ObjectSchema
class MyCloudService(CloudFunctionService):
def __init__(self):
super().__init__('mygroup')
# Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
# self._dependency_resolver.put('controller',
Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'))
self._controller: MyController = None
self._headers = {
'Content-Type': 'application/json'
}
def set_references(self, references: IReferences):
super().set_references(references)
# Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
# self._controller = self._dependency_resolver.get_one_required('controller')
# Comment out the next line of code when using the dependency resolver, uncomment for configuration file
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'))
def register(self):
def handler(req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get('name', name)
res = self._controller.greeting(name)
return res, 200
self._register_action(
'greetings',
ObjectSchema(True).with_required_property("body",
ObjectSchema().with_required_property("name", TypeCode.String)),
handler
)
class MyController(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greeting(self, name):
return f"Hello, {name} !"
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
self.register_as_type(Descriptor("mygroup", "service", "gcp-function", "function", "1.0"), MyCloudService)
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "Mygroup service")
self._controller: MyController = None
self._service: MyCloudService = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
# Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
# self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
# self._dependency_resolver.put('service', Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'))
def set_references(self, references: IReferences):
super().set_references(references)
# Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
# self._controller = self._dependency_resolver.get_one_required('controller')
# self._service = self._dependency_resolver.get_one_required('service')
# Comment out the next two lines of code when using the dependency resolver, uncomment for configuration file
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._service = references.get_one_required(Descriptor('mygroup', 'service', 'gcp-function', 'function', '*'))
Using a CommandSet component
In this case, a CommandSet component containing the definitions of the actions is added to the structure of the microservice from the previous example. The characteristics of such a microservice are:
- The service is of the CommandableCloudService type, which has the added functionality of automatically generating the necessary operations for commands defined in ICommandable components.
- The controller links to the CommandSet via the getCommandSet() method.
- The required business logic of the actions is still defined in the controller.
- The container uses a factory to create the service and the controller.
As in the previous cases, the controller and the service can be instantiated by the container via a factory that obtains the necessary information from a configuration file:
or by defining them as container dependencies:
In the first case, the container looks like this:
// Controller's and service’s descriptors are defined in the configuration file
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
private _service: CloudFunctionService;
public constructor() {
super("mygroup", "Mygroup service");
this._configPath = "./config.yaml";
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
this._service = references.getOneRequired(new Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'));
}
}
// Controller's and service’s descriptors are defined in the configuration file
public class MyCloudFunction: CloudFunction
{
private MyController _controller;
private CloudFunctionService _service;
public MyCloudFunction(): base("mygroup", "Mygroup service")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = references.GetOneRequired<MyController>(new Descriptor("mygroup", "controller", "default", "controller", "*"));
_service = references.GetOneRequired<CloudFunctionService>(new Descriptor("mygroup", "service", "commandable-gcp-function", "function", "*"));
}
}
// Controller's and service’s descriptors are defined in the configuration file
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
service *gcpserv.CloudFunctionService
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup service")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
ctrl, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
serv, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "service", "commandable-gcp-function", "function", "*"))
if err != nil {
panic("Service is not found")
}
c.controller = ctrl.(*MyController)
c.service = serv.(*gcpserv.CloudFunctionService)
}
# Controller's and service’s descriptors are defined in the configuration file
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "Mygroup service")
self._controller: MyController = None
self._service: CloudFunctionService = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._service = references.get_one_required(Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'))
And loads a configuration file that includes the descriptors of the service and the controller:
# Controller
- descriptor: "mygroup:controller:default:controller:1.0"
# Service
- descriptor: "mygroup:service:commandable-gcp-function:function:1.0"
When considering the service and controller as dependencies of the container, the code is:
// Controller and service are added as dependencies
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
private _service: CloudFunctionService;
public constructor() {
super("mygroup", "Mygroup service");
this._configPath = "./config.yaml";
this._factories.add(new MyFactory());
this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
this._dependencyResolver.put('service', new Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'))
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = this._dependencyResolver.getOneRequired('controller');
this._service = this._dependencyResolver.getOneRequired('service');
}
}
// Controller and service are added as dependencies
public class MyCloudFunction : CloudFunction
{
private MyController _controller;
private CloudFunctionService _service;
public MyCloudFunction() : base("mygroup", "Mygroup service")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
_dependencyResolver.Put("service", new Descriptor("mygroup", "service", "commandable-gcp-function", "function", "*"));
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = _dependencyResolver.GetOneRequired<MyController>("controller");
_service = _dependencyResolver.GetOneRequired<CloudFunctionService>("service");
}
}
// Controller and service are added as dependencies
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
service *gcpserv.CloudFunctionService
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup service")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
c.DependencyResolver.Put(context.Background(), "service", cref.NewDescriptor("mygroup", "service", "commandable-gcp-function", "function", "*"))
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
ctrl, err := c.DependencyResolver.GetOneRequired("controller")
if err != nil {
panic("Controller is not found")
}
serv, err := c.DependencyResolver.GetOneRequired("service")
if err != nil {
panic("Service is not found")
}
c.controller = ctrl.(*MyController)
c.service = serv.(*gcpserv.CloudFunctionService)
}
# Controller and service are added as dependencies
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "Mygroup service")
self._controller: MyController = None
self._service: MyCloudFunction = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._dependency_resolver.put('service', Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'))
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = self._dependency_resolver.get_one_required('controller')
self._service = self._dependency_resolver.get_one_required('service')
In both cases, the command set is:
export class MyCommandSet extends CommandSet {
private _controller: MyController;
public constructor(controller: MyController) {
super();
this._controller = controller;
this.addCommand(this.makeGreeting());
}
private makeGreeting(): Command {
return new Command(
"greetings",
new ObjectSchema(true).withRequiredProperty("name", TypeCode.String),
async (correlationId: string, args: Parameters) => {
let name = args.getAsString("name");
return await this._controller.greetings(name);
}
);
}
}
public class MyCommandSet : CommandSet
{
private MyController _controller;
public MyCommandSet(MyController controller) : base()
{
_controller = controller;
AddCommand(MakeGreeting());
}
private Command MakeGreeting()
{
return new Command(
"greetings",
new ObjectSchema().WithRequiredProperty("name", TypeCode.String),
async (string correlationId, Parameters args) =>
{
var name = args.GetAsString("name");
return await _controller.GreetingsAsync(name);
}
);
}
}
type MyCommandSet struct {
*ccomand.CommandSet
controller *MyController
}
func NewMyCommandSet(controller *MyController) *MyCommandSet {
c := &MyCommandSet{}
c.controller = controller
c.AddCommand(c.MakeGreeting())
return c
}
func (c *MyCommandSet) MakeGreeting() *ccomand.Command {
return ccomand.NewCommand(
"greetings",
cvalid.NewObjectSchema().WithRequiredProperty("name", cconv.String),
func(ctx context.Context, correlationId string, args *crun.Parameters) (any, error) {
name := args.GetAsString("name")
return c.controller.Greetings(ctx, name)
},
)
}
class MyCommandSet(CommandSet):
_controller: 'MyController'
def __init__(self, controller):
super().__init__()
self._controller = controller
self.add_command(self._make_greeting())
def _make_greeting(self):
def handler(correlation_id: Optional[str], args: Parameters):
name = args.get_as_string("name")
res = self._controller.greeting(name)
return res
return Command(
"greetings",
ObjectSchema(True).with_required_property("name", TypeCode.String),
handler
)
Which connects to the controller via the getCommandSet()
method. Thus, the controller, which contains the business logic of the actions, looks like this:
export class MyController implements IReferenceable, ICommandable {
private commandSet: CommandSet;
public setReferences(references: IReferences): void {
}
public getCommandSet(): CommandSet {
if (this.commandSet == null) {
this.commandSet = new MyCommandSet(this);
}
return this.commandSet;
}
public async greetings(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
public class MyController : IReferenceable, ICommandable
{
private CommandSet commandSet;
public void SetReferences(IReferences references)
{
}
public CommandSet GetCommandSet()
{
if (commandSet == null)
commandSet = new MyCommandSet(this);
return commandSet;
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
type MyController struct {
commandSet *MyCommandSet
}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) GetCommandSet() *MyCommandSet {
if c.commandSet == nil {
c.commandSet = NewMyCommandSet(c)
}
return c.commandSet
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
class MyController(IReferenceable, ICommandable):
def __init__(self):
super().__init__()
self.__command_set = None
def get_command_set(self):
if self.__command_set is None:
self.__command_set = MyCommandSet(self)
return self.__command_set
def set_references(self, references):
pass
def greeting(self, name):
return f"Hello, {name} !"
In this case, the service is of the CommandableCloudService type, because this class contains the necessary functionality to work with the command set.
export class MyCommandableCloudService extends CommandableCloudFunctionService {
public constructor() {
super('mygroup');
// The 'controller' dependency is required by all Commandable services
this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'));
}
}
public class MyCommandableCloudService : CommandableCloudFunctionService
{
public MyCommandableCloudService() : base("mygroup")
{
// The "controller" dependency is required by all Commandable services
_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "1.0"));
}
}
type MyCommandableCloudService struct {
*gcpserv.CommandableCloudFunctionService
}
func NewMyCommandableCloudService() *MyCommandableCloudService {
c := &MyCommandableCloudService{}
c.CommandableCloudFunctionService = gcpserv.NewCommandableCloudFunctionService("mygroup")
// The "controller" dependency is required by all Commandable services
c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"))
return c
}
class MyCommandableCloudService(CommandableCloudFunctionService):
def __init__(self):
super().__init__('mygroup')
# The 'controller' dependency is required by all Commandable services
self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'))
And the factory registers both the controller and the service:
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController);
this.registerAsType(new Descriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), MyCommandableCloudService);
}
}
public class MyFactory: Factory
{
public MyFactory(): base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
RegisterAsType(new Descriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), typeof(MyCommandableCloudService));
}
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
c.RegisterType(cref.NewDescriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), NewMyCommandableCloudService)
return c
}
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
self.register_as_type(Descriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), MyCommandableloudService)
Finally, after grouping everything together, we obtain the following microservice:
import { Command, CommandSet, Descriptor, IReferences, TypeCode, Parameters, ObjectSchema, IReferenceable, ICommandable } from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";
import { CloudFunction, CloudFunctionService, CommandableCloudFunctionService } from "pip-services3-gcp-nodex";
export class MyCommandSet extends CommandSet {
private _controller: MyController;
public constructor(controller: MyController) {
super();
this._controller = controller;
this.addCommand(this.makeGreeting());
}
private makeGreeting(): Command {
return new Command(
"greetings",
new ObjectSchema(true).withRequiredProperty("name", TypeCode.String),
async (correlationId: string, args: Parameters) => {
let name = args.getAsString("name");
return await this._controller.greetings(name);
}
);
}
}
export class MyCommandableCloudService extends CommandableCloudFunctionService {
public constructor() {
super('mygroup');
// The 'controller' dependency is required by all Commandable services
this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'));
}
}
export class MyController implements IReferenceable, ICommandable {
private commandSet: CommandSet;
public setReferences(references: IReferences): void {
}
public getCommandSet(): CommandSet {
if (this.commandSet == null) {
this.commandSet = new MyCommandSet(this);
}
return this.commandSet;
}
public async greetings(name: string): Promise<string> {
return "Hello, " + name + " !";
}
}
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController);
this.registerAsType(new Descriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), MyCommandableCloudService);
}
}
export class MyCloudFunction extends CloudFunction {
private _controller: MyController;
private _service: CloudFunctionService;
public constructor() {
super("mygroup", "Mygroup service");
this._configPath = "./config.yaml";
this._factories.add(new MyFactory());
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
// this._dependencyResolver.put('controller', new Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
// this._dependencyResolver.put('service', new Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'))
}
public setReferences(references: IReferences): void {
super.setReferences(references);
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
// this._controller = this._dependencyResolver.getOneRequired('controller');
// this._service = this._dependencyResolver.getOneRequired('service');
// Comment out the next two lines of code when using the dependency resolver, uncomment for configuration file
this._controller = references.getOneRequired(new Descriptor('mygroup', 'controller', 'default', 'controller', '*'));
this._service = references.getOneRequired(new Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'));
}
}
using PipServices3.Commons.Commands;
using PipServices3.Commons.Convert;
using PipServices3.Commons.Refer;
using PipServices3.Commons.Run;
using PipServices3.Commons.Validate;
using PipServices3.Components.Build;
using PipServices3.Gcp.Containers;
using PipServices3.Gcp.Services;
using System.Threading.Tasks;
namespace ExampleApp
{
public class MyCommandSet : CommandSet
{
private MyController _controller;
public MyCommandSet(MyController controller) : base()
{
_controller = controller;
AddCommand(MakeGreeting());
}
private Command MakeGreeting()
{
return new Command(
"greetings",
new ObjectSchema().WithRequiredProperty("name", TypeCode.String),
async (string correlationId, Parameters args) =>
{
var name = args.GetAsString("name");
return await _controller.GreetingsAsync(name);
}
);
}
}
public class MyCommandableCloudService : CommandableCloudFunctionService
{
public MyCommandableCloudService() : base("mygroup")
{
// The "controller" dependency is required by all Commandable services
_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "1.0"));
}
}
public class MyController : IReferenceable, ICommandable
{
private CommandSet commandSet;
public void SetReferences(IReferences references)
{
}
public CommandSet GetCommandSet()
{
commandSet ??= new MyCommandSet(this);
return commandSet;
}
public async Task<string> GreetingsAsync(string name)
{
return "Hello, " + name + " !";
}
}
public class MyFactory : Factory
{
public MyFactory() : base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller", "1.0"), typeof(MyController));
RegisterAsType(new Descriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), typeof(MyCommandableCloudService));
}
}
public class MyCloudFunction : CloudFunction
{
private MyController _controller;
private CloudFunctionService _service;
public MyCloudFunction() : base("mygroup", "Mygroup service")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
//_dependencyResolver.Put("controller", new Descriptor("mygroup", "controller", "default", "controller", "*"));
//_dependencyResolver.Put("service", new Descriptor("mygroup", "service", "commandable-gcp-function", "function", "*"));
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
//_controller = _dependencyResolver.GetOneRequired<MyController>("controller");
//_service = _dependencyResolver.GetOneRequired<CloudFunctionService>("service");
// Comment out the next two lines of code when using the dependency resolver, uncomment for configuration file
_controller = references.GetOneRequired<MyController>("controller");
_service = references.GetOneRequired<CloudFunctionService>("service");
}
}
}
import (
"context"
"net/http"
ccomand "github.com/pip-services3-gox/pip-services3-commons-gox/commands"
cconv "github.com/pip-services3-gox/pip-services3-commons-gox/convert"
cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
crun "github.com/pip-services3-gox/pip-services3-commons-gox/run"
cvalid "github.com/pip-services3-gox/pip-services3-commons-gox/validate"
cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
gcpcont "github.com/pip-services3-gox/pip-services3-gcp-gox/containers"
gcpserv "github.com/pip-services3-gox/pip-services3-gcp-gox/services"
gcputil "github.com/pip-services3-gox/pip-services3-gcp-gox/utils"
rpcserv "github.com/pip-services3-gox/pip-services3-rpc-gox/services"
)
type MyCommandSet struct {
*ccomand.CommandSet
controller *MyController
}
func NewMyCommandSet(controller *MyController) *MyCommandSet {
c := &MyCommandSet{}
c.controller = controller
c.AddCommand(c.MakeGreeting())
return c
}
func (c *MyCommandSet) MakeGreeting() *ccomand.Command {
return ccomand.NewCommand(
"greetings",
cvalid.NewObjectSchema().WithRequiredProperty("name", cconv.String),
func(ctx context.Context, correlationId string, args *crun.Parameters) (any, error) {
name := args.GetAsString("name")
return c.controller.Greetings(ctx, name)
},
)
}
type MyCommandableCloudService struct {
*gcpserv.CommandableCloudFunctionService
}
func NewMyCommandableCloudService() *MyCommandableCloudService {
c := &MyCommandableCloudService{}
c.CommandableCloudFunctionService = gcpserv.NewCommandableCloudFunctionService("mygroup")
// The "controller" dependency is required by all Commandable services
c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"))
return c
}
type MyController struct {
commandSet *MyCommandSet
}
func NewMyController() *MyController {
return &MyController{}
}
func (c *MyController) GetCommandSet() *MyCommandSet {
if c.commandSet == nil {
c.commandSet = NewMyCommandSet(c)
}
return c.commandSet
}
func (c *MyController) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController) Greetings(ctx context.Context, name string) (string, error) {
return "Hello, " + name + " !", nil
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller", "1.0"), NewMyController)
c.RegisterType(cref.NewDescriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), NewMyCommandableCloudService)
return c
}
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController
service *gcpserv.CloudFunctionService
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup service")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
// Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
// c.DependencyResolver.Put(context.Background(), "controller", cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
// c.DependencyResolver.Put(context.Background(), "service", cref.NewDescriptor("mygroup", "service", "commandable-gcp-function", "function", "*"))
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// ctrl, err := c.DependencyResolver.GetOneRequired("controller")
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
ctrl, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "default", "controller", "*"))
if err != nil {
panic("Controller is not found")
}
// Comment out the next line of code when using a configuration file, uncomment to use the dependency resolver
// serv, err := c.DependencyResolver.GetOneRequired("service")
// Comment out the next line of code when using the dependency resolver, uncomment for configuration file
serv, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "service", "commandable-gcp-function", "function", "*"))
if err != nil {
panic("Service is not found")
}
c.controller = ctrl.(*MyController)
c.service = serv.(*gcpserv.CloudFunctionService)
}
from typing import Optional
from pip_services3_gcp.containers import CloudFunction
from pip_services3_gcp.services import CommandableCloudFunctionService
from pip_services3_commons.refer import Descriptor, IReferences, IReferenceable
from pip_services3_components.build import Factory
from pip_services3_commons.commands import Command, CommandSet, ICommandable
from pip_services3_commons.convert import TypeCode
from pip_services3_commons.run import Parameters
from pip_services3_commons.validate import ObjectSchema
class MyCommandSet(CommandSet):
_controller: 'MyController'
def __init__(self, controller):
super().__init__()
self._controller = controller
self.add_command(self._make_greeting())
def _make_greeting(self):
def handler(correlation_id: Optional[str], args: Parameters):
name = args.get_as_string("name")
res = self._controller.greeting(name)
return res
return Command(
"greetings",
ObjectSchema(True).with_required_property("name", TypeCode.String),
handler
)
class MyCommandableCloudService(CommandableCloudFunctionService):
def __init__(self):
super().__init__('mygroup')
# The ‘controller’ dependency is required by all Commandable services
self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '1.0'))
class MyController(IReferenceable, ICommandable):
def __init__(self):
super().__init__()
self.__command_set = None
def get_command_set(self):
if self.__command_set is None:
self.__command_set = MyCommandSet(self)
return self.__command_set
def set_references(self, references):
pass
def greeting(self, name):
return f"Hello, {name} !"
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)
self.register_as_type(Descriptor("mygroup", "service", "commandable-gcp-function", "function", "1.0"), MyCommandableCloudService)
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "Mygroup service")
self._controller: MyController = None
self._service: MyCloudFunction = None
self._config_path = './config.yaml'
self._factories.add(MyFactory())
# Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
# self._dependency_resolver.put('controller', Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
# self._dependency_resolver.put('service', Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'))
def set_references(self, references: IReferences):
super().set_references(references)
# Comment out the next two lines of code when using a configuration file, uncomment to use the dependency resolver
# self._controller = self._dependency_resolver.get_one_required('controller')
# self._service = self._dependency_resolver.get_one_required('service')
# Comment out the next two lines of code when using the dependency resolver, uncomment for configuration file
self._controller = references.get_one_required(Descriptor('mygroup', 'controller', 'default', 'controller', '*'))
self._service = references.get_one_required(Descriptor('mygroup', 'service', 'commandable-gcp-function', 'function', '*'))
Combining the basic examples
The former four cases can be combined and expanded in many different ways, providing solutions for complex business scenarios.
The next sections will examine two of such examples. The first considers a microservice with two controllers and actions registered in the container. The second adds a service, links it to one of the controllers, registers some of the actions in the service, and then registers the rest in the container.
Example 1
This example extends the first of the previously explained cases by adding a second controller. Its main characteristics are:
- The container registers two different actions, namely, greetings1() and greetings2().
- Each controller contains the business logic for only one of these actions.
- The controllers are added as dependencies of the container.
The following diagram and code show what this example looks like:
import { Descriptor, IReferenceable, IReferences } from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";
import { CloudFunction, CloudFunctionRequestHelper } from "pip-services3-gcp-nodex";
import { HttpResponseSender } from "pip-services3-rpc-nodex";
export class MyController1 implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greeting1(name: string): Promise<string> {
return "greetings1: Hello, " + name + " !";
}
}
export class MyController2 implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greeting2(name: string): Promise<string> {
return "greetings2: Hello, " + name + " !";
}
}
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller1", "1.0"), MyController1);
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller2", "1.0"), MyController2);
}
}
export class MyCloudFunction extends CloudFunction {
private _controller1: MyController1;
private _controller2: MyController2;
public constructor() {
super("mygroup", "MyGroup");
this._configPath = './config.yaml';
this._dependencyResolver.put('controller1', new Descriptor('mygroup', 'controller', 'default', 'controller1', '*'))
this._dependencyResolver.put('controller2', new Descriptor('mygroup', 'controller', 'default', 'controller2', '*'))
this._factories.add(new MyFactory())
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller1 = this._dependencyResolver.getOneRequired('controller1');
this._controller2 = this._dependencyResolver.getOneRequired('controller2');
}
private async action1(req: Request, res: Response): Promise<void> {
let params = CloudFunctionRequestHelper.getParameters(req);
let name = params.getAsStringWithDefault('name', 'default name');
let result = await this._controller1.greeting1(name);
HttpResponseSender.sendResult(req, res, result);
}
private async action2(req: Request, res: Response): Promise<void> {
let params = CloudFunctionRequestHelper.getParameters(req);
let name = params.getAsStringWithDefault('name', 'default name');
let result = await this._controller2.greeting2(name);
HttpResponseSender.sendResult(req, res, result);
}
protected register(): void {
this.registerAction("greetings1", null, this.action1);
this.registerAction("greetings2", null, this.action2);
}
}
using Microsoft.AspNetCore.Http;
using PipServices3.Commons.Refer;
using PipServices3.Components.Build;
using PipServices3.Gcp.Containers;
using PipServices3.Rpc.Services;
using System.Threading.Tasks;
namespace ExampleApp
{
public class MyController1 : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync1(string name)
{
return "greetings1: Hello, " + name + " !";
}
}
public class MyController2 : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync2(string name)
{
return "greetings2: Hello, " + name + " !";
}
}
public class MyFactory : Factory
{
public MyFactory() : base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller1", "1.0"), typeof(MyController1));
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller2", "1.0"), typeof(MyController2));
}
}
public class MyCloudFunction : CloudFunction
{
private MyController1 _controller1;
private MyController2 _controller2;
public MyCloudFunction() : base("mygroup", "Mygroup service")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
_dependencyResolver.Put("controller1", new Descriptor("mygroup", "controller", "default", "controller1", "*"));
_dependencyResolver.Put("controller2", new Descriptor("mygroup", "controller", "default", "controller2", "*"));
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller1 = references.GetOneRequired<MyController1>("controller1");
_controller2 = references.GetOneRequired<MyController2>("controller2");
}
private async Task Action1(HttpContext context)
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller1.GreetingsAsync1(name);
await HttpResponseSender.SendResultAsync(context.Response, result);
}
private async Task Action2(HttpContext context)
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller2.GreetingsAsync2(name);
await HttpResponseSender.SendResultAsync(context.Response, result);
}
protected override void Register()
{
RegisterAction("greetings1", null, Action1);
RegisterAction("greetings2", null, Action2);
}
}
}
import (
"context"
"net/http"
cconv "github.com/pip-services3-gox/pip-services3-commons-gox/convert"
cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
cvalid "github.com/pip-services3-gox/pip-services3-commons-gox/validate"
cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
gcpcont "github.com/pip-services3-gox/pip-services3-gcp-gox/containers"
gcpserv "github.com/pip-services3-gox/pip-services3-gcp-gox/services"
gcputil "github.com/pip-services3-gox/pip-services3-gcp-gox/utils"
rpcserv "github.com/pip-services3-gox/pip-services3-rpc-gox/services"
)
type MyController1 struct {
}
func NewMyController1() *MyController1 {
return &MyController1{}
}
func (c *MyController1) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController1) Greetings1(ctx context.Context, name string) (string, error) {
return "greetings1: Hello, " + name + " !", nil
}
type MyController2 struct {
}
func NewMyController2() *MyController2 {
return &MyController2{}
}
func (c *MyController2) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController2) Greetings2(ctx context.Context, name string) (string, error) {
return "greetings2: Hello, " + name + " !", nil
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller1", "1.0"), NewMyController1)
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller2", "1.0"), NewMyController2)
return c
}
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller1 *MyController1
controller2 *MyController2
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
c.DependencyResolver.Put(context.Background(), "controller1", cref.NewDescriptor("mygroup", "controller", "default", "controller1", "*"))
c.DependencyResolver.Put(context.Background(), "controller2", cref.NewDescriptor("mygroup", "controller", "default", "controller2", "*"))
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
ctrl1, err := c.DependencyResolver.GetOneRequired("controller1")
if err != nil {
panic("Controller1 is not found")
}
ctrl2, err := c.DependencyResolver.GetOneRequired("controller2")
if err != nil {
panic("Controller2 is not found")
}
c.controller1 = ctrl1.(*MyController1)
c.controller2 = ctrl2.(*MyController2)
}
func (c *MyCloudFunction) Action1(res http.ResponseWriter, req *http.Request) {
params := gcputil.CloudFunctionRequestHelper.GetParameters(req)
name := params.GetAsStringWithDefault("name", "default name")
result, err := c.controller1.Greetings1(req.Context(), name)
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
}
func (c *MyCloudFunction) Action2(res http.ResponseWriter, req *http.Request) {
params := gcputil.CloudFunctionRequestHelper.GetParameters(req)
name := params.GetAsStringWithDefault("name", "default name")
result, err := c.controller2.Greetings2(req.Context(), name)
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
}
func (c *MyCloudFunction) Register() {
c.RegisterAction("greetings1", nil, c.Action1)
c.RegisterAction("greetings2", nil, c.Action2)
}
import flask
from pip_services3_commons.refer import IReferences, IReferenceable
from pip_services3_commons.refer import Descriptor
from pip_services3_components.build import Factory
from pip_services3_gcp.containers import CloudFunction
class MyController1(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greetings1(self, name: str):
return f"greetings1: Hello, {name}"
class MyController2(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greetings2(self, name: str):
return f"greetings2: Hello, {name}"
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller1", "1.0"), MyController1)
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller2", "1.0"), MyController2)
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._config_path = './config.yaml'
self._dependency_resolver.put('controller1', Descriptor('mygroup', 'controller', 'default', 'controller1', '*'))
self._dependency_resolver.put('controller2', Descriptor('mygroup', 'controller', 'default', 'controller2', '*'))
self._factories.add(MyFactory())
self._controller1: MyController1 = None
self._controller2: MyController2 = None
def set_references(self, references: IReferences):
super().set_references(references)
self._controller1 = self._dependency_resolver.get_one_required('controller1')
self._controller2 = self._dependency_resolver.get_one_required('controller2')
def __action1(self, req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get("name", name)
return self._controller1.greetings1(name), 200
def __action2(self, req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get("name", name)
return self._controller2.greetings2(name), 200
def register(self):
self._register_action("greetings1", None, self.__action1)
self._register_action("greetings2", None, self.__action2)
Example 2
This example extends the previous one by adding a service layer linked to one of the controllers. As such, it combines two basic cases: a microservice without a service layer and a microservice with a service layer. It has the following characteristics:
- Actions linked to the first controller are registered in the service.
- Actions linked to the second controller are registered in the container.
- The controllers and the service are created via the factory using information stored in the configuration file.
The following class diagram and code show what this example looks like:
import { Descriptor, IReferenceable, IReferences } from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";
import { CloudFunction, CloudFunctionRequestHelper, CloudFunctionService } from "pip-services3-gcp-nodex";
import { HttpResponseSender } from "pip-services3-rpc-nodex";
export class MyCloudFunctionService extends CloudFunctionService {
private _controller: MyController1;
private _headers = {
'Content-Type': 'application/json'
};
public constructor() {
super("myservice");
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = references.getOneRequired(new Descriptor("mygroup", "controller", "*", "controller1", "1.0"));
}
private async action(req: Request, res: Response): Promise<void> {
let params = CloudFunctionRequestHelper.getParameters(req);
let name = params.getAsStringWithDefault('name', 'default name');
let result = await this._controller.greeting1(name);
HttpResponseSender.sendResult(req, res, result);
}
protected register(): void {
this.registerAction("greetings1", null, this.action);
}
}
export class MyController1 implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greeting1(name: string): Promise<string> {
return "Greetings from service: Hello, " + name + " !";
}
}
export class MyController2 implements IReferenceable {
public setReferences(references: IReferences): void {
}
public async greeting2(name: string): Promise<string> {
return "Greetings from container: Hello, " + name + " !";
}
}
export class MyFactory extends Factory {
public constructor() {
super();
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller1", "1.0"), MyController1);
this.registerAsType(new Descriptor("mygroup", "controller", "default", "controller2", "1.0"), MyController2);
this.registerAsType(new Descriptor("mygroup", "service", "gcp-function", "*", "1.0"), MyCloudFunctionService)
}
}
export class MyCloudFunction extends CloudFunction {
private _controller: MyController2;
public constructor() {
super("mygroup", "Mygroup service");
this._configPath = "./config.yaml";
this._factories.add(new MyFactory());
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._controller = references.getOneRequired(new Descriptor("mygroup", "controller", "*", "controller2", "1.0"));
}
private async action(req: Request, res: Response): Promise<void> {
let params = CloudFunctionRequestHelper.getParameters(req);
let name = params.getAsStringWithDefault('name', 'default name');
let result = await this._controller.greeting2(name);
HttpResponseSender.sendResult(req, res, result);
}
protected register(): void {
this.registerAction('greetings2', null, this.action);
}
}
using Microsoft.AspNetCore.Http;
using PipServices3.Commons.Refer;
using PipServices3.Components.Build;
using PipServices3.Gcp.Containers;
using PipServices3.Gcp.Services;
using PipServices3.Rpc.Services;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ExampleApp
{
public class MyCloudFunctionService : CloudFunctionService
{
private MyController1 _controller;
private IDictionary<string, string> _headers = new Dictionary<string, string>() { { "Content-Type", "application/json" } };
public MyCloudFunctionService() : base("myservice") { }
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = references.GetOneRequired<MyController1>(new Descriptor("mygroup", "controller", "*", "controller1", "1.0"));
}
private async Task Action(HttpContext context)
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller.GreetingsAsync1(name);
await HttpResponseSender.SendResultAsync(context.Response, result);
}
protected override void Register()
{
RegisterAction("greetings1", null, Action);
}
}
public class MyController1 : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync1(string name)
{
return "Greetings from service: Hello, " + name + " !";
}
}
public class MyController2 : IReferenceable
{
public void SetReferences(IReferences references)
{
}
public async Task<string> GreetingsAsync2(string name)
{
return "Greetings from container: Hello, " + name + " !";
}
}
public class MyFactory : Factory
{
public MyFactory() : base()
{
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller1", "1.0"), typeof(MyController1));
RegisterAsType(new Descriptor("mygroup", "controller", "default", "controller2", "1.0"), typeof(MyController2));
RegisterAsType(new Descriptor("mygroup", "service", "gcp-function", "*", "1.0"), typeof(MyCloudFunctionService))
}
}
public class MyCloudFunction : CloudFunction
{
private MyController2 _controller;
public MyCloudFunction() : base("mygroup", "Mygroup service")
{
_configPath = "./config.yaml";
_factories.Add(new MyFactory());
}
public override void SetReferences(IReferences references)
{
base.SetReferences(references);
_controller = references.GetOneRequired<MyController2>(new Descriptor("mygroup", "controller", "*", "controller2", "1.0"));
}
private async Task Action(HttpContext context)
{
var parameters = await CloudFunctionRequestHelper.GetParametersAsync(context);
var name = parameters.GetAsStringWithDefault("name", "default name");
var result = await _controller.GreetingsAsync2(name);
await HttpResponseSender.SendResultAsync(context.Response, result);
}
protected override void Register()
{
RegisterAction("greetings2", null, Action);
}
}
}
import (
"context"
"net/http"
cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
cbuild "github.com/pip-services3-gox/pip-services3-components-gox/build"
gcpcont "github.com/pip-services3-gox/pip-services3-gcp-gox/containers"
gcpserv "github.com/pip-services3-gox/pip-services3-gcp-gox/services"
gcputil "github.com/pip-services3-gox/pip-services3-gcp-gox/utils"
rpcserv "github.com/pip-services3-gox/pip-services3-rpc-gox/services"
)
type MyCloudFunctionService struct {
*gcpserv.CloudFunctionService
controller *MyController1
headers map[string]string
}
func NewMyCloudFunctionService() *MyCloudFunctionService {
c := &MyCloudFunctionService{}
c.CloudFunctionService = gcpserv.NewCloudFunctionService("myservice")
return c
}
func (c *MyCloudFunctionService) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunctionService.SetReferences(ctx, references)
res, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "*", "controller1", "1.0"))
if err != nil {
panic("Controller is not found")
}
c.controller = res.(*MyController1)
}
func (c *MyCloudFunctionService) Action(res http.ResponseWriter, req *http.Request) {
params := gcputil.CloudFunctionRequestHelper.GetParameters(req)
name := params.GetAsStringWithDefault("name", "default name")
result, err := c.controller.Greetings1(req.Context(), name)
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
}
func (c *MyCloudFunctionService) Register() {
c.RegisterAction("greetings1", nil, c.Action)
}
type MyController1 struct {
}
func NewMyController1() *MyController1 {
return &MyController1{}
}
func (c *MyController1) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController1) Greetings1(ctx context.Context, name string) (string, error) {
return "Greetings from service: Hello, " + name + " !", nil
}
type MyController2 struct {
}
func NewMyController2() *MyController2 {
return &MyController2{}
}
func (c *MyController2) SetReferences(ctx context.Context, references cref.IReferences) {
}
func (c *MyController2) Greetings2(ctx context.Context, name string) (string, error) {
return "Greetings from container: Hello, " + name + " !", nil
}
type MyFactory struct {
*cbuild.Factory
}
func NewMyFactory() *MyFactory {
c := &MyFactory{}
c.Factory = cbuild.NewFactory()
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller1", "1.0"), NewMyController1)
c.RegisterType(cref.NewDescriptor("mygroup", "controller", "default", "controller2", "1.0"), NewMyController2)
c.RegisterType(cref.NewDescriptor("mygroup", "service", "gcp-function", "*", "1.0"), NewMyCloudFunctionService)
return c
}
type MyCloudFunction struct {
*gcpcont.CloudFunction
controller *MyController2
}
func NewMyCloudFunction() *MyCloudFunction {
c := &MyCloudFunction{}
c.CloudFunction = gcpcont.InheritCloudFunctionWithParams(c, "mygroup", "Mygroup")
c.SetConfigPath("./config.yaml")
c.AddFactory(NewMyFactory())
return c
}
func (c *MyCloudFunction) SetReferences(ctx context.Context, references cref.IReferences) {
c.CloudFunction.SetReferences(ctx, references)
c.Counters.SetReferences(ctx, references)
ctrl, err := references.GetOneRequired(cref.NewDescriptor("mygroup", "controller", "*", "controller2", "1.0"))
if err != nil {
panic("Controller2 is not found")
}
c.controller = ctrl.(*MyController2)
}
func (c *MyCloudFunction) Action(res http.ResponseWriter, req *http.Request) {
params := gcputil.CloudFunctionRequestHelper.GetParameters(req)
name := params.GetAsStringWithDefault("name", "default name")
result, err := c.controller.Greetings2(req.Context(), name)
rpcserv.HttpResponseSender.SendResult(res, req, result, err)
}
func (c *MyCloudFunction) Register() {
c.RegisterAction("greetings2", nil, c.Action)
}
import flask
from pip_services3_gcp.services.CloudFunctionService import CloudFunctionService
from pip_services3_commons.refer import IReferences, IReferenceable
from pip_services3_commons.refer import Descriptor
from pip_services3_components.build import Factory
from pip_services3_gcp.containers import CloudFunction
class MyCloudFunctionService(CloudFunctionService):
def __init__(self):
super().__init__('myservice')
self._controller: MyController1 = None
self._headers = {
'Content-Type': 'application/json'
}
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = references.get_one_required(Descriptor("mygroup", "controller", "*", "controller1", "1.0"))
def __action(self, req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get("name", name)
return self._controller.greetings1(name), 200
def register(self):
self._register_action("greetings1", None, self.__action)
class MyController1(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greetings1(self, name: str):
return f"Greetings from service: Hello, {name}"
class MyController2(IReferenceable):
def __init__(self):
super().__init__()
def set_references(self, references):
pass
def greetings2(self, name: str):
return f"Greetings from container: Hello, {name}"
class MyFactory(Factory):
def __init__(self):
super().__init__()
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller1", "1.0"), MyController1)
self.register_as_type(Descriptor("mygroup", "controller", "default", "controller2", "1.0"), MyController2)
self.register_as_type(Descriptor("mygroup", "service", "gcp-function", "*", "1.0"), MyCloudFunctionService)
class MyCloudFunction(CloudFunction):
def __init__(self):
super().__init__("mygroup", "MyGroup")
self._config_path = './config.yaml'
self._factories.add(MyFactory())
self._controller = None
def set_references(self, references: IReferences):
super().set_references(references)
self._controller = references.get_one_required(Descriptor("mygroup", "controller", "*", "controller2", "1.0"))
def __action(self, req: flask.Request):
name = 'default name'
if req.is_json:
name = req.json.get("name", name)
return self._controller.greetings2(name), 200
def register(self):
self._register_action("greetings2", None, self.__action)
Where the configuration file is:
# Console logger
- descriptor: "pip-services:logger:console:default:1.0"
level: "trace"
# Service
- descriptor: "mygroup:service:gcp-function:*:1.0"
# Controller 1
- descriptor: "mygroup:controller:default:controller1:1.0"
# Controller 2
- descriptor: "mygroup:controller:default:controller2:1.0"
Testing
After running an application with the help of a tool for example like Functions Framework for Python, the results can be seen by sending a test request to the container via HTTP. The following steps explain how this can be done:
Step 1: Create an instance of the container
function_service = MyCloudFunction()
Step 2: Create an instance of the handler
handler = function_service.get_handler()
Step 3: Run the tool
To see the results on a local machine run:
functions-framework --target handler --signature-type http --port 8080 --source program_name.py
where program_name.py is the name of the file containing the GCP function service.
Step 4: Call an action.
A specific action, such as ‘greetings’ in our previous examples, can be called by running a command similar to the following one:
curl -d '{"cmd": ""myservice.greetings1", "name": "Bob"}' -H "Content-Type: application/json" -X POST http://localhost:8080
After running the previous cURL command, the result will be displayed as part of the response that is received.
Alternatively, a REST Client can be used to achieve the same result:
Wrapping up
In this tutorial, we learned how to create a microservice, packaged in a Google Cloud Platform container. We saw four different scenarios with code examples. The following table summarizes these cases:
Without a service layer | With a service layer | |
---|---|---|
Manually registering the action |
|
|
Using a CommandSet component |
|
|