Taigo is a driver written in GO for Taiga targeting to implement all publicly available API v1 endpoints.
Taiga is an Agile, Free and Open Source Project Management Tool.
Should you have any ideas or recommendations, feel free to report it as an issue or open a pull request.
For the most recent changes, see CHANGELOG.
- Some model members holding integers (i.e.
backlog_order
) were implemented usingint
instead ofint64
or ratheruint64
in some cases which results in runtime errors on machines with 32-bit arm CPUs.
Download Taigo:
go get github.com/theriverman/taigo
Import it into your project:
import (
taiga "github.com/theriverman/taigo"
)
Documentation is located at pkg.go.dev/Taigo.
To use Taigo, you must instantiate a *Client
, and authenticate against Taiga to either get a new token or validate the one given. To authenticate, use one of the following methods:
client.AuthByCredentials
client.AuthByToken
All Taiga objects, such as, Epic, User Story, Issue, Task, Sprint, etc.. are represented as a struct.
Since the API endpoints of Taiga return various payloads for the same object types, a meta system has been added
to simplify the interaction with the Taigo APIs.
For example, in Taiga the http://localhost:8000/api/v1/userstories
endpoint can return the following payload types:
- User story detail
- User story detail (GET)
- User story detail (LIST)
The returned payload type depends on the executed action: list, create, edit, get, etc..
Since Go does not support generics and casting between different types is not a trivial operation, a generic type has been introduced in Taigo for each implemented basic object type (Epic, Milestone, UserStory, Task, etc..) found in Taiga.
These generic types come with meta fields (pointers) to the originally returned payload. In case you need access to a field not represented in the generic type provided by Taigo, you can use the appropriate meta field.
For example, struct Epic
has the following meta fields:
type Epic struct {
ID int
// ...
EpicDetail *EpicDetail
EpicDetailGET *EpicDetailGET
EpicDetailLIST *EpicDetailLIST
}
The available meta field is always stated in the operation's docstring as Available Meta
, for example:
// CreateEpic => https://taigaio.github.io/taiga-doc/dist/api.html#epics-create
//
// Available Meta: *EpicDetail
func (s *EpicService) Create(epic Epic) (*Epic, error) {}
To avoid creating and maintaining a redundant API documentation for each covered Taiga API resource,
the API behaviours and fields are not documented in Taigo.
Instead, the docstring comment provides a direct URL to each appropriate online resource.
Example:
// CreateEpic => https://taigaio.github.io/taiga-doc/dist/api.html#epics-create
func (s *EpicService) Create(epic Epic) (*Epic, error) {}
Upon opening that link, you can find out what fields are available through *Epic
:
- project (required): project id
- subject (required)
To find out which operation requires what fields and what they return, always refer to the official Taiga REST API documentation.
It is rarely useful to receive results paginated in a driver like Taigo, so pagination is disabled by default.
To enable pagination (for all future requests) set Client.DisablePagination
to false
:
// Enable pagination (disabled by default)
client.DisablePagination(false)
If you enable pagination, you can collect pagination details by accessing the returned *http.Response
,
and extracting the relevant headers into a taigo.Pagination struct the following way:
// Collect returned Pagination headers
p := Pagination{}
p.LoadFromHeaders(client, response)
Note: See type Pagination struct
in TAIGO/common.models.go for more details.
For details on Taiga's pagination, see Taiga REST API / Pagination.
package main
import (
"fmt"
"net/http"
taiga "github.com/theriverman/taigo"
)
func main() {
// Create client
client := taiga.Client{
BaseURL: "https://api.taiga.io",
HTTPClient: &http.Client{},
}
// Authenticate (get/set Token)
if err := client.AuthByCredentials(&taiga.Credentials{
Type: "normal",
Username: "admin",
Password: "123123",
}); err != nil {
panic(err)
}
// Get /users/me
me, _ := client.User.Me()
fmt.Println("Me: (ID, Username, FullName)", me.ID, me.Username, me.FullName)
// Get Project (by its slug)
slug := "therivermantaigo-taigo-public-test"
fmt.Printf("Getting Project (slug=%s)..\n", slug)
project, err := client.Project.GetBySlug(slug)
if err != nil {
fmt.Println(err)
}
fmt.Printf("Project name: %s \n\n", project.Name)
}
// get all projects (no filtering, no ordering)
projectsList, _ := client.Project.List(nil)
for _, proj := range *projectsList {
fmt.Println("Project ID:", proj.ID, "Project Name:", proj.Name)
}
/*
ListProjects accepts a `*ProjectsQueryParameters` as an argument, but if you don't need any filtering, you can pass in a nil.
*/
// get all projects for user ID 1337
queryParams := taiga.Project.QueryParameters{
Member: 1337,
}
queryParams.TotalFansLastMonth() // results are ordered by TotalFansLastMonth
projectsList, _ := client.Project.List(&queryParams)
for _, proj := range *projectsList {
fmt.Println("Project ID:", proj.ID, "Project Name:", proj.Name)
}
// upload a file (Create an Epic attachment)
newAttachment, err := client.Epic.CreateAttachment(&taiga.Attachment{ObjectID: 1337, Project: 7331}, "C:/Users/theriverman/Pictures/nghfb.jpg")
- Clone Epic (with UserStories) -- To be implemented
- Clone Epic (without UserStories) -- ✔ Implemented
- Clone UserStory (with sub-tasks) -- To be implemented
- Clone UserStory (without sub-tasks) -- ✔ Implemented
- Clone Sub-Task -- To be implemented
- Copy UserStory to another project [will lose comments and attachments] -- To be implemented
Do you need access to a non yet implemented or special API endpoint? No problem!
It is possible to make requests to custom API endpoints by leveraging *RequestService
.
HTTP operations (GET
, POST
, etc..) should be executed through RequestService
which provides a managed environment for communicating with Taiga.
For example, let's try accessing the epic-custom-attributes
endpoint:
First, a model struct must be created to represent the data returned by the endpoint. Refer to the official Taiga API documentation for response examples:
// EpicCustomAttributeDetail -> https://taigaio.github.io/taiga-doc/dist/api.html#object-epic-custom-attribute-detail
// Converted via https://mholt.github.io/json-to-go/
type EpicCustomAttributeDetail struct {
CreatedDate time.Time `json:"created_date"`
Description string `json:"description"`
Extra interface{} `json:"extra"`
ID int `json:"id"`
ModifiedDate time.Time `json:"modified_date"`
Name string `json:"name"`
Order int `json:"order"`
Project int `json:"project"`
Type string `json:"type"`
}
Then initialise an instance which is going to store the response:
epicCustomAttributes := []EpicCustomAttributeDetail{}
Using client.MakeURL
the absolute URL endpoint is built for the request.
Using client.Request.Get
an HTTP GET request is sent to the API endpoint URL.
The client.Request.Get
call must receive a URL and a pointer to the model struct:
// Final URL should be https://api.taiga.io/api/v1/epic-custom-attributes
resp, err := client.Request.Get(client.MakeURL("epic-custom-attributes"), &epicCustomAttributes)
if err != nil {
fmt.Println(err)
fmt.Println(resp)
} else {
fmt.Println("The following Epic Custom Attributes have been returned:")
fmt.Println("--------------------------------------------------------")
for i := 0; i < 3; i++ {
ca := epicCustomAttributes[i]
fmt.Println(" * ", ca.ID, ca.Name)
}
}
Note: Such raw requests return (*http.Response, error)
.
The custom attribute of various Taiga objects are made of two major corner stones:
- Field Definitions
- Field Values
Field Definitions internally connect a field's name to an ID (int).
Requesting http://localhost:8000/api/v1/epic-custom-attributes/14
results in something like this:
{
"created_date": "2020-07-02T11:57:22.124Z",
"description": "nesciunt consectetur culpa ullam harum fugit veritatis eius dolorem assumenda",
"extra": null,
"id": 14,
"modified_date": "2020-07-03T08:40:34.667Z",
"name": "Duration 1",
"order": 1,
"project": 3,
"type": "url"
}
Field Values internally connect the user-provided unique values to the internal IDs.
Requesting http://localhost:8000/api/v1/epics/custom-attributes-values/15
results in something like this:
{
"attributes_values": {
"14": "240 min"
},
"epic": 15,
"version": 2
}
Observe that Duration 1
has ID 14
in the declaration and that's used as the field's name.
If you request the custom field values of a Taiga object, the values will be returned in a map[string]interface{}
style by default.
You have the option to manually declare the expected custom attribute fields and types in advance, and use your custom struct for serializing the returned values.
For example here's the custom epic custom attribute struct for Taigo's Sandbox Project:
import (
taiga "github.com/theriverman/taigo"
)
type TaigoSandboxEpicCustomAttributeFields struct {
SupportTeamName string `json:"9216"`
EstimatedDeliveryDate string `json:"9217"`
CostUSD int `json:"9218"`
}
type TaigoSandboxEpicCustomAttribValues struct {
taiga.EpicCustomAttributeValues
AttributesValues TaigoSandboxEpicCustomAttributeFields `json:"attributes_values,omitempty"`
}
Observe how the custom TaigoSandboxEpicCustomAttributeFields
is promoted in the custom TaigoSandboxEpicCustomAttribValues
struct.
All that's left is to create an instance of TaigoSandboxEpicCustomAttribValues
and use it to serialze the returned JSON:
cavs := TaigoSandboxEpicCustomAttribValues{} // Custom Attribute Value(s)
resp, err := client.Request.Get(client.MakeURL("epics", "custom-attributes-values", strconv.Itoa("your-epics-id")), &cavs)
if err != nil {
log.Println(err)
log.Println(resp)
return
}
Verbose mode can be enabled by setting the Verbose
field to true
in taiga.Client
. For example:
client := taiga.Client{
Verbose: true
}
You're contribution would be much appreciated!
Feel free to open Issue tickets or create Pull requests.
Should you have an idea which would cause non-backward compatibility, please open an issue first to discuss your proposal!
For more details, see TAIGO Contribution.
- master is always stable
- develop is not always stable
- feature_* branches are used for introducing larger changes
Product | License | Author |
---|---|---|
Go Gopher | CC3.0 | Renee French |
Gopher Konstructor | CC0-1.0 | quasilyte |
TAIGA | AGPL-3.0 | Taiga.io |
Taigo | MIT | theriverman |