Attributes

Extend the platform with attributes on all the things!

Overview

Within Kinetic, we use attributes as a way to extend the baseline Kinetic Platform components like Forms, Kapps, Users and Teams. Think of attributes like variables, tags or metadata that can be leveraged within form logic, security policies, portals or workflows to drive specific behavior.

Attributes are commonly used by Kinetic Platform developers within Workflows and Portals as a way to expose variables that non-developers can 'toggle' in order to change the way the system behaves without actually changing code.

For example, a developer may create an attribute on a Space or Kapp that stores a URL for the background image that gets displayed within a portal. Inside the portals HTML/CSS, the developer then references this attribute allowing a non-developer to change the background image without having to change any front end code.

Defining Attributes

Before associating and setting an attribute value on a Form, Kapp, User...etc, an Attribute Definition must first be created for its respective type. Attributes for space wide components like Users, Teams, and the Space itself are defined at the Space level and require spaceAdmin permissions to create or change them. Attributes for Kapp related components like Forms, Categories, and the Kapp itself are defined at the Kapp level and can only be created or changed by users that have access to modify the kapp.

📘

Attribute Definition Settings

All Attributes Definitions have the following settings:

  1. Name: How the attribute will be referenced within the system
  2. Description: A short description of what the attribute is intended to be used for so that others managing the system can leverage it
  3. Allows Multiple: If set, the attribute can be given multiple values. One use case for allowing multiple is for tagging forms with keywords. You may want to create an attribute called "Keyword" that can have multiple values
1238

Defining an Attribute within the Kinetic Platform

Attribute Types

Space Attributes

Space Attributes are configurations used to extend functionality across the space. Space Attributes can only be defined and set by spaceAdmins within the system. A common Space Attribute is 'Header Color' which may be created by a front end developer to define the header color that is used throughout the system. This attribute can be easily changed in the future if a new header color is desired.

User Attributes

User Attributes are configurations used to extend a user record within the system. User Attributes are defined by spaceAdmins but can be applied to a users record by anyone with 'User Modification' privileges. A common User Attribute is 'Manager' which may be leveraged within workflow to assign approvals to a users manager. Because User Attributes are not changeable by the end user (unless the end user has User Modification privileges) they are a safe, convenient way to store this type of information.

User Profile Attributes

User Profile Attributes are very similar to User Attributes however User Profile Attributes are modifiable by the end user. A common usage of a User Profile Attribute is 'Phone Number'. A users phone number is "safe" for the end user to update and can be used within the portal to display the users phone number wherever their profile is displayed.

Team Attributes

Team Attributes are configurations used to extend a Teams within the system. Team Attributes are defined by spaceAdmins within the Space and can be applied to a Team by anyone with 'Team Modification' privileges. A common Team Attribute is 'Manager' which may be leveraged within workflow to escalate or notify the manager if a team hasn't completed an assignment.

Datastore Form Attributes

Datastore Form Attributes are configurations used to extend Datastore Forms within the system. Datastore Form Attributes are defined within the Space by spaceAdmins. These attributes can be applied to a Datastore Form by anyone that has 'Form Modification' privileges to the respective form. A common Datastore Form Attribute is 'Owning Team' which may be leveraged within a Security Policy Definition to determine which team is able to create or modify datastore records within that given datastore form.

Kapp Attributes

Kapp Attributes are configurations used to extend functionality within and across a Kapp. Kapp Attributes can only be defined and set by anyone with 'Kapp modification' privileges. A common Kapp Attribute is 'Description' which may be created by a portal developer to display to end users the purpose of the Kapp. This attribute can be easily changed in the future by anyone with Kapp Modification privileges if the description needs to be updated.

Category Attributes

Category Attributes are configurations used to extend functionality for a Category within a Kapp. Category Attributes can only be defined and set by anyone with 'Kapp modification' privileges within the Categories respective Kapp. A common Category Attribute is 'Parent' which may be used as a way to build hierarchical relationships between categories and represent them as such within the end user portal.

Form Attributes

Form Attributes are configurations used to extend Forms within a Kapp. Form Attributes are defined within the Kapp by users with 'Kapp Modification' privileges. These attributes can be applied to a Form by anyone that has 'Form Modification' privileges to the respective form. A common Form Attribute is 'Icon' which may be leveraged by a portal developer to display the forms icon or logo with the portal dynamically based on the attributes value.

Hierarchical Attributes

It is common within Kinetic to use Attributes in a hierarchical fashion, allowing lower level components like a Form, to override higher level attributes of the same name at the Kapp or Space level if they are set. A common use case may be setting a system wide SLA for number of days until a request is completed that can be overridden by a Kapp, or a specific Form within a Kapp.

In order to accomplish this, you would create Space, Kapp and Form attributes called 'SLA Days Until Due' and set their respective values as desired. Then, when a Form is submitted we can create logic within a workflow to leverage the 'SLA Days Until Due' attribute value from either the Form, Kapp or Space whichever one is set first (see sample code below) .

Ruby (Workflow) Example

Below is an example of a parameter within a Workflow Node that is leveraging a Submission Tree's Source Data (which includes all Space, Kapp and Form attributes) to output a specific attributes value. In the case below.

<%=
# Configure Attribute Name and Default Value
#
attributeName = 'SLA Days Until Due'
default_value = '10'

# Finds the most significant, cascaded, non-nil value for a given attributeName.
#
def most_significant_value(data, attributeName, default=nil, allow_multiple=false)
  cascaded_values = 
    data['space']['attributes'].merge(
      data['kapp']['attributes']){|k,v1,v2| first_non_nil(v1,v2,allow_multiple)}.merge(
        data['form']['attributes']){|k,v1,v2| first_non_nil(v1,v2,allow_multiple)}.merge(
          data['values']){|k,v1,v2| first_non_nil(v1,v2,allow_multiple)}
  cascaded_values[attributeName] || default
end

def first_non_nil(v1, v2, allow_multiple=false)
  prepare_value(v2, allow_multiple) || prepare_value(v1, allow_multiple)
end

def prepare_value(value, allow_multiple)
  if value.is_a?(Array)
    values = value.select {|v| !v.to_s.strip.empty?}
    val = allow_multiple ? (values.empty? ? nil : values) : values.first
  else
    value
  end
end

# Return the value
data = JSON.parse(@source['Data'])
most_significant_value(data, attributeName, default_value)%>

JS (Portal) Example

The following example uses overloaded functions search for a given attribute value beginning at the Form level and up through the Space Level. The final method getConfig at the bottom of the example is what is leveraged throughout the portal's code.

/**
 * Given a model (Space, Kapp, User, Form...etc) and an attribute name returns the value of that attribute.
 * Should return undefined if attributes are missing or there is no attribute value for the given attrName. 
 *
 * @param model: { attributes }
 * @param attrName
 * @param defaultValue
 */
export const getAttributeValue = (
  { attributes, attributesMap },
  attrName,
  defaultValue,
) =>
  (attributesMap
    ? attributesMap[attrName] && attributesMap[attrName][0]
    : isarray(attributes)
      ? attributes.filter(a => a.name === attrName).map(a => a.values[0])[0]
      : attributes && attributes[attrName] && attributes[attrName][0]) ||
  defaultValue;


const getSpaceConfig = (space, name, val) => {
  if (!space) {
    throw new Error(
      'getConfig did not receive space, it must be included on ' +
        'the kapp or manually passed.',
    );
  }
  if (!space.attributes) {
    throw new Error('getConfig failed, space must include attributes.');
  }
  // If the space has a value for the desired attribute return it otherwise
  // return the default value.
  return getAttributeValue(space, name, val);
};

const getKappConfig = (kapp, space, name, val) => {
  if (!kapp) {
    throw new Error(
      'getConfig did not receive kapp, it must be included on ' +
        'the form or manually passed.',
    );
  } else if (!kapp.attributes) {
    throw new Error('getConfig failed, kapp must include attributes');
  }
  // If the kapp has a value for the desired attribute return it otherwise
  // check the space.
  return (
    getAttributeValue(kapp, name) ||
    getSpaceConfig(space || kapp.space, name, val)
  );
};

const getFormConfig = (form, kapp, space, name, val) => {
  if (!form) {
    throw new Error(
      'getConfig did not receive form, it must be included on ' +
        'the submission or manually passed.',
    );
  } else if (!form.attributes) {
    throw new Error('getConfig failed, form must include attributes');
  }
  // If the form has a value for the desired attribute return it otherwise
  // the default value.
  return (
    getAttributeValue(form, name) ||
    getKappConfig(kapp || form.kapp, space, name, val)
  );
};

/**
 * Given a model (via the  form / kapp / space options) will look for
 * the given configuration value 
 * If not found on the present model it will propagate upwards
 * until it is found otherwise it will return an option default or undefined.
 *
 * @param name
 * @param defaultValue
 * @param form
 * @param kapp
 * @param space
 */
export const getConfig = ({
  name,
  defaultValue,
  form,
  kapp,
  space,
}) => {
	if (form) {
    return getFormConfig(form, kapp, space, name, defaultValue);
  } else if (kapp) {
    return getKappConfig(kapp, space, name, defaultValue);
  } else if (space) {
    return getSpaceConfig(space, name, defaultValue);
  } else {
    throw new Error(
      'getConfig must be called with at least one of: ' +
        'form, kapp, space.',
    );
  }
};

API Documentation

To learn more about creating or modifying Attribute Definitions with our REST API, click here.