diff --git a/client/api.go b/client/api.go index 8b64b400..36929be5 100644 --- a/client/api.go +++ b/client/api.go @@ -15,6 +15,7 @@ import ( "github.com/xendit/xendit-go/payout" "github.com/xendit/xendit-go/qrcode" "github.com/xendit/xendit-go/recurringpayment" + "github.com/xendit/xendit-go/report" "github.com/xendit/xendit-go/retailoutlet" "github.com/xendit/xendit-go/transaction" "github.com/xendit/xendit-go/virtualaccount" @@ -37,6 +38,7 @@ type API struct { QRCode *qrcode.Client Customer *customer.Client Transaction *transaction.Client + Report *report.Client } func (a *API) init() { @@ -53,6 +55,7 @@ func (a *API) init() { a.QRCode = &qrcode.Client{Opt: &a.opt, APIRequester: a.apiRequester} a.Customer = &customer.Client{Opt: &a.opt, APIRequester: a.apiRequester} a.Transaction = &transaction.Client{Opt: &a.opt, APIRequester: a.apiRequester} + a.Report = &report.Client{Opt: &a.opt, APIRequester: a.apiRequester} } // New creates a new Xendit API client diff --git a/example/report/main.go b/example/report/main.go new file mode 100644 index 00000000..f4230ab0 --- /dev/null +++ b/example/report/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "github.com/joho/godotenv" + "github.com/xendit/xendit-go" + "github.com/xendit/xendit-go/report" + "log" + "os" +) + +func main() { + godotenvErr := godotenv.Load() + if godotenvErr != nil { + log.Fatal(godotenvErr) + } + + xendit.Opt.SecretKey = os.Getenv("SECRET_KEY") + + generateReportData := report.GenerateReportParams{ + Type: "BALANCE_HISTORY", // "BALANCE_HISTORY", "TRANSACTIONS", "UPCOMING_TRANSACTIONS" + Filter: report.Filter{ + From: "2020-01-01T00:00:00.000Z", + To: "2020-12-31T23:59:59.999Z", + }, + } + + resp, err := report.GenerateReport(&generateReportData) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("generated report: %+v\n", resp) + + getReportData := report.GetReportParams{ + ReportID: resp.ID, + } + + resp, err = report.GetReport(&getReportData) + if err != nil { + log.Fatal(err) + } + fmt.Printf("retrieved report: %+v\n", resp) +} diff --git a/integration_test/main.go b/integration_test/main.go index 7c609353..a5ca1258 100644 --- a/integration_test/main.go +++ b/integration_test/main.go @@ -26,6 +26,7 @@ var testFunctions = []func(){ transactionTest, accountTest, paymentmethodTest, + reportTest, } func main() { diff --git a/integration_test/report.go b/integration_test/report.go new file mode 100644 index 00000000..4461ea39 --- /dev/null +++ b/integration_test/report.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "github.com/xendit/xendit-go/report" + "log" +) + +func reportTest() { + respReport, err := report.GenerateReport(&report.GenerateReportParams{ + Type: "BALANCE_HISTORY", // "BALANCE_HISTORY", "TRANSACTIONS", "UPCOMING_TRANSACTIONS" + Filter: report.Filter{ + From: "2020-01-01T00:00:00.000Z", + To: "2020-12-31T23:59:59.999Z", + }, + }) + if err != nil { + log.Panic(err) + } + + _, err = report.GetReport(&report.GetReportParams{ + ReportID: respReport.ID, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("report integration tests done!") +} diff --git a/report.go b/report.go new file mode 100644 index 00000000..1256c198 --- /dev/null +++ b/report.go @@ -0,0 +1,21 @@ +package xendit + +import "time" + +type Report struct { + ID string `json:"id"` + Type string `json:"type"` + Filter Filter `json:"filter"` + Format string `json:"format"` + Status string `json:"status"` + Url string `json:"url,omitempty"` + Currency string `json:"currency"` + BusinessID string `json:"business_id"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` +} + +type Filter struct { + From string `json:"from"` + To string `json:"to"` +} diff --git a/report/client.go b/report/client.go new file mode 100644 index 00000000..df18d64a --- /dev/null +++ b/report/client.go @@ -0,0 +1,82 @@ +package report + +import ( + "context" + "fmt" + "github.com/xendit/xendit-go" + "github.com/xendit/xendit-go/utils/validator" + "net/http" +) + +type Client struct { + Opt *xendit.Option + APIRequester xendit.APIRequester +} + +// GenerateReport creates a report +func (c *Client) GenerateReport(data *GenerateReportParams) (*xendit.Report, *xendit.Error) { + return c.GenerateReportWithContext(context.Background(), data) +} + +// GenerateReportWithContext creates a report with context +func (c *Client) GenerateReportWithContext(ctx context.Context, data *GenerateReportParams) (*xendit.Report, *xendit.Error) { + if err := validator.ValidateRequired(ctx, data); err != nil { + return nil, validator.APIValidatorErr(err) + } + + response := &xendit.Report{} + + header := http.Header{} + if data.ForUserID != "" { + header.Add("for-user-id", data.ForUserID) + } + + err := c.APIRequester.Call( + ctx, + "POST", + fmt.Sprintf("%s/reports", c.Opt.XenditURL), + c.Opt.SecretKey, + header, + data, + response, + ) + if err != nil { + return nil, err + } + + return response, nil +} + +// GetReport gets a report +func (c *Client) GetReport(data *GetReportParams) (*xendit.Report, *xendit.Error) { + return c.GetReportWithContext(context.Background(), data) +} + +// GetReportWithContext gets a report with context +func (c *Client) GetReportWithContext(ctx context.Context, data *GetReportParams) (*xendit.Report, *xendit.Error) { + if err := validator.ValidateRequired(ctx, data); err != nil { + return nil, validator.APIValidatorErr(err) + } + + response := &xendit.Report{} + + header := http.Header{} + if data.ForUserID != "" { + header.Add("for-user-id", data.ForUserID) + } + + err := c.APIRequester.Call( + ctx, + "GET", + fmt.Sprintf("%s/reports/%s", c.Opt.XenditURL, data.ReportID), + c.Opt.SecretKey, + header, + nil, + response, + ) + if err != nil { + return nil, err + } + + return response, nil +} diff --git a/report/example_test.go b/report/example_test.go new file mode 100644 index 00000000..5e46a9d6 --- /dev/null +++ b/report/example_test.go @@ -0,0 +1,40 @@ +package report + +import ( + "github.com/xendit/xendit-go" + "log" +) + +func ExampleGenerateReport() { + xendit.Opt.SecretKey = "examplesecretkey" + + generateReport := GenerateReportParams{ + Type: "BALANCE_HISTORY", // "BALANCE_HISTORY", "TRANSACTIONS", "UPCOMING_TRANSACTIONS" + Filter: Filter{ + From: "2020-01-01T00:00:00.000Z", + To: "2020-12-31T23:59:59.999Z", + }, + } + + resp, err := GenerateReport(&generateReport) + if err != nil { + log.Fatal(err.ErrorCode) + } + + log.Printf("generated report: %+v\n", resp) +} + +func ExampleGetReport() { + xendit.Opt.SecretKey = "examplesecretkey" + + getReport := GetReportParams{ + ReportID: "report_5c1b34a2-6ceb-4c24-aba9-c836bac82b28", + } + + resp, err := GetReport(&getReport) + if err != nil { + log.Fatal(err.ErrorCode) + } + + log.Printf("retrieved report: %+v\n", resp) +} diff --git a/report/params.go b/report/params.go new file mode 100644 index 00000000..bd5595ba --- /dev/null +++ b/report/params.go @@ -0,0 +1,20 @@ +package report + +type Filter struct { + From string `json:"from"` + To string `json:"to"` +} + +type GenerateReportParams struct { + ForUserID string `json:"-"` + Type string `json:"type" validate:"required"` + Filter Filter `json:"filter" validate:"required"` + Format string `json:"format"` + Currency string `json:"currency"` + ReportVersion string `json:"report_version"` +} + +type GetReportParams struct { + ForUserID string `json:"-"` + ReportID string `json:"report_id" validate:"required"` +} diff --git a/report/report.go b/report/report.go new file mode 100644 index 00000000..725e7ce1 --- /dev/null +++ b/report/report.go @@ -0,0 +1,43 @@ +package report + +import ( + "context" + "github.com/xendit/xendit-go" +) + +// GenerateReport generates a report +func GenerateReport(data *GenerateReportParams) (*xendit.Report, *xendit.Error) { + return GenerateReportWithContext(context.Background(), data) +} + +// GenerateReportWithContext generates a report with context +func GenerateReportWithContext(ctx context.Context, data *GenerateReportParams) (*xendit.Report, *xendit.Error) { + client, err := getClient() + if err != nil { + return nil, err + } + + return client.GenerateReportWithContext(ctx, data) +} + +// GetReport gets a report +func GetReport(data *GetReportParams) (*xendit.Report, *xendit.Error) { + return GetReportWithContext(context.Background(), data) +} + +// GetReportWithContext gets a report with context +func GetReportWithContext(ctx context.Context, data *GetReportParams) (*xendit.Report, *xendit.Error) { + client, err := getClient() + if err != nil { + return nil, err + } + + return client.GetReportWithContext(ctx, data) +} + +func getClient() (*Client, *xendit.Error) { + return &Client{ + Opt: &xendit.Opt, + APIRequester: xendit.GetAPIRequester(), + }, nil +} diff --git a/report/report_test.go b/report/report_test.go new file mode 100644 index 00000000..2e98c8f8 --- /dev/null +++ b/report/report_test.go @@ -0,0 +1,171 @@ +package report + +import ( + "context" + "encoding/json" + "errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/xendit/xendit-go" + "github.com/xendit/xendit-go/utils/validator" + "net/http" + "testing" + "time" +) + +func initTesting(apiRequesterMockObj xendit.APIRequester) { + xendit.Opt.SecretKey = "examplesecretkey" + xendit.SetAPIRequester(apiRequesterMockObj) +} + +type apiRequesterMock struct { + mock.Mock +} + +func (m *apiRequesterMock) Call(ctx context.Context, method string, url string, secretKey string, header http.Header, body interface{}, result interface{}) *xendit.Error { + m.Called(ctx, method, url, secretKey, header, body, result) + + resultString := `{ + "id": "report_5c1b34a2-6ceb-4c24-aba9-c836bac82b28", + "type": "BALANCE_HISTORY", + "status": "COMPLETED", + "filter": { + "from": "2021-06-23T04:01:55.574Z", + "to": "2021-06-24T04:01:55.574Z" + }, + "format": "CSV", + "url": "https://transaction-report-files.s3-us-west-2.amazonaws.com/{report_name}", + "currency": "IDR", + "business_id": "5f34f60535ba7c1c0eed846a", + "created": "2021-06-24T04:01:55.570Z", + "updated": "2021-06-24T04:01:55.570Z" + }` + + _ = json.Unmarshal([]byte(resultString), &result) + + return nil +} + +func TestGenerateReport(t *testing.T) { + apiRequesterMockObj := new(apiRequesterMock) + initTesting(apiRequesterMockObj) + + created := time.Date(2021, 6, 24, 04, 01, 55, 570000000, time.UTC) + updated := time.Date(2021, 6, 24, 04, 01, 55, 570000000, time.UTC) + + testCases := []struct { + desc string + data *GenerateReportParams + expectedRes *xendit.Report + expectedErr *xendit.Error + }{ + { + desc: "should generate report", + data: &GenerateReportParams{ + Type: "BALANCE_HISTORY", // "BALANCE_HISTORY", "TRANSACTIONS", "UPCOMING_TRANSACTIONS" + Filter: Filter{ + From: "2021-06-23T04:01:55.574Z", + To: "2021-06-24T04:01:55.574Z", + }, + }, + expectedRes: &xendit.Report{ + ID: "report_5c1b34a2-6ceb-4c24-aba9-c836bac82b28", + Type: "BALANCE_HISTORY", + Status: "COMPLETED", + Filter: xendit.Filter{ + From: "2021-06-23T04:01:55.574Z", + To: "2021-06-24T04:01:55.574Z", + }, + Format: "CSV", + Url: "https://transaction-report-files.s3-us-west-2.amazonaws.com/{report_name}", + Currency: "IDR", + BusinessID: "5f34f60535ba7c1c0eed846a", + Created: created, + Updated: updated, + }, + }, + { + desc: "should report error", + data: &GenerateReportParams{}, + expectedErr: validator.APIValidatorErr(errors.New("Missing required fields: 'Type'")), + }, + } + + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + apiRequesterMockObj.On("Call", + context.Background(), + "POST", + xendit.Opt.XenditURL+"/reports", + xendit.Opt.SecretKey, + mock.Anything, + mock.Anything, + &xendit.Report{}, + ).Return(nil) + + resp, err := GenerateReport(tC.data) + assert.Equal(t, tC.expectedRes, resp) + assert.Equal(t, tC.expectedErr, err) + }) + } +} + +func TestGetReport(t *testing.T) { + apiRequesterMockObj := new(apiRequesterMock) + initTesting(apiRequesterMockObj) + + created := time.Date(2021, 6, 24, 04, 01, 55, 570000000, time.UTC) + updated := time.Date(2021, 6, 24, 04, 01, 55, 570000000, time.UTC) + + testCases := []struct { + desc string + data *GetReportParams + expectedRes *xendit.Report + expectedErr *xendit.Error + }{ + { + desc: "should generate report", + data: &GetReportParams{ + ReportID: "report_5c1b34a2-6ceb-4c24-aba9-c836bac82b28", + }, + expectedRes: &xendit.Report{ + ID: "report_5c1b34a2-6ceb-4c24-aba9-c836bac82b28", + Type: "BALANCE_HISTORY", + Status: "COMPLETED", + Filter: xendit.Filter{ + From: "2021-06-23T04:01:55.574Z", + To: "2021-06-24T04:01:55.574Z", + }, + Format: "CSV", + Url: "https://transaction-report-files.s3-us-west-2.amazonaws.com/{report_name}", + Currency: "IDR", + BusinessID: "5f34f60535ba7c1c0eed846a", + Created: created, + Updated: updated, + }, + }, + { + desc: "should report error", + data: &GetReportParams{}, + expectedErr: validator.APIValidatorErr(errors.New("Missing required fields: 'ReportID'")), + }, + } + + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + apiRequesterMockObj.On("Call", + context.Background(), + "GET", + xendit.Opt.XenditURL+"/reports/"+tC.data.ReportID, + xendit.Opt.SecretKey, + mock.Anything, + mock.Anything, + &xendit.Report{}, + ).Return(nil) + + resp, err := GetReport(tC.data) + assert.Equal(t, tC.expectedRes, resp) + assert.Equal(t, tC.expectedErr, err) + }) + } +}