aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fiken/fiken.go28
-rw-r--r--fiken/fiken_test.go16
2 files changed, 40 insertions, 4 deletions
diff --git a/fiken/fiken.go b/fiken/fiken.go
index 2a5d94b..4e1d0dd 100644
--- a/fiken/fiken.go
+++ b/fiken/fiken.go
@@ -95,7 +95,7 @@ func withSleep(f func(time.Duration)) Option {
// personal API token is.
func New(token string, opts ...Option) (*Client, error) {
if strings.TrimSpace(token) == "" {
- return nil, errors.New("no Fiken API token: set FIKEN_API_TOKEN or fiken.token_file")
+ return nil, errors.New("no Fiken API token")
}
return NewWithTokens(StaticToken(token), opts...)
}
@@ -181,7 +181,7 @@ func (c *Client) CreateContact(ctx context.Context, companySlug string, contact
if err != nil {
return 0, fmt.Errorf("encode contact: %w", err)
}
- resp, err := c.do(ctx, http.MethodPost, "/companies/"+companySlug+"/contacts", nil, body)
+ resp, err := c.doOnce(ctx, http.MethodPost, "/companies/"+companySlug+"/contacts", nil, body)
if err != nil {
return 0, err
}
@@ -319,6 +319,26 @@ func asFields(update ContactUpdate) (map[string]any, error) {
return fields, nil
}
+// User is the Fiken account a token belongs to.
+type User struct {
+ Name string `json:"name"`
+ Email string `json:"email"`
+}
+
+// User returns the account the token was issued to.
+func (c *Client) User(ctx context.Context) (User, error) {
+ resp, err := c.do(ctx, http.MethodGet, "/user", nil, nil)
+ if err != nil {
+ return User{}, err
+ }
+ defer resp.Body.Close()
+ var u User
+ if err := json.NewDecoder(io.LimitReader(resp.Body, maxBodyBytes)).Decode(&u); err != nil {
+ return User{}, fmt.Errorf("fiken GET /user: unreadable response: %w", err)
+ }
+ return u, nil
+}
+
// Companies lists the companies the token can act for.
func (c *Client) Companies(ctx context.Context) ([]Company, error) {
return page[Company](ctx, c, "/companies", nil)
@@ -427,7 +447,7 @@ func (c *Client) CreateDraft(ctx context.Context, companySlug string, draft Draf
if err != nil {
return 0, fmt.Errorf("encode invoice draft: %w", err)
}
- resp, err := c.do(ctx, http.MethodPost, "/companies/"+companySlug+"/invoices/drafts", nil, body)
+ resp, err := c.doOnce(ctx, http.MethodPost, "/companies/"+companySlug+"/invoices/drafts", nil, body)
if err != nil {
return 0, err
}
@@ -455,7 +475,7 @@ func (c *Client) DeleteDraft(ctx context.Context, companySlug string, draftID in
func (c *Client) CreateInvoiceFromDraft(ctx context.Context, companySlug string, draftID int64) (int64, error) {
assert.True(draftID > 0, "draft id must be positive, got %d", draftID)
path := fmt.Sprintf("/companies/%s/invoices/drafts/%d/createInvoice", companySlug, draftID)
- resp, err := c.do(ctx, http.MethodPost, path, nil, nil)
+ resp, err := c.doOnce(ctx, http.MethodPost, path, nil, nil)
if err != nil {
return 0, err
}
diff --git a/fiken/fiken_test.go b/fiken/fiken_test.go
index 5e2124a..7103339 100644
--- a/fiken/fiken_test.go
+++ b/fiken/fiken_test.go
@@ -449,3 +449,19 @@ func TestContactReadsTheCurrency(t *testing.T) {
t.Errorf("currency = %q, want it read back so a caller can check it", got.Currency)
}
}
+
+// A 502 does not say whether the draft was made, so asking again could make two.
+func TestCreatingIsNeverRetried(t *testing.T) {
+ calls := 0
+ c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
+ calls++
+ w.WriteHeader(http.StatusBadGateway)
+ })
+ ctx := context.Background()
+ c.CreateDraft(ctx, "sokkel-as", Draft{CustomerID: 1, Lines: []DraftLine{{Description: "x", Quantity: 1}}})
+ c.CreateContact(ctx, "sokkel-as", NewContact{Name: "x"})
+ c.CreateInvoiceFromDraft(ctx, "sokkel-as", 1)
+ if calls != 3 {
+ t.Errorf("%d requests for three creates, want 3", calls)
+ }
+}