Forem

Higor Diego
Higor Diego

Posted on

4 2

API do Jira com golang

Quem nunca esqueceu de apontar as horas das tarefas, né!?

Alt Text

Já esqueceu de apontar as horas no quadro do jira ?

Se sim, esse artigo é para você!

Pois depois que fizer a implementação desse código, se verá livre desse problema.

O que é Jira ?

Jira é um software comercial desenvolvido pela empresa Australiana Atlassian. É uma ferramenta que permite o monitoramento de tarefas e acompanhamento de projetos garantindo o gerenciamento de todas as suas atividades em único lugar. Wikipédia


As vezes esquecem de colocar aquelas horas trabalhada nos cards do jira e levamos aquela "chamada" do Scrum master no dia seguinte.

Para que isso não aconteça é preciso fazer a integração da chave de autenticação, como veremos a seguir:

Primeiramente acesse a conta da atlassian, logo após abrirá a tela abaixo:

alt text

No setor indicado acima clique em "criar e gerenciar tokens de API". Essa ação irá levar a tela abaixo:

alt text

Conforme orientado acima clique em criar token de API. Abrindo assim, a tela seguinte:

alt text

Na opção de login insira um nome para o acesso, e clique no botão criar. Indo então para tela abaixo:

alt text

Pronto! Estamos com a chave de acesso para consumir a API do jira.

Depois do acesso à chave, teremos que seguir alguns passos para chegar no relatório das horas, que são estes:

  • Identificar o id do sua conta no jira.
  • Listar todas suas tarefas abertas.
  • Contabilizar o que reportou ou não na data escolhida.

O jira disponibiliza uma documentação para o consumo dos recursos, clicando aqui.

Faremos o passo a passo no curl, para entender as chamadas e os seus retornos.

Para identificar a conta id precisamos requisitar a seguinte API:

curl -u seu_email_aqui:seu_token_aqui --location --request GET 'https://seu_dominio_aqui/rest/api/3/users/search'
Enter fullscreen mode Exit fullscreen mode

O retorno esperado da chamada:

[
  {
    "self": "https://seu_dominio_aqui/rest/api/3/user?accountId=aqui_vc_tem_seu_account_id",
    "accountId": "aqui_vc_tem_seu_account_id",
    "accountType": "atlassian",
    "emailAddress": "higor@example.com.br",
    "avatarUrls": {
      "48x48": "https://example.com/",
      "24x24": "https://example.com/",
      "16x16": "https://example.com/",
      "32x32": "https://example.com/"
    },
    "displayName": "Higor Diego",
    "active": true,
    "locale": "pt_BR"
  }
]
Enter fullscreen mode Exit fullscreen mode

Na resposta acima temos um accountId. Com isso, podemos analisar o retorno de todas as suas issues cadastradas por data.

Para pegar todas as suas issues com filtro de data e accountId chamaremos a seguinte API:


curl -u seu_email_aqui:seu_token_aqui --location --request POST 'https://seu_dominio_aqui/rest/api/3/search' \
--header 'Content-Type: application/json' \
--data-raw '{
    "jql": "worklogDate>='2021-01-15' and worklogDate<='2021-02-15' and (worklogAuthor in ('aqui_vc_tem_seu_account_id'))",
    "fields":["worklog"]
}'

Enter fullscreen mode Exit fullscreen mode

No corpo da Http Request enviamos JQL, que representa a Linguagem de Consulta do Jira.

O retorno esperado da chamada:

{
  "expand": "schema,names",
  "startAt": 0,
  "maxResults": 50,
  "total": 1,
  "issues": [
    {
      "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields",
      "id": "32620",
      "self": "https://seu_dominio_aqui/rest/api/3/issue/326212",
      "key": "key_project_aqui",
      "fields": {
        "worklog": {
          "startAt": 0,
          "maxResults": 20,
          "total": 1,
          "worklogs": [
            {
              "self": "https://seu_dominio_aqui/rest/api/3/issue/32620/worklog/326212",
              "author": {
                "self": "https://seu_dominio_aqui/rest/api/3/user?accountId=account_aqui",
                "accountId": "seu_acount_aqui",
                "avatarUrls": {
                  "48x48": "http://",
                  "24x24": "http://",
                  "16x16": "http://",
                  "32x32": "http://"
                },
                "displayName": "Higor Diego",
                "active": true,
                "timeZone": "America/Sao_Paulo",
                "accountType": "atlassian"
              },
              "updateAuthor": {
                "self": "https://seu_dominio_aqui/rest/api/3/user?accountId=account_aqui",
                "accountId": "account_aqui",
                "avatarUrls": {
                  "48x48": "http://",
                  "24x24": "http://",
                  "16x16": "http://",
                  "32x32": "http://"
                },
                "displayName": "Higor Diego",
                "active": true,
                "timeZone": "America/Sao_Paulo",
                "accountType": "atlassian"
              },
              "created": "2021-01-05T12:21:56.896-0300",
              "updated": "2021-01-05T12:21:56.896-0300",
              "started": "2021-01-05T12:16:53.769-0300",
              "timeSpent": "5m",
              "timeSpentSeconds": 300,
              "id": "5345021321323211",
              "issueId": "326212"
            }
          ]
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Com base no resultado das chamadas de API, vamos agora codificar o alerta em Go.


Criaremos um arquivo chamado helper para nos auxiliares nas seguintes etapas:

  • Pegar a data atual;
  • Formatar a data;
  • Criar o base64 para autenticação Basic;
  • Converter segundos em horas.
package helpers

import (
    "encoding/base64"
    "fmt"
    "time"
)

// BasicAuth - create basic authenticate
func BasicAuth(email, token string) string {
    auth := email + ":" + token
    return fmt.Sprintf("Basic %v", base64.StdEncoding.EncodeToString([]byte(auth)))
}

// NowDate - func date now parse to string
func NowDate() string {
    t := time.Now()
    return FormatDate(t)
}

// FormatDate - Parse data to string
func FormatDate(t time.Time) string {
    return fmt.Sprintf("%d-%02d-%02d", t.Year(), t.Month(), t.Day())
}

func ConvertHour(value float64) float64 {
    return value / 3600
}
Enter fullscreen mode Exit fullscreen mode

Agora faremos um arquivo chamado integration para nos auxiliares na request http.

package integration

import (
    "io/ioutil"
    "net/http"
    "strings"
    "time"
)


const (
    jiraReport = "https://seu_dominio_aqui/rest/api/2/search"
)


// Jira - struct
type Jira struct {
    Hours int
}

// ResponseJiraIssue - struct
type ResponseJiraIssue struct {
    Expand string `json:"expand"`
    Issues []struct {
        Expand string `json:"expand"`
        Fields struct {
            Worklog struct {
                MaxResults int64 `json:"maxResults"`
                StartAt    int64 `json:"startAt"`
                Total      int64 `json:"total"`
                Worklogs   []struct {
                    Author struct {
                        AccountID   string `json:"accountId"`
                        AccountType string `json:"accountType"`
                        Active      bool   `json:"active"`
                        AvatarUrls  struct {
                            One6x16   string `json:"16x16"`
                            Two4x24   string `json:"24x24"`
                            Three2x32 string `json:"32x32"`
                            Four8x48  string `json:"48x48"`
                        } `json:"avatarUrls"`
                        DisplayName string `json:"displayName"`
                        Self        string `json:"self"`
                        TimeZone    string `json:"timeZone"`
                    } `json:"author"`
                    Comment struct {
                        Content []struct {
                            Content []struct {
                                Text string `json:"text"`
                                Type string `json:"type"`
                            } `json:"content"`
                            Type string `json:"type"`
                        } `json:"content"`
                        Type    string `json:"type"`
                        Version int64  `json:"version"`
                    } `json:"comment"`
                    Created          string `json:"created"`
                    ID               string `json:"id"`
                    IssueID          string `json:"issueId"`
                    Self             string `json:"self"`
                    Started          string `json:"started"`
                    TimeSpent        string `json:"timeSpent"`
                    TimeSpentSeconds int64  `json:"timeSpentSeconds"`
                    UpdateAuthor     struct {
                        AccountID   string `json:"accountId"`
                        AccountType string `json:"accountType"`
                        Active      bool   `json:"active"`
                        AvatarUrls  struct {
                            One6x16   string `json:"16x16"`
                            Two4x24   string `json:"24x24"`
                            Three2x32 string `json:"32x32"`
                            Four8x48  string `json:"48x48"`
                        } `json:"avatarUrls"`
                        DisplayName string `json:"displayName"`
                        Self        string `json:"self"`
                        TimeZone    string `json:"timeZone"`
                    } `json:"updateAuthor"`
                    Updated string `json:"updated"`
                } `json:"worklogs"`
            } `json:"worklog"`
        } `json:"fields"`
        ID   string `json:"id"`
        Key  string `json:"key"`
        Self string `json:"self"`
    } `json:"issues"`
    MaxResults int64 `json:"maxResults"`
    StartAt    int64 `json:"startAt"`
    Total      int64 `json:"total"`
}


func mountedHttp (url, authorization, method string, body *strings.Reader) (*http.Response, error) {
    timeout := 5 * time.Second

    client := http.Client{
        Timeout: timeout,
    }
    request, err := http.NewRequest(method, url, body)


    if err != nil {
        return nil, err
    }

    request.Header.Set("Content-Type", "application/json")
    request.Header.Set("Authorization", authorization)

    response, e := client.Do(request)

    if e != nil {
        return nil, e
    }

    return response, nil
}

// RequestHttpJiraReport - chamada http para request de issues do jira.
func RequestHttpJiraReport (authorization string, body *strings.Reader) ([]byte, error) {

    response, err := mountedHttp(jiraReport, authorization, "POST", body)

    if err != nil {
        return nil, err
    }

    defer response.Body.Close()


    data, er := ioutil.ReadAll(response.Body)

    if er != nil {
        return nil, er
    }

    return data, nil
}
Enter fullscreen mode Exit fullscreen mode

Então criaremos um arquivo main para chamar todas as nossas funções, e revelar quantas horas foram feitas no dia.

package main

import (
    "encoding/json"
    "fmt"
    "github.com/higordiego/jira-tutorial/helpers"
    "github.com/higordiego/jira-tutorial/integration"
    "strings"
)

const (
    email = "seu_email_aqui"
    token = "token_aqui"
    accountID = "account_id"
)

func main() {

    authorization := helpers.BasicAuth(email, token)
    equalData := helpers.NowDate()

    var jiraResponse integration.ResponseJiraIssue

    jql := fmt.Sprintf(`{"jql": "worklogDate>='%v' and worklogDate<='%v' and (worklogAuthor in ('%v'))", "fields":["worklog"] }`, equalData, equalData, accountID)

    payload := strings.NewReader(jql)

    result, err := integration.RequestHttpJiraReport(authorization, payload)

    if err != nil {
        panic(err.Error())
    }

    er := json.Unmarshal(result, &jiraResponse)

    if er != nil {
        panic(er.Error())
    }

    var count = 0.0
    for _, r := range jiraResponse.Issues {
        for _, a := range r.Fields.Worklog.Worklogs {
            count += float64(a.TimeSpentSeconds)
        }
    }

    fmt.Printf("Suas horas trabalhadas: %.2f", helpers.ConvertHour(count))
}
Enter fullscreen mode Exit fullscreen mode

E por fim, teremos o seguinte resultado no console:

Suas horas trabalhadas: 0.5

Com esse resultado podemos introduzir varias formas de aviso. Por exemplo:

  • Envio de e-mail;
  • Envio de mensagem por Slack ou WhatsApp;
  • Alerta no Desktop.

Links úteis:

JQL: comece a usar a pesquisa avançada no Jira | Atlassian

The Go Programming Language (golang.org)

The Jira Cloud platform REST API (atlassian.com)

Autenticação HTTP — HTTP | MDN (mozilla.org)

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (0)