Giter Club home page Giter Club logo

trello's Introduction

Go Trello API

Trello Logo

GoDoc Build Status Coverage Status

A #golang package to access the Trello API. Nearly 100% of the read-only surface area of the API is covered, as is creation and modification of Cards. Low-level infrastructure for features to modify Lists and Boards are easy to add... just not done yet.

Pull requests are welcome for missing features.

My current development focus is documentation, especially enhancing this README.md with more example use cases.

Installation

The Go Trello API has been Tested compatible with Go 1.7 on up. Its only dependency is the github.com/pkg/errors package. It otherwise relies only on the Go standard library.

go get github.com/adlio/trello

Basic Usage

All interaction starts with a trello.Client. Create one with your appKey and token:

client := trello.NewClient(appKey, token)

All API requests accept a trello.Arguments object. This object is a simple map[string]string, converted to query string arguments in the API call. Trello has sane defaults on API calls. We have a trello.Defaults() utility function which can be used when you desire the default Trello arguments. Internally, trello.Defaults() is an empty map, which translates to an empty query string.

board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
  // Handle error
}

Client Longevity

When getting Lists from Boards or Cards from Lists, the original trello.Client pointer is carried along in returned child objects. It's important to realize that this enables you to make additional API calls via functions invoked on the objects you receive:

client := trello.NewClient(appKey, token)
board, err := client.GetBoard("ID", trello.Defaults())

// GetLists makes an API call to /boards/:id/lists using credentials from `client`
lists, err := board.GetLists(trello.Defaults())

for _, list := range lists {
  // GetCards makes an API call to /lists/:id/cards using credentials from `client`
  cards, err := list.GetCards(trello.Defaults())
}

Get Trello Boards for a User

Boards can be retrieved directly by their ID (see example above), or by asking for all boards for a member:

member, err := client.GetMember("usernameOrId", trello.Defaults())
if err != nil {
  // Handle error
}

boards, err := member.GetBoards(trello.Defaults())
if err != nil {
  // Handle error
}

Get Trello Lists on a Board

board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
  // Handle error
}

lists, err := board.GetLists(trello.Defaults())
if err != nil {
  // Handle error
}

Get Trello Cards on a Board

board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
  // Handle error
}

cards, err := board.GetCards(trello.Defaults())
if err != nil {
  // Handle error
}

Get Trello Cards on a List

list, err := client.GetList("lIsTID", trello.Defaults())
if err != nil {
  // Handle error
}

cards, err := list.GetCards(trello.Defaults())
if err != nil {
  // Handle error
}

Creating and deleting a Board

A board can be created or deleted on the Board struct for the user whose credentials are being used.

  board := trello.NewBoard("My bucket list")

  // POST
  err := client.CreateBoard(&board, trello.Defaults())

  // DELETE
  err := board.Delete(trello.Defaults())
  if err != nil {
    fmt.Println(err)
  }
}

Adding a new Member to the Board

  board, err := client.GetBoard("bOaRdID", trello.Defaults())
  if err != nil {
    // Handle error
  }

  member := Member{Email: "[email protected]"}
  _, err := board.AddMember(&member, trello.Defaults()) 

  if err != nil {
    // Handle error
  }

Archiving, Unarchiving & Deleting a Card

// archive
err := card.Archive()

// unarchive
err := card.Unarchive()

// delete
err := card.Delete()

Creating a Card

The API provides several mechanisms for creating new cards.

Creating Cards from Scratch on the Client

This approach requires the most input data on the card:

card := trello.Card{
  Name: "Card Name",
  Desc: "Card description",
  Pos: 12345.678,
  IDList: "iDOfaLiSt",
  IDLabels: []string{"labelID1", "labelID2"},
}
err := client.CreateCard(card, trello.Defaults())

Creating Cards On a List

list, err := client.GetList("lIsTID", trello.Defaults())
list.AddCard(&trello.Card{ Name: "Card Name", Desc: "Card description" }, trello.Defaults())

Creating a Card by Copying Another Card

err := card.CopyToList("listIdNUmber", trello.Defaults())

Get Actions on a Board

board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
  // Handle error
}

actions, err := board.GetActions(trello.Defaults())
if err != nil {
  // Handle error
}

Get Actions on a Card

card, err := client.GetCard("cArDID", trello.Defaults())
if err != nil {
  // Handle error
}

actions, err := card.GetActions(trello.Defaults())
if err != nil {
  // Handle error
}

Rearrange Cards Within a List

err := card.MoveToTopOfList()
err = card.MoveToBottomOfList()
err = card.SetPos(12345.6789)

Moving a Card to Another List

err := card.MoveToList("listIdNUmber", trello.Defaults())

Card Ancestry

Trello provides ancestry tracking when cards are created as copies of other cards. This package provides functions for working with this data:

// ancestors will hold a slice of *trello.Cards, with the first
// being the card's parent, and the last being parent's parent's parent...
ancestors, err := card.GetAncestorCards(trello.Defaults())

// GetOriginatingCard() is an alias for the last element in the slice
// of ancestor cards.
ultimateParentCard, err := card.GetOriginatingCard(trello.Defaults())

Debug Logging

If you'd like to see all API calls logged, you can attach a .Logger (implementing Debugf(string, ...interface{})) to your client. The interface for the logger mimics logrus. Example usage:

logger := logrus.New()
logger.SetLevel(logrus.DebugLevel)
client := trello.NewClient(appKey, token)
client.Logger = logger

trello's People

Contributors

adlio avatar almogbaku avatar crevil avatar derveloper avatar enrico5b1b4 avatar eyjhb avatar fabstu avatar francoishill avatar g-rath avatar heinrichgrt avatar jokaorgua avatar jtsaito avatar justinfarrelldev avatar kolaente avatar mangelajo avatar mauromsl avatar mcasper avatar mehanizm avatar mikelu92 avatar msamoylov avatar n7st avatar rhardih avatar rukenshia avatar spenserpothier avatar surminus avatar temamagic avatar tjm avatar utkuufuk avatar wcarmon avatar xescugc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

trello's Issues

Attachments[] is always empty even if there is attachments

Using the latest release v1.9.0, hopefully I'm just doing something wrong but I have a giant list of cards all with attachments and after doing GetCards() from a list it seems the attachments array in all of them are empty. Is there possibly something I'm missing here I know all the cards have attachments and the rest of the array data is there not sure what else to try here.

invite with link?

I can't seem to find it anywhere in the API docs.. does anyone know how to automate the "invite with link" process? I am creating and populating a board, but someone still has to manually generate the link on their browser.

Custom fields

I was playing around with your lib and found out I could not use custom fields (a trello Power Up). So I forked your repo and did a rough implementation.

Do you think it would be interesting for me to fine tune it and make a PR to adlio/trello or that power up functionality does not really belong here?

In any case thank you for sharing this lib.

Use `args ...Arguments` instead of `args Arguments`?

Hi, I found your module when trying to "publish" TJM/go-trello (fork from VojtechVitek/go-trello ) that I hacked on for several days. I like a lot of the things you have done here, and I am ready to switch over. I was actually working on switching my (real) project over to use your module instead and it occurred to me...

Couldn't you swap all the args Arguments for args ...Arguments, thus making the "trello.Defaults()" on every call unnecessary (optional)? It may not be exactly that, but something like that? The go-trello code that I "forked" had Argument as a struct with Name and Value, then []Arguments or ...Argument :-/ ... just checking as that feels a bit strange to generate a new/empty map for every call. At the very least, I am thinking create it once at the top and use a variable for each one? ๐Ÿคทโ€โ™‚๏ธ

I am fairly new to golang, so I am not sure what the ramifications of that are?

Also, Based on my findings trying to hack go-trello into submission, I think I can offer several PRs to add capability/functionality if you are interested.

One of the biggest epiphanies I had was the "AddMemberResponse" is actually a (partial) "Board" response... of course you have to have []*Memberships and []*Members in your Board type. Anyhow, lets focus on the "optionalization" (new word?) of the args for this issue. :)

~tommy

EDIT: I finally found the right word "variadic" :)

https://www.geeksforgeeks.org/golang-program-that-uses-func-with-variable-argument-list/

At the most basic level, you could just use args ...Arguments and then add a [0] to the places where you access args now, but of course the ... might encourage people to pass more than one list of arguments, so you should maybe come up with some way to "flatten" them? It seems like you already had some "manual" flattening going on (like "if pos, then set pos")... so it would already be useful IMO.

Getting custom fields doesn't work as expected

Problem:
Using the CustomFields function on a Card reciever returns 0 data

Code:

func main () {

   appKey := `<CONFIDENTIAL>`
   token := `<CONFIDENTIAL>`
   boardId := `<CONFIDENTIAL>`

   client := trello.NewClient(appKey, token)
   board, err := client.GetBoard(boardId, trello.Defaults())
   if err != nil {
      fmt.Println(err)
      return
   }

   customFields, err := board.GetCustomFields(trello.Defaults())
   if err != nil {
      fmt.Println(err)
   }

   fmt.Println("Printing custom field objects on the board")
   for _, v := range customFields {
      fmt.Printf("%+v", v)
      fmt.Println()
   }

   lists, err := board.GetLists(trello.Defaults())
   if err != nil {
      fmt.Println(err)
      return
   }

   for _, list := range lists {
     cards, err := list.GetCards(trello.Defaults())
     if err != nil {
        fmt.Println(err)
        continue
     }

     fmt.Println("Number of cards in list:", len(cards))
     var count int = 0

     for _, card := range cards {

        count++
       
        /// It is unclear if the Card slice this function requires should come from Board.GetCustomFields, or if it should be an empty slice.
        customFieldMap := card.CustomFields(customFields)
        fmt.Println("Number of custom fields on card", count, ":", len(customFieldMap))
        fmt.Println("Number of custom field items on card", count, ":", len(card.CustomFieldItems))

     }
   }
}

Output:

Printing custom field objects on the board
&{ID:5fa073a5eedccb1a273b7db2 IDModel:5fa0710b81d40785e7e8d06a IDModelType:board FieldGroup:9fdff5d309838d346add25a8e5260d5c27b4dbcc98e5c3d0da85b5ec991e37f7 Name:Created Pos:16384 Display:{CardFront:false} Type:date Options:[]}
&{ID:5fa073afa41de270281aa94c IDModel:5fa0710b81d40785e7e8d06a IDModelType:board FieldGroup:9c0fdca40714ca5c1f07cf19dce17821e5929624a57ae4a1f8fcd938cceca945 Name:Start Pos:32768 Display:{CardFront:false} Type:date Options:[]}
&{ID:5fa073b63f96c845ec9dd0c7 IDModel:5fa0710b81d40785e7e8d06a IDModelType:board FieldGroup:fc5e9fa90b20867f3c27c61e67e33b2e583653abd693703800c04456ec854114 Name:End Pos:49152 Display:{CardFront:false} Type:date Options:[]}
&{ID:5fa0741e1d9d2c2bc833788f IDModel:5fa0710b81d40785e7e8d06a IDModelType:board FieldGroup:c77db228ea2700530db145e0d91ab5129ce52883999ccc42693e6dae7d2c2ad3 Name:Charge Pos:65536 Display:{CardFront:true} Type:list Options:[0xc0000d64b0 0xc0000d6500 0xc0000d6550]}
&{ID:5fa074286ef27c133d0186bb IDModel:5fa0710b81d40785e7e8d06a IDModelType:board FieldGroup:7602b2a97184edb3beeb5b0076b88f065146bd5797726754fc6c96e099d15abc Name:Workload Pos:81920 Display:{CardFront:true} Type:list Options:[0xc0000d65a0 0xc0000d65f0 0xc0000d6640 0xc0000d6690 0xc0000d66e0 0xc0000d6730]}
&{ID:5fa0746a39b64c850400b920 IDModel:5fa0710b81d40785e7e8d06a IDModelType:board FieldGroup:b0cddfb41bc9462347387c37077a2de5338a50c797e04f3cf0adb6ae2344e83c Name:Priority Pos:98304 Display:{CardFront:true} Type:list Options:[0xc0000d6780 0xc0000d67d0 0xc0000d6820 0xc0000d6870 0xc0000d68c0 0xc0000d6910 0xc0000d69b0]}
&{ID:5fa079eb3c04d6016ef994e2 IDModel:5fa0710b81d40785e7e8d06a IDModelType:board FieldGroup:e8620327b22fc5e743a35720260ad7f03a0edaef6e64e3c47f37f04f09ce4747 Name:Status Pos:114688 Display:{CardFront:true} Type:list Options:[0xc0000d6a50 0xc0000d6aa0 0xc0000d6af0 0xc0000d6b40 0xc0000d6b90]}
Number of cards in list: 5
Number of custom fields on card 1 : 0
Number of custom field items on card 1 : 0
Number of custom fields on card 2 : 0
Number of custom field items on card 2 : 0
Number of custom fields on card 3 : 0
Number of custom field items on card 3 : 0
Number of custom fields on card 4 : 0
Number of custom field items on card 4 : 0
Number of custom fields on card 5 : 0
Number of custom field items on card 5 : 0
Number of cards in list: 2
Number of custom fields on card 1 : 0
Number of custom field items on card 1 : 0
Number of custom fields on card 2 : 0
Number of custom field items on card 2 : 0
Number of cards in list: 6
Number of custom fields on card 1 : 0
Number of custom field items on card 1 : 0
Number of custom fields on card 2 : 0
Number of custom field items on card 2 : 0
Number of custom fields on card 3 : 0
Number of custom field items on card 3 : 0
Number of custom fields on card 4 : 0
Number of custom field items on card 4 : 0
Number of custom fields on card 5 : 0
Number of custom field items on card 5 : 0
Number of custom fields on card 6 : 0
Number of custom field items on card 6 : 0
Number of cards in list: 1
Number of custom fields on card 1 : 0
Number of custom field items on card 1 : 0
Number of cards in list: 4
Number of custom fields on card 1 : 0
Number of custom field items on card 1 : 0
Number of custom fields on card 2 : 0
Number of custom field items on card 2 : 0
Number of custom fields on card 3 : 0
Number of custom field items on card 3 : 0
Number of custom fields on card 4 : 0
Number of custom field items on card 4 : 0

Expectation:
Using the CustomFields function on a Card reciever should provide data on what the value is for each custom field

How to reproduce:
Copy the above example code in your own main program, fill in the appKey, token, and boardId fields to a Trello board you control, then observe the output.

Notes:
It seems that the CustomFieldItems JSON serialized property on Card is empty to begin with, so the loop in Card.CustomFields skips over it.

Webhooks wrong request

There is a bug in Token.GetWebhooks. The function uses t.ID to insert into URL for API. But trello's API waits for an entire token, not the token ID. so it should be like t.client.Token used for URL

Attachment Not Working

Hi all
Thanks for this good working but examples are missing. I am trying to add a card with attachment. I did it without any error but no attachment on Trello.

var attachs []*trello.Attachment
attachs = append(attachs,
&trello.Attachment{URL:"https://www.belightsoft.com/products/imagetricks/img/[email protected]", Name:"test"})

card := &trello.Card{
Name: "Batur Orkun",
Desc: "Card description",
Attachments: attachs,
}
err = list.AddCard(card, trello.Defaults())

Client does not get cleaned up properly

Hi there,

I have built an application using this library - it works really well, so thanks for creating it! I have a background goroutine that tries to sync some data between Trello and another service every couple of minutes. In my loop, I create a new trello.Client via NewClient and noticed that over time, my CPU usage becomes really high. I think it might be related to the throttling built into the library, because time.Tick does not get cleaned up properly (apparently you're supposed to use time.NewTicker and stop it, ref).
I'm not 100% sure that this is the exact cause, but if I do not create a trello client in my loop, the CPU usage stays the same over time.

I'm not sure if there is some sort of best practice on how to resolve this so I wanted to open an issue first - maybe there can be an option to disable the throttling or expose the ticker so that I can stop it myself when I know I won't need the Client anymore? Happy to help out with the solution.

Cheers and thanks again for the library!

Example code (might not work, I removed some parts of my application)

		for {
			time.Sleep(5 * time.Minute)

			for _, user := range s.Users {
				trelloClient := trello.NewClient(user.TrelloAppKey, user.TrelloToken)

				// ... do things with the client
			}
		}

Checklst API

Hello!

Your package is great overall, but I noticed that it lacks API methods to work with checklists. It happened so that I need some of them, so I'd like to work on it.

It might take some time, so feel free to contact me for collaboration :)

Support dropdown list-type custom fields

When creating custom fields, I have the option to select "dropdown", which results in a .customField[].type="list" being created. The options are predefined in .customField[].options[] with .customField[].options[].value being what github.com/adlio/trello calls cfval. It always contains a text field.

It would be great if dropdowns were supported by this library.

Organization ID? (Member.IDOrganizations)

The Member type seems to be missing several fields, specifically IDOrganizations. I see a GetOrganization func, but no idea how you are supposed to get the organization ID to start with if you don't retrieve it from the Member?

Perhaps I am just looking at it wrong?

Otherwise I can submit a PR to fix this part :)

Client longevity is not propagated to cards if requested on lists

When getting lists with cards in the same request, the client is not propagated to the card entities.

Function Board#GetLists only moves the client to the lists, not the cards on the list:

trello/list.go

Lines 38 to 45 in 50836c3

func (b *Board) GetLists(args Arguments) (lists []*List, err error) {
path := fmt.Sprintf("boards/%s/lists", b.ID)
err = b.client.Get(path, args, &lists)
for i := range lists {
lists[i].client = b.client
}
return
}

func getCardsOnList(board *trello.Board) ([]*trello.Card, error) {
  lists, _ := board.GetLists(args("fields", "name", "cards", "open", "card_fields", "id,name,labels"))
  for _, l := range lists {
    for _, c := range l.Cards {
      // any action on c panics
      c.CopyToList("listid", trello.Defaults())
    }
  }
}

The fix seems simple:

func (b *Board) GetLists(args Arguments) (lists []*List, err error) {
	path := fmt.Sprintf("boards/%s/lists", b.ID)
	err = b.client.Get(path, args, &lists)
	for i := range lists {
		lists[i].client = b.client
		if len(lists[i].Cards) == 0 {
			continue
		}
		for j := range lists[i].Cards {
			lists[i].Cards[j].client = lists[i].client
		}
	}
	return
}

I can provide a PR if this is a bug and should be fixed. If it's expected behaviour I think a note in the documentation is expected.

Creating a webhook fails

The error returned from Trello is

JSON decode failed on https://api.trello.com/1/webhooks:
invalid value for idModel: invalid character 'i' looking for beginning of value

Other API calls for this board ID work correctly.

Cannot get instance of some entities with valid client without making an api request

(This is my first dive into Go, so I'm sure my terminology is wrong - please correct me where that's the case so I can learn โ˜บ)

I'm working on making a custom Terraform provider for interacting with Trello, and am using this library for the underlying api calls.

This has been really good so far - while some of the API is missing methods, the exposure of the underlying CURD methods has made it super easy to implement support without needing to make changes to this library.

My plan is at some point contribute back to this library with some PRs expanding it to cover these gaps, but I've hit up against a particular issue that would be great to get addressed now - specifically, not being able to set the client property on structs.

For example, I'm currently in the process of writing a card_membership resource that uses the "Add a Member to a Card" and "Remove a User from a Card" endpoints to add and remove a member with a given id to and from the card with a given id.

Presently, the card package has AddMemberID & RemoveMember which calls these underlying endpoints - however they use the private client property on the card which I can't set, meaning the only way I can get an instance of trello.Card that won't crash if I try to use card.RemoveMember is by doing an API call like so:

func resourceTrelloCardMembershipDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*trello.Client)

	card, err := client.GetCard(data.Get("card_id").(string), trello.Defaults())

	if err != nil {
		return diag.FromErr(err)
	}

	err = card.RemoveMember(data.Get("member_id").(string))

	if err != nil {
		return diag.FromErr(err)
	}

	var diags diag.Diagnostics

	return diags
}

What I'd like to be able to do is this:

func resourceTrelloCardMembershipDelete(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*trello.Client)

	card := trello.Card{ID: data.Get("card_id").(string)}

	card.SetClient(client)

	err := card.RemoveMember(data.Get("member_id").(string))

	if err != nil {
		return diag.FromErr(err)
	}

	var diags diag.Diagnostics

	return diags
}

Alternatively, client could be made public so that I can set that property when assigning.

This applies to most of the entities in this library, including Board as NewBoard doesn't assign client.

I'm happy to have a go at doing a PR that implements SetClient, but wanted to open an issue first as there might be a more Go-like solution that should be used :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.