-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3084ee0
commit bac3411
Showing
6 changed files
with
277 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"os" | ||
|
||
"github.com/anothertobi/viseca-exporter/internal/app" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
func main() { | ||
app := &cli.App{ | ||
Name: "viseca-cli", | ||
Usage: "query Viseca one", | ||
Flags: []cli.Flag{ | ||
&cli.StringFlag{ | ||
Name: "username", | ||
Usage: "username", | ||
Required: true, | ||
EnvVars: []string{"VISECA_CLI_USERNAME"}, | ||
}, | ||
&cli.StringFlag{ | ||
Name: "password", | ||
Usage: "password", | ||
Required: true, | ||
EnvVars: []string{"VISECA_CLI_PASSWORD"}, | ||
}, | ||
}, | ||
Commands: []*cli.Command{ | ||
app.NewTransactionsCommand(), | ||
}, | ||
} | ||
|
||
if err := app.Run(os.Args); err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package app | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"net/url" | ||
"time" | ||
|
||
"github.com/PuerkitoBio/goquery" | ||
"github.com/anothertobi/viseca-exporter/pkg/viseca" | ||
) | ||
|
||
func login(ctx context.Context, username string, password string) (*viseca.Client, error) { | ||
cookieJar, err := cookiejar.New(nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
httpClient := &http.Client{ | ||
Jar: cookieJar, | ||
// Don't follow redirects since the auth flow is built around redirects | ||
CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||
return http.ErrUseLastResponse | ||
}, | ||
} | ||
|
||
res, err := httpClient.Get("https://one.viseca.ch/login/login") | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer res.Body.Close() | ||
|
||
doc, err := goquery.NewDocumentFromReader(res.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var formToken string | ||
var found bool | ||
doc.Find(`input[name="FORM_TOKEN"]`).EachWithBreak(func(i int, s *goquery.Selection) bool { | ||
formToken, found = s.Attr("value") | ||
return !found | ||
}) | ||
|
||
form := url.Values{} | ||
form.Add("FORM_TOKEN", formToken) | ||
form.Add("USERNAME", username) | ||
form.Add("PASSWORD", password) | ||
|
||
res, err = httpClient.PostForm("https://one.viseca.ch/login/login", form) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode != 302 { | ||
return nil, errors.New("login failed (no redirect response)") | ||
} | ||
|
||
err = awaitAppConfirmation(ctx, httpClient) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
visecaClient := viseca.NewClient(httpClient) | ||
|
||
return visecaClient, nil | ||
} | ||
|
||
func awaitAppConfirmation(ctx context.Context, httpClient *http.Client) error { | ||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://one.viseca.ch/login/app-confirmation", nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
if err := ctx.Err(); err != nil { | ||
return err | ||
} | ||
default: | ||
res, err := httpClient.Do(req) | ||
if err != nil { | ||
return err | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode == 302 { | ||
return nil | ||
} | ||
} | ||
time.Sleep(2 * time.Second) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package app | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/anothertobi/viseca-exporter/internal/csv" | ||
"github.com/anothertobi/viseca-exporter/pkg/viseca" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
const flagDateFrom = "date-from" | ||
const flagDateTo = "date-to" | ||
|
||
// NewTransactionsCommand creates a new transactions CLI command | ||
func NewTransactionsCommand() *cli.Command { | ||
return &cli.Command{ | ||
Name: "transactions", | ||
Usage: "list all transactions for given card id", | ||
ArgsUsage: "cardID", | ||
Flags: []cli.Flag{ | ||
&cli.TimestampFlag{ | ||
Name: flagDateFrom, | ||
Usage: "from which date on transactions should be fetched (format: 2006-01-02)", | ||
Layout: "2006-01-02", | ||
Timezone: time.Local, | ||
}, | ||
&cli.TimestampFlag{ | ||
Name: flagDateTo, | ||
Usage: "to which date transactions should be fetched (format: 2006-01-02)", | ||
Layout: "2006-01-02", | ||
Timezone: time.Local, | ||
}, | ||
}, | ||
Action: func(cCtx *cli.Context) error { | ||
return transactions(cCtx) | ||
}, | ||
} | ||
} | ||
|
||
func transactions(cCtx *cli.Context) error { | ||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) | ||
defer cancel() | ||
|
||
if !cCtx.Args().Present() { | ||
return errors.New("requires card id arg") | ||
} | ||
cardID := cCtx.Args().First() | ||
|
||
username := cCtx.String("username") | ||
password := cCtx.String("password") | ||
|
||
visecaClient, err := login(ctx, username, password) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
listOptions := viseca.NewDefaultListOptions() | ||
dateFrom := cCtx.Timestamp(flagDateFrom) | ||
if dateFrom != nil { | ||
listOptions.DateFrom = *dateFrom | ||
} | ||
dateTo := cCtx.Timestamp(flagDateTo) | ||
if dateTo != nil { | ||
listOptions.DateFrom = *dateTo | ||
} | ||
|
||
// returns all transactions if date from | ||
transactions, err := visecaClient.ListAllTransactionsOpts(ctx, cardID, listOptions) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
out := csv.TransactionsString(transactions) | ||
fmt.Print(out) | ||
|
||
return nil | ||
} |