Descriptors

How to locate a component.

Key takeaways

Descriptor A component's locator, which is defined by its group, type, kind, name, and version.
Scenarios Descriptors can define many scenarios such as, for example, a specific component or all persistence components for a certain microservice.

Introduction

This tutorial will help you understand what a descriptor is, how to create one, how to get its properties, how to check its completeness, how to convert it to a string, and how to compare it to other descriptors. Finally, it provides an example of its usage.

What is a Descriptor?

Within the Pip.Services toolkit, a descriptor is a component’s locator that is based on the component’s group, type, kind, name, and version. The Descriptor class is part of the Commons module and is included in the Refer library. The figure below summarizes the different elements of a descriptor.

figure 1

Using Descriptors

a) Pre-requisites

In order to create and use a descriptor, we first need to import this component. This can be done with the following command:

import { Descriptor } from "pip-services4-components-node";
Not available
import (
    refer "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
)
Not available
from pip_services4_components.refer import Descriptor
Not available

b) Creating a descriptor

Descriptors can be created by defining an instance of the Descriptor class. The constructor of this class presents the following structure:

Descriptor(group, type, kind, name, version),

where each field can contain a specific value, *, or None.

Based on this syntax, descriptors allow for the implementation of many different scenarios. Some examples are:

import { Descriptor } from "pip-services4-components-node";

export function main() {
  // Locate all connectors(match by type)
  let locator = Descriptor.fromString("*:connector:*:*:*");

  // Locate all connectors for a specific microservice(match by group and type)
  locator = Descriptor.fromString("mygroup:connector:*:*:*"); 

  // Locate a specific component(match by name)
  locator = Descriptor.fromString("*:*:*:my_name:*");
}
Not available
import (
	refer "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
)

func main() {
	// Locate all connectors (match by type)
	locator, _ := refer.ParseDescriptorFromString("*:connector:*:*:*")

	// Locate all connectors for a specific microservice (match by group and type)
	locator, _ = refer.ParseDescriptorFromString("mygroup:connector:*:*:*")

	// Locate a specific component (match by name)
	locator, _ = refer.ParseDescriptorFromString("*:*:*:my_name:*")
}
Not available
# Locate all connectors (match by type)
locator = Descriptor.from_string("*:connector:*:*:*") 

# Locate all connectors for a specific microservice (match by group and type)
locator = Descriptor.from_string("mygroup:connector:*:*:*") 

# Locate a specific component (match by name)
locator = Descriptor.from_string("*:*:*:my_name:*") 

Not available

c) Getting its properties

The values of an instance of a descriptor can be obtained via get_xxx() methods, where xxx stands for group, kind, name, or version respectively. The example below shows how to use each of them.

import { Descriptor } from "pip-services4-components-node";

export function main() {
  let locator = new Descriptor("mygroup", "connector", "aws", "default", "1.0");

  console.log(locator.getGroup());   // returns "my_group"
  console.log(locator.getKind());    // returns "aws"
  console.log(locator.getName());    // returns "default"
  console.log(locator.getVersion()); // returns "1.0"
}
Not available
import (
	"fmt"

	refer "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
)

func main() {
	locator := refer.NewDescriptor("mygroup", "connector", "aws", "default", "1.0")

	fmt.Println(locator.Group())   // returns "my_group"
	fmt.Println(locator.Kind())    // returns "aws"
	fmt.Println(locator.Name())    // returns "default"
	fmt.Println(locator.Version()) // returns "1.0"
}
Not available
locator = Descriptor("mygroup", "connector", "aws", "default", "1.0")

locator.get_group()   # returns "my_group"
locator.get_kind()    # returns "aws"
locator.get_name()    # returns "default"
locator.get_version() # returns "1.0"

Not available
d) Converting to string

A string version of a descriptor can be obtained via the to_string() method. The following example explains it.

import { Descriptor } from "pip-services4-components-node";

export function main() {
  let locator1 = new Descriptor("mygroup", "connector", "aws", "default", "1.0");
  console.log(locator1.toString());
}
Not available
import (
	refer "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
)

func main() {
	locator1 := refer.NewDescriptor("mygroup", "connector", "aws", "default", "1.0")
	locator1.String()
}
Not available
locator1 = Descriptor("mygroup", "connector", "aws", "default", "1.0")
locator1.to_string()

Not available
figure 2

e) Checking its completeness

A descriptor is complete when all its fields have a specific value, that is, a value different from * or None. The method is_complete() can be used to check the completeness of a descriptor. It returns true if the descriptor is complete and false otherwise. The example below explains its usage.

import { Descriptor } from "pip-services4-components-node";

export function main() {
  let locator1 = new Descriptor("mygroup", "connector", "aws", "default", "1.0");
  let locator2 = Descriptor.fromString("mygroup:connector:*:*:1.0");

  locator1.isComplete();   // returns True
  locator2.isComplete();   // returns False
}
Not available
import (
	refer "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
)

func main() {
	locator1 := refer.NewDescriptor("mygroup", "connector", "aws", "default", "1.0")
	locator2, _ := refer.ParseDescriptorFromString("mygroup:connector:*:*:1.0")

	locator1.IsComplete() // returns True
	locator2.IsComplete() // returns False
}
Not available
locator1 = Descriptor("mygroup", "connector", "aws", "default", "1.0")
locator2 = Descriptor.from_string("mygroup:connector:*:*:1.0")

locator1.is_complete()   # returns True
locator2.is_complete()   # returns False

Not available

f) Comparing descriptors

There are three matching methods namely match, exact_match and equals. The first partially matches a descriptor to another descriptor. If any field contains * or None, this field will be excluded from the match. The second method matches two descriptors by all fields. Finally, the equals() method compares a descriptor to a value. If the value is a Descriptor, it tries to match them. Otherwise, the method returns false. The following example shows how to use them.

import { Descriptor } from "pip-services4-components-node";

export function main() {
  let locator1 = new Descriptor("mygroup", "connector", "aws", "default", "1.0");
  let locator2 = Descriptor.fromString("mygroup:connector:*:*:1.0");
  let locator3 = Descriptor.fromString("mygroup:connector:aws:default:1.0");

  locator1.match(locator2);       // returns True
  locator1.exactMatch(locator2);  // returns False
  locator1.equals(locator3);      // returns True
}
Not available
import (
	refer "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
)

func main() {
	locator1 := refer.NewDescriptor("mygroup", "connector", "aws", "default", "1.0")
	locator2, _ := refer.ParseDescriptorFromString("mygroup:connector:*:*:1.0")
	locator3, _ := refer.ParseDescriptorFromString("mygroup:connector:aws:default:1.0")

	locator1.Match(locator2)      // returns True
	locator1.ExactMatch(locator2) // returns False
	locator1.Equals(locator3)     // returns True
}
Not available
locator1 = Descriptor("mygroup", "connector", "aws", "default", "1.0")
locator2 = Descriptor.from_string("mygroup:connector:*:*:1.0")
locator3 = Descriptor.from_string("mygroup:connector:aws:default:1.0")

locator1.match(locator2)        # returns True
locator1.exact_match(locator2)  # returns False
locator1.equals(locator3)       # returns True
Not available

Example of usage

Now that we know how to handle a descriptor, we will see an example of its application.

Adding a component to a factory

Factories are used to automate component creation. They work by registering components based on their descriptors. In the following example, we first define a custom component, then we register it in a factory previously created, and finally we create an instance of the class. The code is:

import { Descriptor, Factory } from "pip-services4-components-node";

export function main() {
  let MyFactory1 = new Factory();
  let classADescriptor = new Descriptor("mygroup", "class", "classA", "classA", "1.0");

  MyFactory1.registerAsType(classADescriptor, ClassA);

  MyFactory1.create(classADescriptor);
}

class ClassA{
  public constructor() {
      console.log("ClassA created");
  }
}
Not available
import (
	"fmt"

	build "github.com/pip-services4/pip-services4-go/pip-services4-components-go/build"
	refer "github.com/pip-services4/pip-services4-go/pip-services4-components-go/refer"
)

type ComponentA struct{}

func NewComponentA() *ComponentA {
	defer fmt.Println("Created ComponentA")
	return &ComponentA{}
}

func main() {
	MyFactory1 := build.NewFactory()

	myComponentADescriptor := refer.NewDescriptor("mygroup", "class", "classA", "classA", "1.0")

	MyFactory1.RegisterType(myComponentADescriptor, NewComponentA)

	MyFactory1.Create(myComponentADescriptor)
}
Not available
from pip_services4_components.build import Factory 
from pip_services4_components.refer import Descriptor

MyFactory1 = Factory()

class ClassA:   
    
    def __init__(self):
        print("classA created")

classA_descriptor = Descriptor("mygroup", "class", "classA", "classA", "1.0")

MyFactory1.register_as_type(classA_descriptor, ClassA)

MyFactory1.create(classA_descriptor)
Not available
figure 3

Wrapping up

In this tutorial, we have learned that descriptors can be used to define components and groups of components. We have also seen how to obtain a descriptor’s properties, check its completeness, convert it to a string, and compare it to other descriptors. Lastly, we saw an example of their usage by showing how to register a component to a factory.