Skip to main content

Check out Port for yourself ➜ 

Jira

Loading version...

Port's Jira Cloud integration allows you to model Jira Cloud resources in your software catalog and ingest data into them.

Supported resources ProjectUserIssueTeam
Jira Cloud integration

This integration is designed for Jira Cloud. For Jira Server (self-hosted), use Port's Jira Server Ocean integration.

Setup

Choose one of the following installation methods: Not sure which method is right for your use case? Check the available installation methods.

Configuration

Port integrations use a YAML mapping block to ingest data from the third-party api into Port.

The mapping makes use of the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from the integration API.

Default mapping configuration

This is the default mapping configuration for this integration:

Default mapping configuration (click to expand)
deleteDependentEntities: true
createMissingRelatedEntities: true
enableMergeEntity: true
resources:
- kind: user
selector:
query: 'true'
port:
entity:
mappings:
identifier: .accountId
title: .displayName
blueprint: '"jiraUser"'
properties:
emailAddress: .emailAddress
displayName: .displayName
active: .active
accountType: .accountType
- kind: project
selector:
query: 'true'
port:
entity:
mappings:
identifier: .key
title: .name
blueprint: '"jiraProject"'
properties:
url: (.self | split("/") | .[:3] | join("/")) + "/projects/" + .key
- kind: issue
selector:
query: 'true'
jql: (statusCategory != Done) OR (created >= -1w) OR (updated >= -1w)
port:
entity:
mappings:
identifier: .key
title: .fields.summary
blueprint: '"jiraIssue"'
properties:
url: (.self | split("/") | .[:3] | join("/")) + "/browse/" + .key
status: .fields.status.name
issueType: .fields.issuetype.name
components: .fields.components
creator: .fields.creator.emailAddress
priority: .fields.priority.name
labels: .fields.labels
created: .fields.created
updated: .fields.updated
resolutionDate: .fields.resolutiondate
relations:
project: .fields.project.key
parentIssue: .fields.parent.key
subtasks: .fields.subtasks | map(.key)
jira_user_assignee: .fields.assignee.accountId
jira_user_reporter: .fields.reporter.accountId
assignee:
combinator: '"or"'
rules:
- property: '"jira_user_id"'
operator: '"="'
value: .fields.assignee.accountId
- property: '"$identifier"'
operator: '"="'
value: .fields.assignee.email
reporter:
combinator: '"or"'
rules:
- property: '"jira_user_id"'
operator: '"="'
value: .fields.reporter.accountId
- property: '"$identifier"'
operator: '"="'
value: .fields.reporter.email
- kind: issue
selector:
query: 'true'
jql: (statusCategory != Done) OR (created >= -1w) OR (updated >= -1w)
port:
entity:
mappings:
identifier: .key
title: .fields.summary
blueprint: '"jiraIssue"'
relations:
pull_request:
combinator: '"and"'
rules:
- property: '"$title"'
operator: '"contains"'
value: .key

JQL support for issues

The Ocean Jira integration supports querying objects from the issue kind using JQL, making it possible to specifically filter the issues that are queried from Jira and ingested to Port.

To use JQL filtering, add to the selector object a jql key with your desired JQL query as the value. For example:

resources:
- kind: issue # JQL filtering can only be used with the "issue" kind
selector:
query: "true" # JQ boolean expression. If evaluated to false - this object will be skipped.
jql: "status != Done" # JQL query, will only ingest issues whose status is not "Done"
port:

Fields support for issue kind

The Jira integration allows you to customize what fields are available for ingestion using the fields selector. The fields selector accepts a comma-separated string containing what fields are available. Possible values are *all, *navigate, <field_name> and -<field_name> (specifically for excluding fields).

The default value is set to *all which makes all fields available by default.

More details what values the field selector allows is available on Jira's issue API documentation.

Ingesting Sprint field for issue kind

By default, the Jira integration does not include information on the issues' sprint. To ingest sprint information, a custom field must be added to issues to display sprints. This custom field is then included in the Jira issues mapping configuration.

Follow the steps below:

  1. Add Sprint as a custom field to Jira issue on your Jira dashboard. Take note of the custom field ID for the sprints field. This ID can be gotten by calling the fields API to first retreive the list of fields for issues which returns a payload like so:

    Issues field API response (click to expand)
    [
    {
    "id": "thumbnail",
    "key": "thumbnail",
    "name": "Images",
    "custom": false,
    "orderable": false,
    "navigable": true,
    "searchable": false,
    "clauseNames": []
    },
    {
    "id": "created",
    "key": "created",
    "name": "Created",
    "custom": false,
    "orderable": false,
    "navigable": true,
    "searchable": true,
    "clauseNames": [
    "created",
    "createdDate"
    ],
    "schema": {
    "type": "datetime",
    "system": "created"
    }
    },
    {
    "id": "customfield_11111",
    "key": "customfield_11111",
    "name": "Sprint",
    "untranslatedName": "Sprint",
    "custom": true,
    "orderable": true,
    "navigable": true,
    "searchable": true,
    "clauseNames": [
    "cf[11111]",
    "Sprint"
    ],
    "schema": {
    "type": "array",
    "items": "json",
    "custom": "com.pyxis.greenhopper.jira:gh-sprint",
    "customId": 11111
    }
    }
    ]

    Locate the Sprint field and take note of the ID - in this case, customfield_11111.

  2. Extend the default blueprint to include a field for sprint:

    Issue blueprint with sprint (click to expand)
    {
    "identifier": "jiraIssue",
    "title": "Jira Issue",
    "icon": "Jira",
    "schema": {
    "properties": {
    "url": {
    "title": "Issue URL",
    "type": "string",
    "format": "url",
    "description": "URL to the issue in Jira"
    },
    "status": {
    "title": "Status",
    "type": "string",
    "description": "The status of the issue"
    },
    "issueType": {
    "title": "Type",
    "type": "string",
    "description": "The type of the issue"
    },
    "components": {
    "title": "Components",
    "type": "array",
    "description": "The components related to this issue"
    },
    "assignee": {
    "title": "Assignee",
    "type": "string",
    "format": "user",
    "description": "The user assigned to the issue"
    },
    "reporter": {
    "title": "Reporter",
    "type": "string",
    "description": "The user that reported to the issue",
    "format": "user"
    },
    "creator": {
    "title": "Creator",
    "type": "string",
    "description": "The user that created to the issue",
    "format": "user"
    },
    "priority": {
    "title": "Priority",
    "type": "string",
    "description": "The priority of the issue"
    },
    "sprint": {
    "title": "Sprint",
    "type": "string",
    "description": "The last sprint this issue belongs to"
    },
    "created": {
    "title": "Created At",
    "type": "string",
    "description": "The created datetime of the issue",
    "format": "date-time"
    },
    "updated": {
    "title": "Updated At",
    "type": "string",
    "description": "The updated datetime of the issue",
    "format": "date-time"
    }
    }
    },
    "calculationProperties": {
    "handlingDuration": {
    "title": "Handling Duration (Days)",
    "icon": "Clock",
    "description": "The amount of time in days from issue creation to issue resolution",
    "calculation": "if (.properties.resolutionDate != null and .properties.created != null) then ((.properties.resolutionDate[0:19] + \"Z\" | fromdateiso8601) - (.properties.created[0:19] + \"Z\" | fromdateiso8601)) / 86400 else null end",
    "type": "number"
    }
    },
    "mirrorProperties": {},
    "aggregationProperties": {},
    "relations": {
    "project": {
    "target": "jiraProject",
    "title": "Project",
    "description": "The Jira project that contains this issue",
    "required": false,
    "many": false
    },
    "parentIssue": {
    "target": "jiraIssue",
    "title": "Parent Issue",
    "required": false,
    "many": false
    },
    "subtasks": {
    "target": "jiraIssue",
    "title": "Subtasks",
    "required": false,
    "many": true
    },
    "assignee": {
    "target": "jiraUser",
    "title": "Assignee",
    "required": false,
    "many": false
    },
    "reporter": {
    "target": "jiraUser",
    "title": "Reporter",
    "required": false,
    "many": false
    }
    }
    }

    Sprints field in issues payload response

    On the issue response returned by calling the Jira issues API, sprints is returned as an array of sprints:

    Sprints field in Jira API response (click to expand)
    {
    . . .,
    "customfield_11111": [
    {
    "id": 37,
    "name": "Sprint 32",
    "state": "closed",
    "boardId": 1,
    "goal": "",
    "startDate": "2024-07-08T11:59:07.316Z",
    "endDate": "2024-07-28T21:00:00.000Z",
    "completeDate": "2024-07-29T14:04:34.397Z"
    },
    {
    "id": 38,
    "name": "Sprint 33",
    "state": "closed",
    "boardId": 1,
    "goal": "",
    "startDate": "2024-07-29T14:05:25.295Z",
    "endDate": "2024-08-18T23:06:20.000Z",
    "completeDate": "2024-08-20T09:19:48.396Z"
    },
    {
    "id": 40,
    "name": "Sprint 34",
    "state": "closed",
    "boardId": 1,
    "goal": "",
    "startDate": "2024-08-20T09:20:27.259Z",
    "endDate": "2024-09-08T20:30:00.000Z",
    "completeDate": "2024-09-10T13:50:01.397Z"
    },
    {
    "id": 42,
    "name": "Sprint 35",
    "state": "active",
    "boardId": 1,
    "goal": "",
    "startDate": "2024-09-10T13:51:44.000Z",
    "endDate": "2024-10-15T01:01:00.000Z"
    }
    ],
    . . .
    }

    For the purpose of this guide, we are simply retrieving the ID of the latest sprint.

  3. Add the mapping configuration for the sprint field:

    Issue with sprint field mapping configuration (click to expand)
    createMissingRelatedEntities: true
    deleteDependentEntities: true
    resources:
    - kind: issue
    selector:
    query: "true"
    jql: "statusCategory != Done"
    port:
    entity:
    mappings:
    identifier: .key
    title: .fields.summary
    blueprint: '"jiraIssue"'
    properties:
    url: (.self | split("/") | .[:3] | join("/")) + "/browse/" + .key
    status: .fields.status.name
    issueType: .fields.issuetype.name
    components: .fields.components
    assignee: .fields.assignee.emailAddress
    reporter: .fields.reporter.emailAddress
    creator: .fields.creator.emailAddress
    priority: .fields.priority.name
    sprint: .fields.customfield_11111[-1].name // ""
    created: .fields.created
    updated: .fields.updated
    relations:
    project: .fields.project.key
    parentIssue: .fields.parent.key
    subtasks: .fields.subtasks | map(.key)

    Where customfield_11111 is your sprint field.

  4. Click on Resync and watch sprints information being pulled alongside issues data.

Ingesting issues based on the current sprint

Ingesting only issues from the current sprint can be done by combining the sprint property with selector magic:

Issue from current sprint (click to expand)
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: issue
selector:
query: .fields.customfield_11111[-1].name == "Sprint 35" # Replace "Sprint 35" with the name of the current sprint
jql: "statusCategory != Done"
port:
entity:
mappings:
identifier: .key
title: .fields.summary
blueprint: '"jiraIssue"'
properties:
url: (.self | split("/") | .[:3] | join("/")) + "/browse/" + .key
status: .fields.status.name
issueType: .fields.issuetype.name
components: .fields.components
assignee: .fields.assignee.emailAddress
reporter: .fields.reporter.emailAddress
creator: .fields.creator.emailAddress
priority: .fields.priority.name
sprint: .fields.customfield_11111 | sort_by(.id) | .[-1].name // ""
created: .fields.created
updated: .fields.updated
relations:
project: .fields.project.key
parentIssue: .fields.parent.key
subtasks: .fields.subtasks | map(.key)
Issues with blank Sprint values

If the createMissingRelatedEntities is set to true, issues with blank Sprint values and some empty/null properties will be created in Port. To avoid this, set createMissingRelatedEntities to false.

Mapping & examples per resource

Use the explorer below to view sample payloads and the resulting Port entities for each resource type. For additional resources and advanced configurations, see the examples page.

Monitoring and sync status

To learn more about how to monitor and check the sync status of your integration, see the relevant documentation.

Limitations

Getting user emails

By default, Jira does not include user emails in API responses. To enable this:

  1. Go to the Jira admin panel and verify your domain under Settings > Domains.
  2. After verification, claim your user accounts under the same Domains section.

Once complete, Jira API responses will include the emailAddress field. See Jira's documentation for full details.

Examples

To view and test the integration's mapping against examples of the third-party API responses, use the jq playground in your data sources page. Find the integration in the list of data sources and click on it to open the playground.

Additional examples of blueprints and the relevant integration configurations can be found on the jira examples page

Relevant Guides

For relevant guides and examples, see the guides section.