Task Entry Point

Task Entry Point

Introduction

To demonstrate the process of Extension development, a sample Extension will be created and deployed using Visual Studio Code with the UIP Visual Studio Code Extension.  The UIP Visual Studio Code Extension relies heavily on functionality provided by the uip-cli tool to enhance the UIP development experience.  Therefore, a similar experience can be achieved with other code editors using uip-cli manually.  This alternative use scenario will be documented as well.

The functionality of this sample Extension is contrived and serves no real purpose other than to illustrate the process of developing an extension that supports and utilizes all features available. The sample Extension, however, can be a good starting point for creating more complex Extensions.

On this page, we will cover the following:

  1. Introduce the UIP Visual Studio Code Extension and uip-cli that will be used to create and configure the sample Extension.

  2. Deploy the initial Extension without any changes and review the output.

  3. Modify the sample Extension.

  4. Deploy the modified Extension to the Controller and review the output.

Note that it is assumed the latest version 2.0.0 of UIP Visual Studio Code Extension and uip-cli are already installed. See the previous document for installation instructions.

Step 1 - Create a New Extension Project using the UIP VS Code Extension

As mentioned in Development Environment Set-Up, this tutorial will be using Visual Studio Code running in Windows and connected to a WSL (Windows Subsystem for Linux) project environment.

Initializing a new UIP project is a multi-step process.

  1. Select a folder for VS Code to open for the new UIP project.

  2. Select a starter template for the UIP project.

  3. Iterate over the template parameters.

To begin, create a project directory (for example, ~/dev/extensions/sample-task) in the WSL file system where the Universal Extension will be created.

Select project folder

Next, Use Visual Studio Code to open the sample-task folder in WSL:

  1. Open the Remote Window command pallet.

  2. Select "Open Folder in WSL...".

In the resulting dialog, navigate to the sample-task folder and click on Select Folder:

Selecting starter template

Now that VS Code is in the WSL environment, navigate to the activity bar on the left hand size and click the Stonebranch logo.

This will expand the menu and you should see a list of all the available extension templates. Go ahead and click the icon shown below to initialize the ue-task template:

Setting template parameter values for selected starter template

A few seconds after clicking the icon shown above, a sequence of input boxes will shown up containing the parameters for the ue-task starter template. The parameters allow you to supply project specific values into boilerplate template code at creation time.

For this tutorial, all parameters are suitable so, just pressing 'Enter' to select the default for each parameter would be sufficient.  However, in the series of images below, the 'Extension Owner' parameter was modified to use a value of "SampleOwner" (step 6/8).  Notice that the dialog title will update to indicate which step you are on and how many steps are left to complete the dialog - (2/8), (3/8), (4/8), etc..  

Note that pressing “Alt + left arrow” or clicking the ← icon in the top left of the image will return to the previous step (preserving the value of the current step).

After hitting enter on the last parameter:

  1. A notification pops up to indicate the new project has been created.

  2. The new project is created

  3. The uip.yml project configuration file is opened in an editor.

  4. The generated extension.py source file is opened in an editor.

When the “UIP: Initialize New Project” command completes, a project will be initialized in the selected folder with the following structure:

|-- sample-task # Sample project directory |--setup.py # Setup file for packaging extension |--setup.cfg # Configuration file for configuring setup |--__init__.py | |--.uip # Folder used by CLI to validate directory | |--config # Configuration File Folder | |--uip.yml # Local Configuration File | |--src # Source directory for Extension implementation |--extension.py # Extension implementation |--extension.yml # Extension metadata |--__init__.py | |--templates |--template.json # Universal Template JSON Definition File

At this point, the project is fully initialized. 

Step 1 supplemental - Create a New Extension Project using the CLI

Create a project directory (for example, ~/dev/extensions/sample-task) where the sample-task Extension will be created, and cd into the directory.

As of now, the CLI offers two starter Extension templates called ue-task and ue-publisher. To see all the available starter Extension templates, type the template-list command:

Both the starter Extensions above can be configured before they are initialized. 

To see the list of variables that can be used to configure one of the starter Extension templates, type the template name after the previous command.

Shown below are the list of variables for the ue-task starter template:

As shown in the image above, there are quite a few ways to configure the Extension. As for the sample Extension developed for this tutorial, we will work with ue-task, and only the owner_name option will be configured. Everything else will be set to the default value.

The Extension can be configured and initialized using the init command. There are three ways to configure the ue-task Extension template using the command line:

  • Using the -e option multiple times:

  • Using a JSON string 

  • Using a JSON/YAML file

    • Create a YAML file called vars.yml and define the variables as follows:

    • Use the YAML file to initialize the project:

Pick one of the three ways shown above; it does not matter which one. Note that an optional, positional argument can be provided at the end of each of the three commands that specifies the directory in which the Extension will be initialized to. If the directory does not exist, the CLI will create it. 

To verify that the Extension was configured as intended, open the extension.yml file (see directory structure below), and ensure the owner_name is SampleOwner.

This is the directory structure of the sample-1 Extension:

|-- sample-task # Sample project directory |--setup.py # Setup file for packaging extension |--setup.cfg # Configuration file for configuring setup |--__init__.py | |--.uip # Folder used by CLI to validate directory | |--config # Configuration File Folder | |--uip.yml # Local Configuration File | |--src # Source directory for Extension implementation |--extension.py # Extension implementation |--extension.yml # Extension metadata |--__init__.py | |--templates |--template.json # Universal Template JSON Definition File

 

Step 2 - Deploy the Initial Extension using the UIP VS Code Extension

Before deploying the Extension, let's take a look at the code to get a sense of the expected output. Open the ~/dev/extensions/sample-task/src/extension.py in VS Code (It should already be open in the editor following the project initialization):

extension.py
from __future__ import (print_function) from universal_extension import UniversalExtension from universal_extension import ExtensionResult from universal_extension import logger class Extension(UniversalExtension): """Required class that serves as the entry point for the extension """ def __init__(self): """Initializes an instance of the 'Extension' class """ # Call the base class initializer super(Extension, self).__init__() def extension_start(self, fields): """Required method that serves as the starting point for work performed for a task instance. Parameters ---------- fields : dict populated with field values from the associated task instance launched in the Controller Returns ------- ExtensionResult once the work is done, an instance of ExtensionResult must be returned. See the documentation for a full list of parameters that can be passed to the ExtensionResult class constructor """ # Get the value of the 'action' field action = fields.get('action', [""])[0] if action.lower() == 'print': # Print to standard output... print("Hello STDOUT!") else: # Log to standard error... logger.info('Hello STDERR!') # Return the result with a payload containing a Hello message... return ExtensionResult( unv_output='Hello Extension!' )

The code presented above is ready to run without any modifications.

Note the following points of interest from the code above:

Line 17

Starts the implementation of the extension_start override method. This is the entry point for normal task execution and must be implemented by all Universal tasks.

Line 36

Extracts the value of the 'action' field passed down from the Controller (See the Info box below).

Line 38-40

Uses the standard Python print function to print the 'Hello STDOUT!' message to standard output stream if the selected action is 'print'

Line 42-43

Uses the ExtensionLogger class (exposed as part of the universal_extension file) to log the 'Hello STDERR!' message to the standard error stream if action is anything other than 'print'

Line 46

Uses the unv_output parameter of the ExtensionResult class to send the 'Hello Extension!' string to the EXTENSION output payload associated with the task instance in the Controller.

What is the 'action' field?

You may have noticed the template.json file in the sample-task directory structure above. That is the Universal Template, in JSON format, that the Controller uses to render the template UI. 

One of the things template.json defines is the 'action' field of type Choice with two possible values: 'print' and 'log'. Soon, the template will be pushed out to the Controller where we will be able to see it visually. 

For now, keep in mind that the Extension can use fields defined in the Universal Template. 

 

Now, let's deploy the Extension. Note that Extensions are stored in the Controller as Universal Templates of type Extension.

To connect to the Controller, the CLI needs the Controller's URL along with userid and password. To avoid typing the same information multiple times, the URL and userid can be stored in the local configuration file (~/dev/extensions/sample-task/.uip/config/uip.yml), and the password can be stored as an environment variable.  Alternatively, all values can be set as environment variables and, using the UIP VS Code Extension, those values can be persisted with the project and reloaded as needed. 

Set Project specific Environment Variables