Skip to content

Commit

Permalink
Allow sources to declare custom periods to be queried (#13)
Browse files Browse the repository at this point in the history
* refactor & update configuration to contain custom query periods

* update readme

* polish readme

* implement period check & update existing tests

* add unit test cases for period-checking

* add unit test for config package

* fix test name

* fix bug

* polish
  • Loading branch information
utkuufuk committed May 25, 2020
1 parent f5ceb66 commit b1ec933
Show file tree
Hide file tree
Showing 16 changed files with 661 additions and 345 deletions.
44 changes: 37 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,52 @@ Run this as a cron job to periodically check custom data sources and automatical

An example use case (which is already implemented) could be to create a Trello card for each GitHub issue that's been assigned to you.

### Currently Available Sources
## Currently Available Sources
* Github Issues - https://github.com/issues/assigned
* TodoDock Tasks - https://tododock.com

Feel free to add new sources or improve the implementations of the existing ones. Contributions are always welcome!

### Configuration
Copy and rename `config.example.yml` as `config.yml`, then set your own values in `config.yml`.
## Configuration
Copy and rename `config.example.yml` as `config.yml`, then set your own values in `config.yml` according to the following:

#### Trello Client Configuration
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.

The given list will be the one that new cards is going to be inserted, and it has to be in the given board.

#### Disbling Individual Data Sources
In order to disable a source, just update the `enabled` flag as follows. (There's no need to remove/edit the other parameters for that source.)
In order to disable a source, just update the `enabled` flag to `false`. There's no need to remove/edit the other parameters for that source.

#### Defining Custom Periods
You can define a custom query period for each source, by populating the `type` and `interval` fields under the `period` for a source.

Example:
```yml
enabled: false
# query at 3rd, 6th, 9th, ... of each month
period:
type: day
interval: 3

# query at 00:00, 02:00, 04:00, ... every day
period:
type: hour
interval: 2

# query at XX:00, XX:15, XX:30 and XX:45 every hour
period:
type: minute
interval: 15

# query on each execution
period:
type: default
interval:
```

### Example Cron Job
## Example Cron Job
It's important to make sure that the cron job runs frequently enough to accomodate the most frequent custom interval for a source. It wouldn't make sense to define a custom period of 15 minutes while the cron job only runs every hour.

Both of the following jobs run every hour and both assume that `config.yml` is located in the current working directory.
``` sh
# use "go run"
Expand All @@ -37,7 +67,7 @@ Both of the following jobs run every hour and both assume that `config.yml` is l
0 * * * * cd /path/to/binary && ./entrello
```

### 3rd Party Dependencies
## 3rd Party Dependencies
| Dependency | Purpose |
|:-|:-|
| [adlio/trello](https://github.com/adlio/trello) | Trello API Client |
Expand Down
36 changes: 2 additions & 34 deletions cmd/entrello/main.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
package main

import (
"context"
"log"

"github.com/utkuufuk/entrello/internal/config"
"github.com/utkuufuk/entrello/internal/github"
"github.com/utkuufuk/entrello/internal/tododock"
"github.com/utkuufuk/entrello/internal/trello"
)

// Source represents a card source which exports a name and a getter for the cards to be created
type Source interface {
// GetName returns a human-readable name of the source
GetName() string

// GetLabel returns the corresponding card label ID for the source
GetLabel() string

// GetNewCards returns a list of Trello cards to be inserted into the board from the source
GetNewCards() ([]trello.Card, error)
}

func main() {
cfg, err := config.ReadConfig("config.yml")
if err != nil {
Expand All @@ -41,13 +26,13 @@ func main() {
log.Fatalf("[-] could not create trello client: %v", err)
}

if err := client.LoadExistingCardNames(labels); err != nil {
if err := client.LoadExistingCards(labels); err != nil {
// @todo: send telegram notification instead if enabled
log.Fatalf("[-] could not load existing cards from the board: %v", err)
}

for _, src := range sources {
cards, err := src.GetNewCards()
cards, err := src.FetchNewCards()
if err != nil {
// @todo: send telegram notification instead if enabled
log.Printf("[-] could not get cards for source '%s': %v", src.GetName(), err)
Expand All @@ -60,20 +45,3 @@ func main() {
}
}
}

// getEnabledSourcesAndLabels returns a list of enabled sources & all relevant label IDs
func getEnabledSourcesAndLabels(cfg config.Sources) (sources []Source, labels []string) {
if cfg.GithubIssues.Enabled {
src := github.GetSource(context.Background(), cfg.GithubIssues)
sources = append(sources, src)
labels = append(labels, src.GetLabel())
}

if cfg.TodoDock.Enabled {
src := tododock.GetSource(cfg.TodoDock)
sources = append(sources, src)
labels = append(labels, src.GetLabel())
}

return sources, labels
}
84 changes: 0 additions & 84 deletions cmd/entrello/main_test.go

This file was deleted.

87 changes: 87 additions & 0 deletions cmd/entrello/source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"context"
"fmt"
"log"
"time"

"github.com/utkuufuk/entrello/internal/config"
"github.com/utkuufuk/entrello/internal/github"
"github.com/utkuufuk/entrello/internal/tododock"
"github.com/utkuufuk/entrello/internal/trello"
)

// Source defines an interface for a Trello card source
type Source interface {
// IsEnabled returns true if the source is enabled by configuration.
IsEnabled() bool

// GetName returns a human-readable name of the source
GetName() string

// GetLabel returns the corresponding card label ID for the source
GetLabel() string

// GetPeriod returns the period in minutes that the source should be checked
GetPeriod() config.Period

// FetchNewCards returns a list of Trello cards to be inserted into the board from the source
FetchNewCards() ([]trello.Card, error)
}

// getEnabledSourcesAndLabels returns a list of enabled sources & all relevant label IDs
func getEnabledSourcesAndLabels(cfg config.Sources) (sources []Source, labels []string) {
arr := []Source{
github.GetSource(context.Background(), cfg.GithubIssues),
tododock.GetSource(cfg.TodoDock),
}
now := time.Now()

for _, src := range arr {
if ok, err := shouldQuery(src, now); !ok {
if err != nil {
// @todo: send telegram notification instead if enabled
log.Printf("[-] could not check if '%s' should be queried or not, skipping", src.GetName())
}
continue
}
sources = append(sources, src)
labels = append(labels, src.GetLabel())
}
return sources, labels
}

// shouldQuery checks if the given source should be queried at the given time
func shouldQuery(src Source, now time.Time) (bool, error) {
if !src.IsEnabled() {
return false, nil
}

interval := src.GetPeriod().Interval
if interval < 0 {
return false, fmt.Errorf("period interval must be a positive integer, got: '%d'", interval)
}

switch src.GetPeriod().Type {
case config.PERIOD_TYPE_DEFAULT:
return true, nil
case config.PERIOD_TYPE_DAY:
if interval > 31 {
return false, fmt.Errorf("daily interval cannot be more than 14, got: '%d'", interval)
}
return now.Day()%interval == 0 && now.Hour() == 0 && now.Minute() == 0, nil
case config.PERIOD_TYPE_HOUR:
if interval > 23 {
return false, fmt.Errorf("hourly interval cannot be more than 23, got: '%d'", interval)
}
return now.Hour()%interval == 0 && now.Minute() == 0, nil
case config.PERIOD_TYPE_MINUTE:
if interval > 60 {
return false, fmt.Errorf("minute interval cannot be more than 60, got: '%d'", interval)
}
return now.Minute()%interval == 0, nil
}

return false, fmt.Errorf("unrecognized source period type: '%s'", src.GetPeriod().Type)
}
Loading

0 comments on commit b1ec933

Please sign in to comment.