Browse Source

Implement EachDonate func

Volodymyr Tkach 2 years ago
parent
commit
d05c4f176d
5 changed files with 161 additions and 2 deletions
  1. 14 0
      README.md
  2. 89 0
      donatello/v1/each.go
  3. 44 0
      donatello/v1/v1_test.go
  4. 1 1
      go.mod
  5. 13 1
      main.go

+ 14 - 0
README.md

@@ -5,6 +5,8 @@ Go API client for ukrainian donate platform donatello.to
 
 ## Demo and testing
 
+Example is here: [https://github.com/vladimirok5959/golang-donatello/blob/main/main.go](https://github.com/vladimirok5959/golang-donatello/blob/main/main.go)
+
 ```sh
 export TOKEN="YOUR-TOKEN"
 go run main.go
@@ -63,6 +65,18 @@ respClients: &v1.ResponseClients{
     }
 }
 err: <nil>
+
+client.EachDonate:
+EachDonate: &v1.ResponseDonatesContent{
+    PubID: "A1B-A123456",
+    ClientName: "ClientName",
+    Message: "Message",
+    Amount: "100",
+    Currency: "UAH",
+    Goal: "",
+    IsPublished: false,
+    CreatedAt: "2022-10-20 00:30:50"
+}
 ```
 
 API faker included, see: [https://github.com/vladimirok5959/golang-donatello/blob/main/donatello/v1/client_fake_api.go](https://github.com/vladimirok5959/golang-donatello/blob/main/donatello/v1/client_fake_api.go)

+ 89 - 0
donatello/v1/each.go

@@ -0,0 +1,89 @@
+package v1
+
+import (
+	"context"
+	"fmt"
+	"time"
+)
+
+// Count of items per page
+var DonatesPerPage = 20
+
+// Pause between requests, Because we can catch:
+//
+// {"success": false, "message": "Забагато запитів. Спробуйте пізніше."}
+var ThrottlingInMilliseconds = 1000
+
+// EachDonate is iterate over all donates and use pagination and throttling.
+// Always check returned error and use transaction for preventing to save bad data.
+// Count of items per page and throttling value can be changed once in whole package, it's not a constants
+//
+// Please throw error inside fn function to break whole process, or return just nil. Example:
+//
+//	isFound := false
+//	client.EachDonate(ctx, func(donate *v1.ResponseDonatesContent) error {
+//		if donate.PubID == "12345" {
+//			fmt.Printf("Donate: %#v\n", donate)
+//			isFound = true
+//		}
+//		if isFound {
+//			return fmt.Errorf("break")
+//		}
+//		return nil
+//	})
+func (c *Client) EachDonate(ctx context.Context, fn func(donate *ResponseDonatesContent) error) error {
+	var resp *ResponseDonates
+	var err error
+	p := int64(0)
+
+	for {
+		resp, err = c.Donates(ctx, p, int64(DonatesPerPage))
+		if err != nil {
+			return err
+		}
+
+		if !resp.Success {
+			return fmt.Errorf("%s", resp.Message)
+		}
+
+		if len(resp.Content) > 0 {
+			for _, donate := range resp.Content {
+				err = fn(&donate)
+				if err != nil {
+					return err
+				}
+
+				select {
+				case <-ctx.Done():
+					return fmt.Errorf("context canceled")
+				default:
+				}
+			}
+		}
+
+		p++
+
+		if err != nil {
+			break
+		}
+
+		if p >= resp.Pages {
+			break
+		}
+
+		if resp.Page >= resp.Pages {
+			break
+		}
+
+		select {
+		case <-ctx.Done():
+			return fmt.Errorf("context canceled")
+		default:
+		}
+
+		// Throttling
+		time.Sleep(time.Duration(ThrottlingInMilliseconds) * time.Millisecond)
+	}
+
+	return err
+}

+ 44 - 0
donatello/v1/v1_test.go

@@ -155,6 +155,50 @@ var _ = Describe("Client", func() {
 				}))
 			})
 		})
+
+		Context("EachDonate", func() {
+			It("iterate over all donates", func() {
+				api.MockDonates = func(page, size int64) (int64, []byte, error) {
+					return http.StatusOK, []byte(`{
+						"content": [
+							{
+								"pubId": "A1B-A123451",
+								"clientName": "ClientName 1",
+								"message": "Message 1",
+								"amount": "101",
+								"currency": "UAH",
+								"isPublished": false,
+								"createdAt": "2022-10-20 00:30:50"
+							},
+							{
+								"pubId": "A1B-A123452",
+								"clientName": "ClientName 2",
+								"message": "Message 2",
+								"amount": "102",
+								"currency": "UAH",
+								"isPublished": false,
+								"createdAt": "2022-10-20 00:30:50"
+							}
+						],
+						"page": 1,
+						"size": 20,
+						"pages": 1,
+						"first": true,
+						"last": true,
+						"total": 1
+					}`), nil
+				}
+				donates := []v1.ResponseDonatesContent{}
+				err := client.EachDonate(ctx, func(donate *v1.ResponseDonatesContent) error {
+					donates = append(donates, *donate)
+					return nil
+				})
+				Expect(err).To(Succeed())
+				Expect(len(donates)).To(Equal(2))
+				Expect(donates[0].PubID).To(Equal("A1B-A123451"))
+				Expect(donates[1].PubID).To(Equal("A1B-A123452"))
+			})
+		})
 	})
 })
 

+ 1 - 1
go.mod

@@ -5,13 +5,13 @@ go 1.19
 require (
 	github.com/onsi/ginkgo v1.16.5
 	github.com/onsi/gomega v1.21.1
+	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
 )
 
 require (
 	github.com/fsnotify/fsnotify v1.4.9 // indirect
 	github.com/google/go-cmp v0.5.8 // indirect
 	github.com/nxadm/tail v1.4.8 // indirect
-	golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
 	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
 	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect

+ 13 - 1
main.go

@@ -34,13 +34,25 @@ func main() {
 	fmt.Printf("respMe: %#v\n", respMe)
 	fmt.Printf("err: %#v\n\n", err)
 
+	time.Sleep(1000 * time.Millisecond)
+
 	fmt.Printf("client.Donates:\n")
 	respDonates, err := client.Donates(ctx, 0, 20)
 	fmt.Printf("respDonates: %#v\n", respDonates)
 	fmt.Printf("err: %#v\n\n", err)
 
+	time.Sleep(1000 * time.Millisecond)
+
 	fmt.Printf("client.Clients:\n")
 	respClients, err := client.Clients(ctx)
 	fmt.Printf("respClients: %#v\n", respClients)
-	fmt.Printf("err: %#v\n", err)
+	fmt.Printf("err: %#v\n\n", err)
+
+	time.Sleep(1000 * time.Millisecond)
+
+	fmt.Printf("client.EachDonate:\n")
+	_ = client.EachDonate(ctx, func(donate *v1.ResponseDonatesContent) error {
+		fmt.Printf("EachDonate: %#v\n", donate)
+		return nil
+	})
 }