Skip to content
This repository has been archived by the owner on Jan 9, 2025. It is now read-only.

Commit

Permalink
feat: add Jira component (#205)
Browse files Browse the repository at this point in the history
Because

- We need a Jira component to complete some development automation tasks
- Related Issue: instill-ai/instill-core#1022

This commit

- Support get boards/issue/epic/sprint

TODO
- [x] TASK_LIST_BOARDS: list all boards in Jira
- [x] TASK_LIST_ISSUES: list issues in Jira by various methods
- [x] TASK_GET_ISSUE: get an issue by ID or key
- Note: This API can get issue/epic with the key since they use the same
indexing system.
- [x] TASK_GET_SPRINT: get a sprint by ID

Next PR
- create issue
- create epic
- create sprint

Added by @chuang8511 to visualise Jira's design

![image](https://github.com/user-attachments/assets/17ff0aaf-5ab0-458c-95cd-a147f12e314a)

---------

Co-authored-by: Chang, Hui-Tang <[email protected]>
  • Loading branch information
YCK1130 and donch1989 authored Jul 28, 2024
1 parent 810f850 commit 51f3ed7
Show file tree
Hide file tree
Showing 17 changed files with 3,435 additions and 1 deletion.
186 changes: 186 additions & 0 deletions application/jira/v0/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
---
title: "Jira"
lang: "en-US"
draft: false
description: "Learn about how to set up a VDP Jira component https://github.com/instill-ai/instill-core"
---

The Jira component is an application component that allows users to do anything available on Jira.
It can carry out the following tasks:

- [List Boards](#list-boards)
- [List Issues](#list-issues)
- [List Sprints](#list-sprints)
- [Get Issue](#get-issue)
- [Get Sprint](#get-sprint)



## Release Stage

`Alpha`



## Configuration

The component configuration is defined and maintained [here](https://github.com/instill-ai/component/blob/main/application/jira/v0/config/definition.json).




## Setup


| Field | Field ID | Type | Note |
| :--- | :--- | :--- | :--- |
| Token (required) | `token` | string | Fill in your Jira API token. You can generate one from your Jira account "settings > security > API tokens". |
| Base URL (required) | `base-url` | string | Fill in your Jira base URL. For example, if your Jira URL is https://mycompany.atlassian.net, then your base URL is https://mycompany.atlassian.net. |
| Email (required) | `email` | string | Fill in your Jira email address. |




## Supported Tasks

### List Boards

List all boards in Jira


| Input | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Task ID (required) | `task` | string | `TASK_LIST_BOARDS` |
| Project Key or ID | `project-key-or-id` | string | This filters results to boards that are relevant to a project. Relevance meaning that the JQL filter defined in board contains a reference to a project. |
| Board Type | `board-type` | string | The type of board, can be: scrum, kanban, simple. Default is simple |
| Name | `name` | string | Name filters results to boards that match or partially match the specified name. Default is empty |
| Start At | `start-at` | integer | The starting index of the returned boards. Base index: 0. Default is 0 |
| Max Results | `max-results` | integer | The maximum number of boards to return. Default is 50 |



| Output | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Boards (optional) | `boards` | array[object] | A array of boards in Jira |
| Start At | `start-at` | integer | The starting index of the returned boards. Base index: 0 |
| Max Results | `max-results` | integer | The maximum number of boards |
| Total | `total` | integer | The total number of boards |
| Is Last | `is-last` | boolean | Whether the last board is reached |






### List Issues

List issues in Jira


| Input | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Task ID (required) | `task` | string | `TASK_LIST_ISSUES` |
| Board ID (required) | `board-id` | integer | The ID of the board |
| Range | `range` | object | Choose the range of issues to return. Default is `all` |
| Start At | `start-at` | integer | The starting index of the returned boards. Base index: 0. Default is 0 |
| Max Results | `max-results` | integer | The maximum number of boards to return. Default is 50 |



| Output | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Issues (optional) | `issues` | array[object] | A array of issues in Jira |
| Start At | `start-at` | integer | The starting index of the returned boards. Base index: 0 |
| Max Results | `max-results` | integer | The maximum number of boards |
| Total | `total` | integer | The total number of boards |






### List Sprints

List sprints in Jira


| Input | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Task ID (required) | `task` | string | `TASK_LIST_SPRINTS` |
| Board ID (required) | `board-id` | integer | The ID of the board |
| Start At | `start-at` | integer | The starting index of the returned boards. Base index: 0. Default is 0 |
| Max Results | `max-results` | integer | The maximum number of boards to return. Default is 50 |



| Output | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Sprints (optional) | `sprints` | array[object] | A array of sprints in Jira |
| Start At | `start-at` | integer | The starting index of the returned boards. Base index: 0 |
| Max Results | `max-results` | integer | The maximum number of boards |
| Total | `total` | integer | The total number of boards |






### Get Issue

Get an issue in Jira


| Input | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Task ID (required) | `task` | string | `TASK_GET_ISSUE` |
| Issue ID or Key (required) | `issue-id-or-key` | string | The ID or key of the issue |
| Update History | `update-history` | boolean | Whether the project in which the issue is created is added to the user's Recently viewed project list, as shown under Projects in Jira. |



| Output | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| ID | `id` | string | The ID of the issue |
| Key | `key` | string | The key of the issue |
| Self | `self` | string | The URL of the issue |
| Fields | `fields` | object | The fields of the issue. All navigable and Agile fields are returned |
| Issue Type (optional) | `issue-type` | string | The type of the issue, can be: `Task`, `Epic` |
| Summary (optional) | `summary` | string | The summary of the issue |
| Description (optional) | `description` | string | The description of the issue |
| Status (optional) | `status` | string | The status of the issue, can be: `To Do`, `In Progress`, `Done` |






### Get Sprint

Get a sprint in Jira


| Input | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| Task ID (required) | `task` | string | `TASK_GET_SPRINT` |
| Sprint ID (required) | `sprint-id` | integer | The ID of the sprint. The sprint will only be returned if you can view the board that the sprint was created on, or view at least one of the issues in the sprint. |



| Output | ID | Type | Description |
| :--- | :--- | :--- | :--- |
| ID (optional) | `id` | integer | The ID of the sprint |
| Self (optional) | `self` | string | The URL of the sprint |
| State (optional) | `state` | string | The state of the sprint, can be: `active`, `closed`, `future` |
| Name (optional) | `name` | string | The name of the sprint |
| Start Date (optional) | `start-date` | string | The start date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z |
| End Date (optional) | `end-date` | string | The end date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z |
| Complete Date (optional) | `complete-date` | string | The complete date of the sprint. In the RFC3339 format, e.g. 2018-03-05T00:00:00Z |
| Origin Board ID (optional) | `origin-board-id` | integer | The ID of the origin board |
| Goal (optional) | `goal` | string | The Goal of the sprint |







15 changes: 15 additions & 0 deletions application/jira/v0/assets/jira.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 109 additions & 0 deletions application/jira/v0/boards.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package jira

import (
"context"
_ "embed"
"fmt"

"github.com/instill-ai/component/base"
"github.com/instill-ai/x/errmsg"
"google.golang.org/protobuf/types/known/structpb"
)

type Board struct {
ID int `json:"id"`
Name string `json:"name"`
Self string `json:"self"`
BoardType string `json:"type"`
}

type ListBoardsInput struct {
ProjectKeyOrID string `json:"project-key-or-id,omitempty" api:"projectKeyOrId"`
BoardType string `json:"board-type,omitempty" api:"type"`
Name string `json:"name,omitempty" api:"name"`
StartAt int `json:"start-at,omitempty" api:"startAt"`
MaxResults int `json:"max-results,omitempty" api:"maxResults"`
}
type ListBoardsResp struct {
Values []Board `json:"values"`
StartAt int `json:"startAt"`
MaxResults int `json:"maxResults"`
Total int `json:"total"`
IsLast bool `json:"isLast"`
}

type ListBoardsOutput struct {
Boards []Board `json:"boards"`
StartAt int `json:"start-at"`
MaxResults int `json:"max-results"`
Total int `json:"total"`
IsLast bool `json:"is-last"`
}

func (jiraClient *Client) listBoardsTask(ctx context.Context, props *structpb.Struct) (*structpb.Struct, error) {
var opt ListBoardsInput
if err := base.ConvertFromStructpb(props, &opt); err != nil {
return nil, err
}

boards, err := jiraClient.listBoards(ctx, &opt)
if err != nil {
return nil, err
}
var output ListBoardsOutput
output.Boards = append(output.Boards, boards.Values...)
if output.Boards == nil {
output.Boards = []Board{}
}
output.StartAt = boards.StartAt
output.MaxResults = boards.MaxResults
output.IsLast = boards.IsLast
output.Total = boards.Total
return base.ConvertToStructpb(output)
}

func (jiraClient *Client) listBoards(_ context.Context, opt *ListBoardsInput) (*ListBoardsResp, error) {
apiEndpoint := "rest/agile/1.0/board"

req := jiraClient.Client.R().SetResult(&ListBoardsResp{})
err := addQueryOptions(req, *opt)
if err != nil {
return nil, err
}
resp, err := req.Get(apiEndpoint)

if err != nil {
return nil, err
}
boards := resp.Result().(*ListBoardsResp)
return boards, err
}

type GetBoardResp struct {
Location struct {
DisplayName string `json:"displayName"`
Name string `json:"name"`
ProjectKey string `json:"projectKey"`
ProjectID int `json:"projectId"`
ProjectName string `json:"projectName"`
ProjectTypeKey string `json:"projectTypeKey"`
UserAccountID string `json:"userAccountId"`
UserID string `json:"userId"`
} `json:"location"`
Board
}

func (jiraClient *Client) getBoard(_ context.Context, boardID int) (*GetBoardResp, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)

req := jiraClient.Client.R().SetResult(&GetBoardResp{})
resp, err := req.Get(apiEndpoint)
if err != nil {
return nil, fmt.Errorf(
err.Error(), errmsg.Message(err),
)
}
result := resp.Result().(*GetBoardResp)

return result, err
}
Loading

0 comments on commit 51f3ed7

Please sign in to comment.