-
Notifications
You must be signed in to change notification settings - Fork 0
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
0 parents
commit 487fb5e
Showing
16 changed files
with
1,086 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Created by https://www.toptal.com/developers/gitignore/api/go | ||
# Edit at https://www.toptal.com/developers/gitignore?templates=go | ||
|
||
### Go ### | ||
# If you prefer the allow list template instead of the deny list, see community template: | ||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | ||
# | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
vendor/ | ||
|
||
# Go workspace file | ||
go.work | ||
|
||
# End of https://www.toptal.com/developers/gitignore/api/go | ||
|
||
.env | ||
.envrc |
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,33 @@ | ||
# Created by https://www.toptal.com/developers/gitignore/api/go | ||
# Edit at https://www.toptal.com/developers/gitignore?templates=go | ||
|
||
### Go ### | ||
# If you prefer the allow list template instead of the deny list, see community template: | ||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | ||
# | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
vendor/ | ||
|
||
# Go workspace file | ||
go.work | ||
|
||
# End of https://www.toptal.com/developers/gitignore/api/go | ||
|
||
.env | ||
.envrc | ||
|
||
vodafone-billing-downloader | ||
|
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,66 @@ | ||
package fetcher | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/http/httputil" | ||
) | ||
|
||
type BearerToken interface { | ||
AuthenticateAPI(request *http.Request) *http.Request | ||
} | ||
|
||
var ( | ||
ErrJsonRequestFailed = errors.New("JSON request failed") | ||
) | ||
|
||
func GetJson(url string, bearerToken BearerToken, target interface{}) error { | ||
request, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return err | ||
} | ||
request = setJsonRequestHeaders(request) | ||
request = bearerToken.AuthenticateAPI(request) | ||
return jsonFromRequest(request, target) | ||
} | ||
|
||
func setJsonRequestHeaders(request *http.Request) *http.Request { | ||
request.Header.Set("Content-Type", "application/json") | ||
return request | ||
} | ||
|
||
func jsonFromRequest(request *http.Request, target interface{}) error { | ||
response, err := http.DefaultClient.Do(request) | ||
if err != nil { | ||
return err | ||
} | ||
if response.StatusCode != http.StatusOK { | ||
requestBytes, err := httputil.DumpRequest(request, true) | ||
if err != nil { | ||
panic(err) | ||
} | ||
fmt.Println(string(requestBytes)) | ||
fmt.Println("") | ||
|
||
responseBytes, err := httputil.DumpResponse(response, true) | ||
if err != nil { | ||
panic(err) | ||
} | ||
fmt.Println(string(responseBytes)) | ||
return ErrJsonRequestFailed | ||
} | ||
|
||
return getJsonFromResponse(response, target) | ||
} | ||
|
||
func getJsonFromResponse(response *http.Response, target interface{}) error { | ||
defer response.Body.Close() | ||
data, err := io.ReadAll(response.Body) | ||
if err != nil { | ||
return err | ||
} | ||
return json.Unmarshal(data, target) | ||
} |
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,28 @@ | ||
module github.com/cbrand/vodafone-billing-downloader | ||
|
||
go 1.21.0 | ||
|
||
require ( | ||
github.com/fatih/color v1.16.0 | ||
github.com/stretchr/testify v1.8.4 | ||
github.com/urfave/cli/v2 v2.27.1 | ||
) | ||
|
||
require ( | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/jedib0t/go-pretty/v6 v6.5.0 // indirect | ||
github.com/mattn/go-colorable v0.1.13 // indirect | ||
github.com/mattn/go-isatty v0.0.20 // indirect | ||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/rivo/uniseg v0.4.4 // indirect | ||
github.com/rodaine/table v1.1.0 // indirect | ||
github.com/russross/blackfriday/v2 v2.1.0 // indirect | ||
github.com/schollz/progressbar/v3 v3.14.1 // indirect | ||
github.com/urfave/cli v1.22.14 // indirect | ||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect | ||
golang.org/x/sys v0.16.0 // indirect | ||
golang.org/x/term v0.16.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
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,64 @@ | ||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= | ||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= | ||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/jedib0t/go-pretty/v6 v6.5.0 h1:FI0L5PktzbafnZKuPae/D3150x3XfYbFe2hxMT+TbpA= | ||
github.com/jedib0t/go-pretty/v6 v6.5.0/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= | ||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= | ||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | ||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | ||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= | ||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= | ||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | ||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | ||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | ||
github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4= | ||
github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g= | ||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/schollz/progressbar/v3 v3.14.1 h1:VD+MJPCr4s3wdhTc7OEJ/Z3dAeBzJ7yKH/P4lC5yRTI= | ||
github.com/schollz/progressbar/v3 v3.14.1/go.mod h1:Zc9xXneTzWXF81TGoqL71u0sBPjULtEHYtj/WVgVy8E= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= | ||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= | ||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= | ||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= | ||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= | ||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= | ||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= | ||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= | ||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= | ||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= | ||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
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,159 @@ | ||
package invoice | ||
|
||
import ( | ||
"crypto/md5" | ||
"encoding/base64" | ||
"encoding/hex" | ||
"fmt" | ||
|
||
"github.com/cbrand/vodafone-billing-downloader/fetcher" | ||
) | ||
|
||
const ( | ||
INVOICE_URL_TEMPLATE = "https://api.vodafone.de/meinvodafone/v2/customer/urn:vf-de:cable:can:%s/invoice" | ||
INVOICE_DOCUMENT_URL_TEMPLATE = "https://api.vodafone.de/meinvodafone/v2/customer/%s/invoiceDocument/%s" | ||
) | ||
|
||
type ContractID string | ||
|
||
func invoiceURLFor(contractID ContractID) string { | ||
return fmt.Sprintf(INVOICE_URL_TEMPLATE, contractID) | ||
} | ||
|
||
type ContractIDFetcher interface { | ||
GetAllContractIDs() []string | ||
} | ||
|
||
type InvoiceOverview struct { | ||
Data map[ContractID]*InvoiceList | ||
} | ||
|
||
func (invoiceOverview *InvoiceOverview) GetNumInvoices() int { | ||
numInvoices := 0 | ||
for _, invoiceList := range invoiceOverview.Data { | ||
numInvoices += invoiceList.GetNumInvoices() | ||
} | ||
return numInvoices | ||
} | ||
|
||
func (invoiceOverview *InvoiceOverview) GetNumDocuments() int { | ||
numDocuments := 0 | ||
for _, invoiceList := range invoiceOverview.Data { | ||
numDocuments += invoiceList.GetNumDocuments() | ||
} | ||
return numDocuments | ||
} | ||
|
||
func List(contractIDFetcher ContractIDFetcher, bearerToken fetcher.BearerToken) (*InvoiceOverview, error) { | ||
invoices := make(map[ContractID]*InvoiceList) | ||
for _, contractID := range contractIDFetcher.GetAllContractIDs() { | ||
invoiceList, err := ListFor(ContractID(contractID), bearerToken) | ||
if err != nil { | ||
return nil, err | ||
} | ||
invoices[ContractID(contractID)] = invoiceList | ||
} | ||
|
||
return &InvoiceOverview{Data: invoices}, nil | ||
} | ||
|
||
func ListFor(contractID ContractID, bearerToken fetcher.BearerToken) (*InvoiceList, error) { | ||
invoiceList := &InvoiceList{} | ||
err := fetcher.GetJson(invoiceURLFor(contractID), bearerToken, invoiceList) | ||
if err != nil { | ||
return nil, err | ||
} | ||
invoiceList.PropagateCustomerID() | ||
return invoiceList, nil | ||
} | ||
|
||
type InvoiceList struct { | ||
CustomerID string `json:"customerId"` | ||
Invoices []*Invoice `json:"invoices"` | ||
} | ||
|
||
func (invoiceList *InvoiceList) GetNumInvoices() int { | ||
return len(invoiceList.Invoices) | ||
} | ||
|
||
func (invoiceList *InvoiceList) GetNumDocuments() int { | ||
numDocuments := 0 | ||
for _, invoice := range invoiceList.Invoices { | ||
numDocuments += invoice.GetNumDocuments() | ||
} | ||
return numDocuments | ||
} | ||
|
||
func (invoiceList *InvoiceList) PropagateCustomerID() { | ||
for _, invoice := range invoiceList.Invoices { | ||
invoice.SetCustomerID(invoiceList.CustomerID) | ||
} | ||
} | ||
|
||
type Invoice struct { | ||
Number string `json:"number"` | ||
Date string `json:"date"` | ||
Amount float64 `json:"amount"` // Vodafone API has this defined as a float in json. This is a bad idea. | ||
DueDate string `json:"dueDate"` | ||
From string `json:"from"` | ||
About string `json:"about"` | ||
Documents []*InvoiceDocument `json:"documents"` | ||
CustomerID string `json:"-"` | ||
} | ||
|
||
func (invoice *Invoice) GetNumDocuments() int { | ||
return len(invoice.Documents) | ||
} | ||
|
||
func (invoice *Invoice) SetCustomerID(customerID string) { | ||
invoice.CustomerID = customerID | ||
for _, document := range invoice.Documents { | ||
document.SetCustomerID(customerID) | ||
} | ||
} | ||
|
||
type InvoiceDocument struct { | ||
DocumentID string `json:"documentId"` | ||
Category string `json:"category"` | ||
Icon string `json:"icon"` | ||
SubType string `json:"subType"` | ||
CustomerID string `json:"-"` | ||
} | ||
|
||
func (invoiceDocument *InvoiceDocument) SetCustomerID(customerID string) { | ||
invoiceDocument.CustomerID = customerID | ||
} | ||
|
||
func (invoiceDocument *InvoiceDocument) DownloadURL() string { | ||
return fmt.Sprintf(INVOICE_DOCUMENT_URL_TEMPLATE, invoiceDocument.CustomerID, invoiceDocument.DocumentID) | ||
} | ||
|
||
func (invoiceDocument *InvoiceDocument) Download(bearerToken fetcher.BearerToken) (*DocumentData, error) { | ||
documentData := &DocumentData{} | ||
invoiceDownloadURL := invoiceDocument.DownloadURL() | ||
err := fetcher.GetJson(invoiceDownloadURL, bearerToken, documentData) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return documentData, nil | ||
} | ||
|
||
type DocumentData struct { | ||
CustomerID string `json:"customerId"` | ||
DocumentID string `json:"documentId"` | ||
MimeType string `json:"mime"` | ||
Data string `json:"data"` | ||
} | ||
|
||
func (documentData *DocumentData) Bytes() ([]byte, error) { | ||
return base64.StdEncoding.DecodeString(documentData.Data) | ||
} | ||
|
||
// Checksum returns an md5checksum the same as paperless would calculate for | ||
// cross referencing. | ||
func (documentData *DocumentData) Checksum() string { | ||
data, _ := documentData.Bytes() | ||
hash := md5.New() | ||
hash.Write(data) | ||
return hex.EncodeToString(hash.Sum(nil)) | ||
} |
Oops, something went wrong.