Creating Extensions

Flogo exposes a number of different extension points. You can easily extend the capabilities available by building your own activities. In this section, you explore the Activity contribution point and learn how to build a custom Activity in GO.

Step 1: Generate a basic framework

The easiest way to start creating Activities is to clone the content present in TIBCO Extensions. The Activity built returns the concatenation of the two parameters and displays it on the console.

You must pull the following sample Activity to begin working on Flogo Core.

git clone https://github.com/TIBCOSoftware/tci-flogo.git

mkdir-p myNewActivity

cp -R /tci-flogo/samples/extensions/TIBCO/activity/* /myNewActivity

Step 2: Update the Metadata

After you have pulled an example from the Flogo core, the first step is to update the descriptor.json file with the required information. The file contains the metadata for the new Flogo Activity. The metadata in the file contains the following elements.

Element Description
name

The name of the Activity

This must match with the name of the folder in which the Activity has been added.

version

The version of the Activity.

The semantic versioning for the activities must be used.

type

The type of contribution.

For example: flogo:Activity in this case.

title The application title to be displayed in the Flogo Web UI.
ref The reference to the GO package that is used by the web UI to fetch the contribution details during the installation.
description

A brief description of the Activity.

This is displayed in the Flogo Web UI.

author The creator of the Activity.
settings

An array of name-type pairs that describe the Activity settings.

Note:
  • Activity settings are pre-compiled and can be used to increase performance.
  • The settings are not fetched for every invocation.
input

An array of name-type pairs that describe the input to the Activity.

The anInput parameter must be of the string type.

output

An array of name-type pairs that describe the output of the Activity.

The anOutput parameter must be of the string type.

The updated descriptor.json file must look as follows:

Caution: Code snippets in the PDF could have undesired line breaks due to space constraints and should be verified before directly copying and running it in your program.
{
  "name": "sample-Activity",
  "type": "flogo:Activity",
  "version": "0.0.1",
  "title": "Sample Activity",
  "description": "Sample Activity",
  "homepage":"https://github.com/project-flogo/tree/master/examples/Activity",
  "settings": [
    {
      "name": "aSetting",
      "type": "string",
      "required": true
    }
  ],
  "input": [
    {
      "name": "anInput",
      "type": "string",
      "required": true
    }
  ],
  "output": [
    {
      "name": "anOutput",
      "type": "string"
    }
  ]

Step 3: Build the Logic

Now, you must update the .GO files available in the current directory. The . GO files in the directory are as follows:

File types Description
Activity.go Contains the logic behind Activity implementation in GO
Activity_test.go Contains unit tests for the Activity
metadata.go Contains the basic input, output, and settings metadata used by the engine

The first step is to update the metadata.go file. Define the input, output, and settings in the file. These details are used by the engine to build the Activity. Also it is used for leveraging contributions using the Flogo GO library. This enables GO developers to leverage strongly typed objects for IDE auto-completion.

The sample package of the metadata file must look like as follows:

Caution: Code snippets in the PDF could have undesired line breaks due to space constraints and should be verified before directly copying and running it in your program.
import "github.com/project-flogo/core/data/coerce"

type Settings struct {
	ASetting string `md:"aSetting,required"`
}
			
type Input struct {
	AnInput string `md:"anInput,required"`
}

func (i *Input) FromMap(values map[string]interface{}) error {
	strVal, err := coerce.ToString(values["anInput"])
	if err != nil {
		return err
	}
	i.AnInput = strVal
	return nil
}
			
func (i *Input) ToMap() map[string]interface{} {
	return map[string]interface{}{
		"anInput": i.AnInput,
	}
}

type Output struct {
	AnOutput string `md:"anOutput"`
}

func (o *Output) FromMap(values map[string]interface{}) error {
	strVal, err := coerce.ToString(values["anOutput"])
	if err != nil {
		return err
	}
	o.AnOutput = strVal
	return nil
}
			
func (o *Output) ToMap() map[string]interface{} {
	return map[string]interface{}{
		"anOutput": o.AnOutput,
	}
}

The next step is to look at the Business logic and update the Activity.go file.

The sample package of the Activity file must look like as follows:

Caution: Code snippets in the PDF could have undesired line breaks due to space constraints and should be verified before directly copying and running it in your program.
package sample

import (
	"github.com/project-flogo/core/Activity"
	"github.com/project-flogo/core/data/metadata"
)

func init() {
	//Activity.Register(&Activity{}, New) to create instances using 
      factory method 'New'
	_ = Activity.Register(&Activity{})
}

var ActivityMd = Activity.ToMetadata(&Settings{}, &Input{}, &Output{})

//New optional factory method, should be used if one Activity instance per configuration is desired
func New(ctx Activity.InitContext) (Activity.Activity, error) {

	s := &Settings{}
	err := metadata.MapToStruct(ctx.Settings(), s, true)
	if err != nil {
		return nil, err
	}

	ctx.Logger().Debugf("Setting: %s", s.ASetting)

	act := &Activity{} //add aSetting to instance

	return act, nil
}

// Activity is an sample Activity that can be used as a base to create a custom Activity
type Activity struct {
}

// Metadata returns the Activity's metadata
func (a *Activity) Metadata() *Activity.Metadata {
	return ActivityMd
}

// Eval implements api.Activity.Eval - Logs the Message
func (a *Activity) Eval(ctx Activity.Context) (done bool, err error) {

	input := &Input{}
	err = ctx.GetInputObject(input)
	if err != nil {
		return true, err
	}

	ctx.Logger().Debugf("Input: %s", input.AnInput)

	output := &Output{AnOutput: input.AnInput}
	err = ctx.SetOutputObject(output)
	if err != nil {
		return true, err
	}

	return true, nil
}

Now, to test and build the Activity, you must get below GO packages.

go mod init
go mod tidy

Step 4: Perform Unit Testing

After you have completed the building logic of the Activity, you must now perform a unit test. Unit testing gives you an automated way to test the Activity to make sure that it works. This also lets other developers run the same tests to validate the output.

The sample package of the Activity_test file must look like as follows:

Caution: Code snippets in the PDF could have undesired line breaks due to space constraints and should be verified before directly copying and running it in your program.
package sample

import (
	"testing"

	"github.com/project-flogo/core/Activity"
	"github.com/project-flogo/core/support/test"
	"github.com/stretchr/testify/assert"
)

func TestRegister(t *testing.T) {

	ref := Activity.GetRef(&Activity{})
	act := Activity.Get(ref)

	assert.NotNil(t, act)
}

func TestEval(t *testing.T) {

	act := &Activity{}
	tc := test.NewActivityContext(act.Metadata())
	input := &Input{AnInput: "test"}
	err := tc.SetInputObject(input)
	assert.Nil(t, err)

	done, err := act.Eval(tc)
	assert.True(t, done)
	assert.Nil(t, err)

	output := &Output{}
	err = tc.GetOutputObject(output)
	assert.Nil(t, err)
	assert.Equal(t, "test", output.AnOutput)
}

To run all the test cases for your Activity, run below command:

go test

On a successful run the result must look like as follows:

PASS
ok github.com/tibco/newConnector/myNewActivity 0.002s

Step 5: Upload the Activity in the Flogo App

Now, you can use the Activity in a Flogo app.

To install the Activity in Flogo, in the web UI, under Environment and Tools, go to the Extensions tab, and click Upload.