Skip to content

Commit

Permalink
use a global timeout instead of a workgroup
Browse files Browse the repository at this point in the history
  • Loading branch information
utkuufuk committed May 31, 2020
1 parent a53ff76 commit d15c614
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 57 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Feel free to add new sources or improve the implementations of the existing ones
## Configuration
Copy and rename `config.example.yml` as `config.yml`, then set your own values in `config.yml` according to the following:

#### Global Timeout
You can edit the `timeout_secs` config value in order to update global timeout (in seconds) for a single execution.

The execution will not terminate until the timeout is reached, so it's important that the timeout is shorter than the cron job period.

#### Trello
You need to set your [Trello API key & token](https://trello.com/app-key) in the configuraiton file, as well as the Trello board & list IDs.

Expand Down
62 changes: 33 additions & 29 deletions cmd/entrello/main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package main

import (
"context"
"log"
"sync"
"time"

"github.com/utkuufuk/entrello/internal/config"
"github.com/utkuufuk/entrello/internal/trello"
Expand All @@ -22,14 +23,19 @@ func main() {
log.Fatalf("[-] could not read config variables: %v", err)
}

// set global timeout
timeout := time.Second * time.Duration(cfg.TimeoutSeconds)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

// get a list of enabled sources and the corresponding labels for each source
sources, labels := getEnabledSourcesAndLabels(cfg.Sources)
sources, labels := getEnabledSourcesAndLabels(ctx, cfg.Sources)
if len(sources) == 0 {
return
}

// initialize the Trello client
client, err := trello.NewClient(cfg)
client, err := trello.NewClient(cfg.Trello)
if err != nil {
// @todo: send telegram notification instead if enabled
log.Fatalf("[-] could not create trello client: %v", err)
Expand All @@ -48,35 +54,33 @@ func main() {
err: make(chan error),
}

var wg sync.WaitGroup
wg.Add(len(sources))
// concurrently fetch new cards from each source and start queuing cards to be created & deleted
for _, src := range sources {
go queueActionables(src, client, q, &wg)
go queueActionables(src, client, q)
}

go func() {
for {
select {
case c := <-q.add:
// @todo: send telegram notification instead if enabled
if err := client.CreateCard(c); err != nil {
log.Printf("[-] error occurred while creating card: %v", err)
break
}
log.Printf("[+] created new card: %s", c.Name)
case c := <-q.del:
// @todo: send telegram notification instead if enabled
if err := client.ArchiveCard(c); err != nil {
log.Printf("[-] error occurred while archiving card: %v", err)
break
}
log.Printf("[+] archived stale card: %s", c.Name)
case err := <-q.err:
// @todo: send telegram notification instead if enabled
log.Printf("[-] %v", err)
//
for {
select {
case c := <-q.add:
// @todo: send telegram notification instead if enabled
if err := client.CreateCard(c); err != nil {
log.Printf("[-] error occurred while creating card: %v", err)
break
}
log.Printf("[+] created new card: %s", c.Name)
case c := <-q.del:
// @todo: send telegram notification instead if enabled
if err := client.ArchiveCard(c); err != nil {
log.Printf("[-] error occurred while archiving card: %v", err)
break
}
log.Printf("[+] archived stale card: %s", c.Name)
case err := <-q.err:
// @todo: send telegram notification instead if enabled
log.Printf("[-] %v", err)
case <-ctx.Done():
return
}
}()

wg.Wait()
}
}
10 changes: 3 additions & 7 deletions cmd/entrello/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"log"
"sync"
"time"

"github.com/utkuufuk/entrello/internal/config"
Expand Down Expand Up @@ -35,9 +34,9 @@ type source interface {
}

// getEnabledSourcesAndLabels returns a list of enabled sources & all relevant label IDs
func getEnabledSourcesAndLabels(cfg config.Sources) (sources []source, labels []string) {
func getEnabledSourcesAndLabels(ctx context.Context, cfg config.Sources) (sources []source, labels []string) {
arr := []source{
github.GetSource(context.Background(), cfg.GithubIssues),
github.GetSource(ctx, cfg.GithubIssues),
tododock.GetSource(cfg.TodoDock),
}
now := time.Now()
Expand Down Expand Up @@ -92,17 +91,14 @@ func shouldQuery(src source, now time.Time) (bool, error) {

// queueActionables fetches new cards from the source, then pushes those to be created and
// to be deleted into the corresponding channels, as well as any errors encountered.
func queueActionables(src source, client trello.Client, q CardQueue, wg *sync.WaitGroup) {
defer wg.Done()

func queueActionables(src source, client trello.Client, q CardQueue) {
cards, err := src.FetchNewCards()
if err != nil {
q.err <- fmt.Errorf("could not fetch cards for source '%s': %v", src.GetName(), err)
return
}

new, stale := client.CompareWithExisting(cards, src.GetLabel())
fmt.Printf("%s\nnew: %v\nstale:%v\n", src.GetName(), new, stale)

for _, c := range new {
q.add <- c
Expand Down
4 changes: 3 additions & 1 deletion cmd/entrello/source_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"testing"
"time"
Expand Down Expand Up @@ -68,14 +69,15 @@ func TestGetEnabledSourcesAndLabels(t *testing.T) {
},
}

ctx := context.Background()
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
cfg := config.Sources{
GithubIssues: tc.githubIssues,
TodoDock: tc.todoDock,
}

sources, labels := getEnabledSourcesAndLabels(cfg)
sources, labels := getEnabledSourcesAndLabels(ctx, cfg)
if len(sources) != tc.numResults {
t.Errorf("expected %d source(s); got %v", tc.numResults, len(sources))
}
Expand Down
11 changes: 7 additions & 4 deletions config.example.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
api_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
board_id: xxxxxxxxxxxxxxxxxxxxxxxx
list_id: xxxxxxxxxxxxxxxxxxxxxxxx
timeout_secs: 45

trello:
api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
api_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
board_id: xxxxxxxxxxxxxxxxxxxxxxxx
list_id: xxxxxxxxxxxxxxxxxxxxxxxx

sources:
github_issues:
Expand Down
13 changes: 9 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ type Sources struct {
TodoDock TodoDock `yaml:"tododock"`
}

type Trello struct {
ApiKey string `yaml:"api_key"`
ApiToken string `yaml:"api_token"`
BoardId string `yaml:"board_id"`
ListId string `yaml:"list_id"`
}

type Config struct {
TrelloApiKey string `yaml:"trello_api_key"`
TrelloApiToken string `yaml:"trello_api_token"`
BoardId string `yaml:"board_id"`
ListId string `yaml:"list_id"`
TimeoutSeconds int `yaml:"timeout_secs"`
Trello Trello `yaml:"trello"`
Sources Sources `yaml:"sources"`
}

Expand Down
14 changes: 8 additions & 6 deletions internal/trello/trello.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ type Client struct {
existingCards map[string][]Card
}

func NewClient(c config.Config) (client Client, err error) {
if c.BoardId == "" || c.ListId == "" || c.TrelloApiKey == "" || c.TrelloApiToken == "" {
func NewClient(cfg config.Trello) (client Client, err error) {
if cfg.BoardId == "" || cfg.ListId == "" || cfg.ApiKey == "" || cfg.ApiToken == "" {
return client, fmt.Errorf("could not create trello client, missing configuration parameter(s)")
}

return Client{
api: trello.NewClient(c.TrelloApiKey, c.TrelloApiToken),
boardId: c.BoardId,
listId: c.ListId,
api: trello.NewClient(cfg.ApiKey, cfg.ApiToken),
boardId: cfg.BoardId,
listId: cfg.ListId,
existingCards: make(map[string][]Card),
}, nil
}
Expand All @@ -52,6 +52,7 @@ func NewCard(name, label, description string, dueDate *time.Time) (card Card, er
if description == "" {
return card, fmt.Errorf("description cannot be blank")
}

return &trello.Card{
Name: name,
Desc: description,
Expand Down Expand Up @@ -84,7 +85,8 @@ func (c Client) LoadCards(labels []string) error {
return nil
}

// CompareWithExisting
// CompareWithExisting compares the given cards with the existing cards and returns two arrays;
// one containing new cards and the other containing stale cards.
func (c Client) CompareWithExisting(cards []Card, label string) (new, stale []Card) {
m := make(map[string]*trello.Card)
for _, card := range c.existingCards[label] {
Expand Down
14 changes: 8 additions & 6 deletions internal/trello/trello_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,15 @@ func TestNewClient(t *testing.T) {
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
cfg := config.Config{
TrelloApiKey: tc.apiKey,
TrelloApiToken: tc.apiToken,
BoardId: tc.boardId,
ListId: tc.listId,
Sources: config.Sources{},
Trello: config.Trello{
ApiKey: tc.apiKey,
ApiToken: tc.apiToken,
BoardId: tc.boardId,
ListId: tc.listId,
},
Sources: config.Sources{},
}
_, err := NewClient(cfg)
_, err := NewClient(cfg.Trello)
if (err != nil && !tc.err) || err == nil && tc.err {
t.Fatalf("did not expect the error outcome to be: '%t'", tc.err)
}
Expand Down

0 comments on commit d15c614

Please sign in to comment.