Last updatedJun 12, 2018

Storing data without a database

Apps can store data in the form of entity properties in the host application. Entity properties are key-value pairs where the key is a string used to identify the property in all operations, and the value is a JSON blob. Each host application allows properties to be stored on different types of entities, such as Jira issues, Confluence pages, or on the app. Your app will need to request the right scopes to perform operations on entity properties.

Hosted data storage is useful to Atlassian Connect developers for the following reasons:

  • Your app does not need to include a database to store data.
    Your app could be written as a set of static web pages using only HTML, CSS, and JavaScript, without any need for an application server. Your data will be stored against the host application entities.
  • Imports and exports are handled by the host product.
    Since your data is stored with the host application it is included in the host applications backups. This means that the import process will restore your data automatically. With entity properties you never need to worry about your data being lost or disconnected from the customer.
  • Conditions can be predicated on entity properties.
    You can configure whether a web fragment will be shown based on the value of an entity property.
  • The products have access to your properties.
    This means that you can write JQL queries based on issue entity properties, enabling users to enjoy the power of JQL on search data that you have defined.

Host properties are a powerful tool for Atlassian Connect developers. The following sections provide detailed explanations of how app properties, entity properties and content properties may be used in your app.

App properties

App properties are entity properties stored against the app itself. In this case the app is considered to be the storage container. However, app properties are still unique to each host application: the same app installed on two different host applications will not share the same app properties.

Limitations of app properties

App properties have the following limitations:

  • The properties for each app are sandboxed to the app. Only the app that writes the app properties can read those properties. They cannot be shared or read by other apps.
  • Each app can create a maximum of 100 properties, each property value cannot be more than 32KB in size.
  • The value stored in each property must be in valid JSON format. (Valid JSON format is defined as anything that JSON.parse can read).
  • Requests via AP.request to store and receive app properties can only be made via a logged-in user.
  • There is no mechanism to handle concurrent edits by two users to the one app property. Whomever saves data last will win.

Warning


App properties can be manipulated by a malicious authenticated user (e.g., by making REST calls through the developer console). For this reason:

  • Don't store user-specific data in app properties (particularly sensitive data).
  • Be defensive when retrieving app properties, and don't assume data consistency (arbitrary keys may be modified or deleted by users).

Supported operations

The following operations may be performed to manipulate app properties:

Examples

App properties can be set using Create or update app property:

1
2
3
4
5
6
curl --request PUT \ 
--user 'email@example.com:<api_token>' \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data '{"string":"string-value","number":5}' \
 'https://your-domain.atlassian.net/rest/atlassian-connect/1/addons/{addonKey}/properties/{propertyKey}'

Use Get app property to get the value of the property we just set:

1
2
3
4
curl --request GET \ 
--user 'email@example.com:<api_token>' \
--header "Accept: application/json" \
 'https://your-domain.atlassian.net/rest/atlassian-connect/1/addons/{addonKey}/properties/{propertyKey}'

Here is an example that will show a pop-up with a JSON property named my-property-key for an app with the key my-app-key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AP.require(['request'], function(request) {
    request({
        url: '/rest/atlassian-connect/1/addons/my-app-key/properties/my-property-key?jsonValue=true',
        success: function(response) {
            // Convert the string response to JSON
            response = JSON.parse(response);
            alert(response);
        },
        error: function(response) {
            console.log("Error loading API (" + uri + ")");
            console.log(arguments);
        },
        contentType: "application/json"
    });
});

Apart from using AP.request, the same resources are accessible via a request signed with JWT.

Conditions based on app properties

App properties can be referenced in the entity_property_equal_to, entity_property_contains_any, and entity_property_contains_all conditions to decide whether or not to show a web fragment. For example, the following is a valid condition on the app property activated:

1
2
3
4
5
6
7
8
9
{
  "condition": "entity_property_equal_to",
  "params": {
      "entity": "addon",
      "propertyKey": "activated",
      "objectName": "for-users"  
      "value": "true"
  }
}

The structure of the JSON value of the activated app property might look like this:

1
2
3
4
5
{
  "for-anonymous": false,
  "for-users": true,
  "for-admins": true,
}

Only if the for-users sub-property is set to true against the app will the condition allow the web fragment to show. Thus you can use this to decide whether or not to show web fragments based on data that you have stored in app properties. This is very useful when you have host application-wide configuration that you wish to rely upon.

Here is an example of a condition that requires the browser user to be in at least one specified group:

1
2
3
4
5
6
{
  "condition": "addon_property_contains_any_user_group",
  "params": {
      "propertyKey": "myListOfGroups"
  }
}

addon_property_contains_any_user_role is very similar to addon_property_contains_any_user_group, but references project roles in Jira.

Jira entity properties

Jira provides a mechanism to store key-value pair data against Jira entities and these are known as entity properties. Below are a list of entities that you can store properties against:

Jira Cloud platform

Jira Software

Limitations of entity properties

Entity properties have the following limitations:

  • Entity properties can be modified by all users that have permission to edit the entity; they are not sandboxed on a per-user basis. This means that the same entity property can be edited by two different users.
  • Entity properties can be modified by all apps in the system and exist in a global namespace. We recommend that you namespace the entity property keys for the properties that you wish to be specific to your app. This also means that you should avoid storing unencrypted sensitive data in entity properties.
  • There is no mechanism to handle concurrent edits by two users to the one app property. Whomever saves data last will win.
  • The scopes that your app requires to modify entity properties are different depending on the type of entity property that you wish to modify. For example, to delete an issue entity property you only need DELETE scope. However, to delete a project entity property you require PROJECT_ADMIN scope.
  • The value stored in each property must be in valid JSON format. (Valid JSON format is defined as anything that JSON.parse can read).
  • The maximum length of an entity property key is 255 bytes.
  • The maximum length of an entity property value is 32768 bytes.

Keep these limitations in mind when you use entity properties.

Issue property example

In this example an issue entity property with the key party-members is set on the issue ET-1.

To create an issue property called party-members for an issue ET-1, you can make the following request:

1
2
3
4
5
6
curl --request PUT \
  --user email@example.com:<api_token> \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"party": {"attendees": ["andrew","becky","charlie","dave"],"attendeeCount": 4}}' \
  --url 'https://your-domain.atlassian.net/rest/api/2/issue/ET-1/properties/party-members'

Then to get that data back, you can make the following request using Get issue property:

1
2
3
4
curl --request GET \ 
--user 'email@example.com:<api_token>' \
--header "Accept: application/json" \
 'https://your-domain.atlassian.net/rest/api/2/issue/ET-1/properties/party-members'

Which returns a JSON representation of the party-members issue property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    "key": "party-members",
    "value": {
        "party": {
            "attendees": [
                "andrew",
                "becky",
                "charlie",
                "dave"
            ],
            "attendeeCount": 4
        }
    }
}

You could then use the Jira entity property module to index these issue entity properties so that the data becomes available in JQL searches. See the entity property documentation for details.

Conditions on entity properties

Entity properties can be referenced in the following conditions to decide whether or not to show a web fragment:

  • entity_property_exists
  • entity_property_equal_to
  • entity_property_equal_to_context
  • entity_property_contains_any
  • entity_property_contains_all
  • entity_property_contains_context
  • entity_property_contains_any_user_group
  • entity_property_contains_any_user_role

You can use the entity_property_equal_to condition to decide whether or not to show a web fragment based on the data in an entity property. For example, if we had an issue entity property with the key myExtraProperties and a JSON value that has the field isSpecial set to true (JSON boolean) then we could write the following condition:

1
2
3
4
5
6
7
8
9
{
  "condition": "entity_property_equal_to",
  "params": {
      "entity": "issue",
      "propertyKey": "myExtraProperties",
      "objectName": "isSpecial",
      "value": "true"
  }
}

It is important to note that the params.value field currently expects a string. Therefore you will need to convert your JSON into a string before you can compare it for equality. For example, to check that the JSON string "special" was stored in myExtraProperties then the condition must be written like so:

1
2
3
4
5
6
7
8
9
{
  "condition": "entity_property_equal_to",
  "params": {
      "entity": "issue",
      "propertyKey": "myExtraProperties",
      "objectName": "isSpecial",
      "value": "\"special\""
  }
}

Also, there is currently no way to get a nested value out of a JSON object stored in an entity property for the purposes of comparison in a condition.