PKG-INFO Format

PKG-INFO should be a YAML file with the following key-value items.

entry-point

Entry-point is mandatory.

The value refers to the entry point of the application code from the framework. Value of the entry-point should in the following format

module.path:callable_name

For example, if the entry point callable app_entry(state) is in aalam_base.main module, the entry point will be

aalam_base.main:app_entry

statics-url

statics-url is optional

It is the prefix of the URL on which all the static files are served. The static resource can be accessed with the prefix followed by the path of the file. The absolute path of the file be obtained with the help of statics-path value. Example, if the prefix is /<provider-code>/<app-code>/s and the file is installed in <statics-path>/images/static.png, the URL to this resource will be like /<provider-code>/<app-code>/s/images/static.png.

The static resources that are not served through URLs starting with this prefix, has to have a URL route registered for the same.

With this setting, the application need not register explicit URLs to serve static resources. The framework will automatically control the response to the request of these static resources.

statics-path

statics-path is mandatory when statics-url is used. Otherwise not required.

It contains the name of the directory under which all the static files are installed. The directory name should be relative to the installed root.

For example, if the static files are all installed in $ROOT/resource/statics/, value for this will be resource/statics

The framework derives the path of the resource with the help of the URL. For example, for a URL /aalam/base/s/contacts/script.js with the statics-url as /aalam/base/s, the path of the file will be

os.path.join(cfg.CONF.package_dir, statics_path, "contacts/script.js")

permissions

‘permissions’ is optional

The value to this another key-value dictionary. This describes the list of permissions that the application supports.

Every permission name will have a group name.A permission is referred by provider-code/app-code/group-name/permission-name.

The permission map should be in the following format

permissions:
    permission-groups:
        group-name-1: "Group 1's description"
        group-name-2: "Group 2's description"
        .
        .
        group-name-n: "Group n's description"

    group-name-1:
        permission-name-11: "Permission 11's description"
        permission-name-12: "Permission 12's description"
        .
        .
        permission-name-1n: "Permission 1n's description"

    .
    .
    group-name-n:
        permission-name-n1: "Permission n1's description"
        .
        .
        permission-name-nn: "Permission nn's description"

    requires:
        provider-1/app-1:
            group-name-p1a1-1:
                - permission-p1a1-11
                - permission-p1a1-22
            group-name-p1a1-n:
                - permission-pa-nn

        provider-2/app-2:
            group-name-p2a2-1:
                - permission-p2a2-11
                - permission-p2a2-22
            group-name-p2a2-n:
                - permission-pa-nn

The permissions map contains the list of dictionary objects. Out of all the keys, permission-groups and requires are standard. Rest of the key names are dynamic based on the keys in permission-groups object.

Here, requires object contains the list of permissions that are defined in other applications but required by this application.

Note that ‘group-name’ and ‘permission-name’ should contain only alpha numeric characters and the special character hyphen -, however it should not start with a hyphen.

A real world example of the permissions value can be seen below.

permission-groups:
    Journal-Books: "Journal Book management"

Journal-Books:
    create: "Create a journal book"
    delete: "Delete a journal book"
    access: "Access book details"

requires:
    aalam/base:
        Contacts:
            - create
            - delete

The application in the above example defines the following set of permissions

Journal-Books/create,
Journal-Books/delete,
Journal-Books/access

It also needs the following permissions from the base application

aalam/base/Contacts/create,
aalam/base/Contacts/delete

messages

messages is optional

The value of this item is a key-value dictionary object.

This item defines the list of message templates used for sending alerts.

For example,when an app is installed in a server, an alert will be posted to some users with a fixed template.

Each key in the value refers to the name of a message group. Message can be grouped, so that it becomes easier for the users to follow/unfollow all the message in a group.

Each message template is identified by it’s index value. The index value should be unique across this message map.

Format of messages is as below

messages:
    MessageGroup-1:
        - index: 0
          body: Template value 1, {dynamic_variable}
        - index: 1
          body: Template value 2, {dynamic_variable}

    MessageGroup-2
        - index: 2
          body: Template value 3, a static template
        - index: 3
          body: Template value 4.

Here ,body contains the message template. Any dynamic value that needs to formatted on the template should be put within {}. The value for the dynamic variables will be given the application while posting the message.

For example, when an application is stopped, the template can have the {app_name} as a dynamic variable and will supply the value for the {app_name} which is stopped, at the time of posting this message.

A real world example will be like

message:
    WelcomeMessages:
        - index: 0
          body: Welcome to this portal. You are the administrator to this portal.
        - index: 1
          body: Welcome! You can check you permissions here.
    AppMessages:
        - index: 2
          body: New app - {app_name} is installed by {user}
        - index: 3
          body: '{app_name}' is uninstalled by '{user}'

In the above example, we have two groups.

  • WelcomeMessages are the messages that the application sends whenever a new user joins the portal. The index 0 message is used for posting the owner and index 1 is used for posting other users.
  • AppMessages are the messages sent when any app is installed or uninstalled. Items with {} will be replaced by relevant values.

settings

settings is optional

The value to this item is a key-value dictionary object.

This item defines the list of settings for an application. settings are variables for which the user chooses the values.

A settings variable is of two types

  • Global
Global settings are variables that is applicable to the entire server and the values of which are same for all users. To modify a global setting, a user needs to have aalam/base/Settings/modify-globals permission. Example, date and time format is a global setting.
  • Personal
Personal settings are the variables that are user customizable. A user doesn’t need any extra permission to modify personal settings. Example, choice of a theme’s colour is a personal setting.

The settings format is as below

settings:
    - code: short_form_descritpion. See "Code Specification"
      name: Detailed name, which will be seen by the user
      default: Default value of this setting
      property: see "Property Specification"
      type: Global/Personal
      help: Information on how to input the values
      validator: Method name to be invoked for validation. See "Method Specification"

    - Similiar to the above format

Code Specification

Settings code is a short code for describing for a setting variable. The code value will not be displayed to the user.It is to identify the setting by the framework and other applications.

It should abide by the following rules.

  • Can contain only alpha numeric and hyphen - characters.
  • Should not start with hyphen character.
  • It should be unique for an application.

Property Specification:

Every setting can have following property type

  • Number
Integer or decimal value
  • Text
String
  • Choices

Lets the user to choose from a list of choices. If the choice type and method are

  • single, dropdown: A listbox will be displayed to choose the value
  • single, boxes: A set of radio buttons will be displayed
  • multiple, dropdown: List box with support for multiple selection will be displayed
  • multiple, boxes: A set of check boxes will be displayed.

Property will be of the following format

property:
    type: Ex. number/text/choices(??)
    number:
       max: Maximum value for a number, applicable only for number. This is optional.
       min: Minimum value for a number, applicable only for number. This is optional.
    text:
       max_len: Maximum length of text, applicable only for text. This is optional.
    choices:
       type: single/multiple, applicable only for choices. This is mandatory
       method: dropdown/boxes, applicable only for choices. This is mandatory.
       values:
        - value1
        - value2
        - value3

Method Specification:

This is the method that will be invoked by the framework whenever a setting is fed by the user. The method name should be of the format

module.path:method_name

The validator method should accept two parameters

  1. settings_code The code defined for a setting in the application’s settings map object.
  2. input_value The value that was fed by the user for this settings_code.

The validator method should return either of the following

  • None When the method finds the input_value to be valid for the settings_code
  • String message When the validator finds an error with the input_value, this message will be displayed to the user for further assistance.

A sample validator can be seen below

def color_validator(settings_code, input_value) :
    if input_value not in ['red', 'green', 'blue']:
        # Value is invalid, return an error message.
        return "Value should be either 'red', 'green' or 'blue'"

    # Value is valid, so return None
    return None

hooks

hooks are optional.

Value should be a key-value dictionary.

‘hooks’ lists the set of hooks that an application is interested in. Please see the python framework documentation for more information on hooks.

Value contains just two keys.

  • hook
List of URLs in other applications that this application wants to hook.
  • restrict
List of URLs in this application that it desires to restrict being hooked by other applications. One can include exceptions to the restrictions.

hooks format is as below.

hook:
    - url: Url to be hooked, See "Hook Url Format"
      app: App which has this URL in the format provider-code/app-code
      method: HTTP Method filter for the url
      handler: Hooker's handler to be invoked on hook, see "Hook callback"
      type: Before or After or Both (B | A | BA)

    - Same as above

restrict:
    - url: The url to be restricted, See "Hook Url Format"
      method: The method of the url to be restricted
      type: (B | A | BA) to be restricted
      except:
        - provider-code-1/app-code-1
        - provider-code-2/app-code-2
        - provider-code-3/app-code-3

Hook URL format

If the URL needs to be matched statically, then a static url can be used. For example, if you want to hook GET /aalam/base/user/user@test.test, you can input the URL path directly. But if you want to include a wildcard match) you have to use

/aalam/base/user/*

The asterik * matches an element with the slashes /<matches-only-this-value/. For example, the above will match the following url

/aalam/base/user/user1@test.test
/aalam/base/user/user2@test.test

But will not match

/aalam/base/user/user1@test.test/status

Wherever you want to add a wildcard match, keep an asterik *. For example,

/aalam/base/*/user1@test.test/*

will match the following urls

/aalam/base/user/user1@test.test/status
/aalam/base/user/user1@test.test/info
/aalam/base/permissions/user1@test.test/assign

Hook Callback

Hook callback method name should be like

module.path:method_name

Refer the python framework documentation for more information on the parameters and the return values of the hook callback methods.

libdeps

libdeps is optional

libdeps contain additional dependencies in the form other than python packages. For example, if your app depends on some libraries like libjpeg or libz, those dependencies can be listed under libdeps.

libdeps is a listing of a set of commands which fetches or builds some program or library and its artifacts that will be used by your app.

The packager runs on a tiny core linux based distribution. You can use tce-load to install .tcz packages.

For example, let’s say your app has a dependency on a python package ‘Pillow’, which needs libz.so and libjpeg.so. libz.so is available from the tiny core linux package zlib_base-dev.tcz and libjpeg.so is available from the package libjpeg-turbo-dev.tcz. For your app to run properly, you would need these libraries. In such a case your libdeps should be like the following

libdeps:
  -
    name: jpeg
    actions:
      - tce-load -wic libjpeg-turbo-dev
    artifacts:
      - /usr/local/lib/libjpeg.so.62
  -
    name: libz
    actions:
      - tce-load -wic zlib_base-dev
    artifacts:
      - /usr/lib/libz.so

In the above example, you can just have one action/artifact object to install both the libraries in one command.

Alternatively, you can even build libraries from source using make and gcc. If the building process needs libraries like autoconf, libtool etc, you can add an action to install these required .tcz packages before running the actual command to build the app.

libdeps:
  -
    actions:
      - tce-load -wic cmake
      - curl -v https://url.of.the.source.archive | tar -xz -C /tmp/source-path
      - cd /tmp/source-path && ./configure && make
    artifacts:
      - /tmp/source-path/buid-dir/lib.so

properties

This is optional

properties contains the list of pre-defined properties for an app. It should be a dictionary with the following keys

  • public_rootable
By setting this property to True, you mean that this web application can serve the '/' (slash/root) page for the public (anonymous) users.Server administrators can choose one such public rootable app from the list of their apps to serve it as a website for the public users as home page. Apps that are publicly rootable should have the routes registered for the same.
For example, if your app is public rootable, then you should be having a route for '/'. These routes work only for the anonymous users. For users who are authenticated, these routes will never be used. For authenticated users, routes starting with /<provider-code>/<app-code>/ alone will work.

eval_rst .. note:: Except `'/'` all other routes should begin with `'/_/'`. Only then the requests will be routed to the public rootable app.

The value should be added to the configuration like

properties:
  public_rootable: True

depends

When your application depends on other applications, those dependecies must be listed under this section.

These dependencies are used while installing or updating any application. Each dependency should be given in the following format.

depends:
  - provider_code: Provider code of the dependee app
    app_code: App code of the dependee app
    pattern: The version as is or a regex pattern to match more than one version

You can still use APIs for other apps from your application even without listing them under this section. But there is no guarentee that the API will be successfull.

For example, the Aalam Invoicer app depends on Aalam Stock app. It should list the Stock app under its depends section for successful API transaction. But suppose that the Aaalam invoicer calls the API of a payment service application that is not listed under depends section,then there will be no guarentee that the API transaction will be successful.