Google Cloud Platform

How to use the toolkit’s GCP containers and services.

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:

  1. GCP containers and services: A basic explanation of the different containers and services available in the GCP module.
  2. Basic microservices: An explanation of how to build basic microservices using GCP containers and services.
  3. Combining basic microservices: An explanation of how to further expand the basic microservice examples, providing solutions for complex business scenarios.
  4. Testing: An explanation of how to test GCP-based microservices.
  5. 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:

figure 1

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:

figure 2

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:

  1. It registers the actions in the container via the register() method.
  2. The business logic of the actions is defined in the controller.
  3. 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:

figure 3

And the second is by registering the controller as a dependency for the container:

figure 4

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)
}

Not available
# 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)

Not available

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)
}

Not available
# 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)

Not available

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
}

Not available
class MyController(IReferenceable):
    def __init__(self):
        super().__init__()

    def set_references(self, references):
        pass
    
    def greetings(self, name: str):
        return f"Hello, {name} !"


Not available

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
}

Not available
class MyFactory(Factory):
    
    def __init__(self):
        super().__init__()
        self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)

Not available

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)
}
Not available
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)



Not available

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:

  1. The actions are defined in the command set.
  2. The controller links to the CommandSet via the getCommandSet() method.
  3. The business logic of the actions is still defined in the controller.
  4. The container uses a factory to create the controller.

As in the previous case, the container can create the controller via a configuration file:

figure 5

or by adding it as a dependency:

figure 6

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)
}

Not available
# 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', '*'))

Not available

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)
}

Not available
# 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')



Not available

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
}

Not available
class MyFactory(Factory):
    
    def __init__(self):
        super().__init__()
        self.register_as_type(Descriptor("mygroup", "controller", "default", "controller", "1.0"), MyController)


Not available

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
}
Not available
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} !"
Not available

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)
		},
	)
}

Not available
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
        )

Not available

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)
}

Not available
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', '*'))  

Not available

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:

  1. Actions are registered in the service.
  2. The business logic of the actions is defined in the controller.
  3. 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:

figure 7

or by defining them as the container’s dependencies:

figure 8

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)
}

Not available
# 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', '*'))


Not available

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)
}

Not available
# 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')

Not available

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
}
Not available
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)
 
Not available

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)
		},
	)
}

Not available
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
        )

Not available

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
}

Not available
class MyController(IReferenceable):

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

    def set_references(self, references):
        pass

    def greeting(self, name):
        return f"Hello, {name} !"

Not available

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
	
Not available
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', '*'))


Not available

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:

  1. The service is of the CommandableCloudService type, which has the added functionality of automatically generating the necessary operations for commands defined in ICommandable components.
  2. The controller links to the CommandSet via the getCommandSet() method.
  3. The required business logic of the actions is still defined in the controller.
  4. 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:

figure 9

or by defining them as container dependencies:

figure 10

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)
}
Not available
# 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', '*'))
Not available

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)
}
Not available
# 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')

Not available

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)
		},
	)
}
Not available
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
        )

Not available

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
}

Not available
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} !"

Not available

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
}

Not available
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'))

Not available

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
}
Not available
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)

Not available

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)
}

Not available
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', '*'))

Not available

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:

  1. The container registers two different actions, namely, greetings1() and greetings2().
  2. Each controller contains the business logic for only one of these actions.
  3. The controllers are added as dependencies of the container.

The following diagram and code show what this example looks like:

figure 11

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)
}
Not available
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)

Not available

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:

  1. Actions linked to the first controller are registered in the service.
  2. Actions linked to the second controller are registered in the container.
  3. 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:

figure 12

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)
}

Not available
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)

Not available

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.

figure 13

Alternatively, a REST Client can be used to achieve the same result:

figure 14

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
  • Actions are registered in the container.
  • The controller is created via a factory using information from a configuration file or by defining it as the container’s dependency.
  • Actions are registered in the service.
  • The controller and service are created via a factory using information from a configuration file or by defining them as the container’s dependencies.
Using a CommandSet component
  • A CommandSet component defines the actions.
  • The controller adds the getCommandSet() method and implements the ICommandable interface.
  • The business logic is added to the actions in the controller.
  • A commandable version of the container is used.
  • The controller is created via a factory using information from a configuration file or by defining it as the container’s dependency.
  • A CommandSet component defines the actions.
  • The controller adds the getCommandSet() method and implements the ICommandable interface.
  • The business logic is added to the actions in the controller.
  • A commandable version of the service is used.
  • The controller and service are created via a factory using information from a configuration file or by defining them as the container’s dependencies.