Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.


Panel

Table of Contents
maxLevel3

Prerequisites

...

The following products should be installed:

  • VSCode Plugin 2.0.0
  • uip-cli

...

  • 2.0.0
  • Universal Controller 7.4.0.0
  • Universal Agent 7.4.0

...

  • .0

The development environment will be WSL (Windows Subsystem for Linux), however, it could be any of the supported platforms. The tutorial assumes a folder called demo_template has been opened in VSCode.

Introduction

The custom, starter template that we will create is a contrived example, but it sufficiently covers all the features and its usefulnessAll work will be done in a Python 3.7.16 virtual environment. Make sure the uip-cli is installed in the virtual environment.

Introduction

We will create an Extension that uses the requests module (pure Python) and psutil module (not pure Python as it requires C runtime).

Step 1 -

...

Initializing the

...

To create a custom starter template, we will first make use of the built-in ue-task template. Open up the terminal to the demo_template folder and run

Code Block
 uip init -t ue-task -e "extension_name=my_custom_ext" -e "universal_template_name=my_custom_template"

The directory structure should be as follows (not showing the Python *.pyc/__pycache__ files):

Code Block
demo_template/
├── .uip
│   └── config
│       └── uip.yml
├── __init__.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── src
    ├── __init__.py
    ├── extension.py
    ├── extension.yml
    └── templates
        └── template.json

The basic structure of the custom template is almost finished! Go ahead and create a file called template_config.yml at the same level as the src/ folder. The resulting directory tree is as follows:

Code Block
demo_template/
├── .uip
│   └── config
│       └── uip.yml
├── __init__.py
├── requirements.txt
├── setup.cfg
├── setup.py
├── src
│   ├── __init__.py
│   ├── extension.py
│   ├── extension.yml
│   └── templates
│       └── template.json
└── template_config.yml

Step 2 - Configuring the Initial Template

To make the template configurable, add the following content below to template_config.yml:

Code Block
themeConfluence
titletemplate_config.yml
linenumberstrue
name: example-template

version: 1.0.0

description: this is the description for example template

files_to_template: 
  - src/extension.py
  - src/templates/template.json

variables:
  msg:
    default: test_message
    description: message to print to STDOUT and STDERR
  log_level:
    default: Info
    description: Universal Template Log Level 
Info
titletemplate_config.yml details

Each template_config.yml must contain:

  • name

    • A string that identifies the name of the template (this is NOT referring to the Universal Template name). It can be anything other than the name of the built-in templates: ue-task and ue-publisher.

  • version

    • A string that identifies the template version. Not restricted to x.y.z (SemVer); it could be anything (e.g. v1).

  • description

    • A string describing the template.

Although not required, it can also contain:

  • files_to_template

    • An array containing paths to files that will be “templated” using Jinja2. All files must be specified relative to template_config.yml.

  • variables

    • A mapping/dictionary of variables that will be substituted in the relevant files specified by files_to_template. The value of each key/variable is another mapping/dictionary that must contain default and description.

Now, open src/extension.py and replace extension_start() with the code below:

...

languagepy
themeConfluence
titlesrc/extension.py::extension_start()
linenumberstrue

Extension

Open VSCode to an empty folder (e.g. /tmp/tutorial) and activate the virtual environment you wish to use.

Initialize the ue-task extension by clicking:

Image Added

For this tutorial, all parameters are suitable so, just pressing 'Enter' to select the default for each parameter is sufficient. 

Once initialized, you should now have a file called requirements.txt in your working folder:

Image Added

Open the file and add requests==2.28.2 (version is optional) as follows:

Code Block
titlerequirements.txt
linenumberstrue
#
# Specify any third-party Python modules that should be bundled with the
# extension. uip-cli will automatically download and bundle the modules
# upon running `uip build` or `uip push`. 
#
# Refer to https://pip.pypa.io/en/stable/reference/requirements-file-format/
# for the expected format.
#

requests==2.28.2

Step 2 - Using requests module

Open extension.py and modify it as follows:

Code Block
languagepy
themeConfluence
titleextension.py
linenumberstrue
from __future__ import (print_function)
from universal_extension import UniversalExtension
from universal_extension import ExtensionResult
from universal_extension import logger
import requests


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
        """

        my_msgresp = "{{ msg }}"

        # Get the value of the 'action' field
        action_field = fieldsrequests.get('action',
[])         if len(action_field) != 1:
            # 'action' field was not specified or is invalid'https://httpbin.org/basic-auth/user/pass',
                   action = ''
        else:
            action = action_field[0]

        if action.lower() == 'print':auth=('user', 'pass')
        )
   # Print to standard output...
            print(my_msg)
        else:
            # Log to standard error...
            logger.info(my_msgprint(resp.status_code)

        # Return the result with a payload containing a Hello message...
        return ExtensionResult(
            unv_output='Hello Extension!'
        )

On line 19, a variable called my_msg is defined with the value of "{{ msg }}". This is Jinja2 syntax that will eventually be replaced with the specified value for msg (or the default, if they don’t specify anything) during initialization time.

On lines 31 and 34, the message was changed to print the value of my_msg.

Now, open src/templates/template.json and replace the value of logLevel key (should be around line 86) with the change shown below:

Code Block
themeConfluence
firstline82
titletemplate.json
linenumberstrue
      "sysId": "535c354ed083489cb10413019d71a57c",
      "textType": "Plain"
    }
  ],
  "logLevel": "{{ log_level | title }}",
  "minReleaseLevel": "7.0.0.0",
  "name": "my_custom_template",

...

Step 3 - Packaging the Initial Template

Although the example is a bit contrived, we have managed to create a fully-functional customized template.

To package the template, simply zip up everything into a file called example_template.zip (or whatever you want to call it). Its structure should be as follows (if *.pyc and __pycache__ files are there, it’s fine):

Code Block
Archive:  example_template.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2023-04-04 15:37   __init__.py
      306  2023-04-04 15:37   requirements.txt
       43  2023-04-05 11:03   setup.cfg
    12088  2023-04-05 11:03   setup.py
        0  2023-04-05 11:03   src/
     1723  2023-04-05 11:03   src/extension.py
      221  2023-04-05 11:03   src/extension.yml
        0  2023-04-05 11:03   src/templates/
     3278  2023-04-05 11:09   src/templates/template.json
        0  2023-04-04 15:37   src/__init__.py
      349  2023-04-05 11:05   template_config.yml
---------                     -------
    18008                     11 files

...

5, we import the requests module

On lines 36-40, we call requests.get on a sample url and print the status code

Step 3 - Pushing out Extension

We are ready to test out our extension!

Assuming this Extension has not been pushed out to the Controller, go ahead and run

Image Added

You may need to configure the username, password, and url first.

Running the Push All command internally runs the Build All command followed by the Upload All command. The Build All command will download all dependencies specified in requirements.txt to a folder called 3pp and package the folder in the final package archive.

Next >