Created
This commit is contained in:
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# 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
|
||||
|
||||
# Test coverage output
|
||||
coverage*.*
|
||||
|
||||
# postgres data volume used by postgres server container for testing purpose
|
||||
testdata/postgres
|
||||
|
||||
# server binary
|
||||
./server
|
||||
|
||||
# PID file generated to support live reload
|
||||
.pid
|
||||
|
||||
.idea/
|
||||
37
CHANGELOG.md
Normal file
37
CHANGELOG.md
Normal file
@@ -0,0 +1,37 @@
|
||||
WooCommerce for golang Change Log
|
||||
=================================
|
||||
|
||||
## 1.0.4 under development
|
||||
|
||||
-Bug: #1 Fixed signature error if query params include array value
|
||||
|
||||
## 1.0.3
|
||||
|
||||
- Bug: Fixed date type params validation
|
||||
- Bug: Fixed Setting group and option properties
|
||||
- Enh: Add 501 error handling
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- Bug: Fixed parse string to float64 failed if an empty string
|
||||
- New: Added data and report service tests
|
||||
- Bug: Fixed an issue report query parameters did not take effect
|
||||
- Bug: Fixed report struct error
|
||||
- Chg: Modify order money field type from string to float64
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- Enh: Perfect doc
|
||||
- Enh: Perfect product variation query params validation
|
||||
- Bug: Fixed All() method isLastPage return error
|
||||
- Chg: Simplify query params process
|
||||
- Bug: Fixed Include, Exclude query params type error
|
||||
- Bug: Fixed shipping zone location endpoint error
|
||||
- Enh: Set per page is max to 100
|
||||
- Bug: Fixed is last page check condition
|
||||
- Enh: Add total and totalPages return in All() method
|
||||
- Chg: product and product variation price, weight attribute change to float64
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- Initial release.
|
||||
29
LICENSE
Normal file
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022, hiscaler
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
14
config/config.go
Normal file
14
config/config.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
type Config struct {
|
||||
Debug bool `json:"debug"` // 是否为调试模式
|
||||
URL string `json:"url"` // 店铺地址
|
||||
Version string `json:"version"` // API 版本
|
||||
ConsumerKey string `json:"consumer_key"` // Consumer Key
|
||||
ConsumerSecret string `json:"consumer_secret"` // Consumer Secret
|
||||
AddAuthenticationToURL bool `json:"add_authentication_to_url"` // 是否将认证信息附加到 URL 中
|
||||
Timeout time.Duration `json:"timeout"` // 超时时间(秒)
|
||||
VerifySSL bool `json:"verify_ssl"` // 是否验证 SSL
|
||||
}
|
||||
10
config/config_test.json
Normal file
10
config/config_test.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"debug": true,
|
||||
"url": "http://127.0.0.1/",
|
||||
"version": "v3",
|
||||
"consumer_key": "",
|
||||
"consumer_secret": "",
|
||||
"add_authentication_to_url": false,
|
||||
"timeout": 10,
|
||||
"verify_ssl": true
|
||||
}
|
||||
8
constant/datetime.format.go
Normal file
8
constant/datetime.format.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
DateFormat = "2006-01-02"
|
||||
TimeFormat = "15:04:05"
|
||||
DatetimeFormat = "2006-01-02 15:04:05"
|
||||
WooDatetimeFormat = "2006-01-02T15:04:05"
|
||||
)
|
||||
208
coupon.go
Normal file
208
coupon.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// https://woocommerce.github.io/woocommerce-rest-api-docs/?php#coupon-properties
|
||||
|
||||
type couponService service
|
||||
|
||||
type CouponsQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
After string `url:"after,omitempty"`
|
||||
Before string `url:"before,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
Code string `url:"code,omitempty"`
|
||||
}
|
||||
|
||||
func (m CouponsQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Before, validation.When(m.Before != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.After, validation.When(m.After != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "include", "date", "title", "slug").Error("无效的排序字段"))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all coupons
|
||||
func (s couponService) All(params CouponsQueryParams) (items []entity.Coupon, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
params.After = ToISOTimeString(params.After, false, true)
|
||||
params.Before = ToISOTimeString(params.Before, true, false)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/coupons")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a coupon
|
||||
func (s couponService) One(id int) (item entity.Coupon, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/coupons/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateCouponRequest struct {
|
||||
Code string `json:"code"`
|
||||
DiscountType string `json:"discount_type"`
|
||||
Amount float64 `json:"amount,string"`
|
||||
IndividualUse bool `json:"individual_use"`
|
||||
ExcludeSaleItems bool `json:"exclude_sale_items"`
|
||||
MinimumAmount float64 `json:"minimum_amount,string"`
|
||||
}
|
||||
|
||||
func (m CreateCouponRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.DiscountType, validation.In("percent", "fixed_cart", "fixed_product").Error("无效的折扣类型")),
|
||||
validation.Field(&m.Amount,
|
||||
validation.Min(0.0).Error("金额不能小于 {{.threshold}}"),
|
||||
validation.When(m.DiscountType == "percent", validation.Max(100.0).Error("折扣比例不能大于 {{.threshold}}")),
|
||||
),
|
||||
validation.Field(&m.MinimumAmount, validation.Min(0.0).Error("最小金额不能小于 {{.threshold}}")),
|
||||
)
|
||||
}
|
||||
|
||||
// Create Create a coupon
|
||||
func (s couponService) Create(req CreateCouponRequest) (item entity.Coupon, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/coupons")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateCouponRequest struct {
|
||||
Code string `json:"code,omitempty"`
|
||||
DiscountType string `json:"discount_type,omitempty"`
|
||||
Amount float64 `json:"amount,omitempty,string"`
|
||||
IndividualUse bool `json:"individual_use,omitempty"`
|
||||
ExcludeSaleItems bool `json:"exclude_sale_items,omitempty"`
|
||||
MinimumAmount float64 `json:"minimum_amount,omitempty,string"`
|
||||
}
|
||||
|
||||
func (m UpdateCouponRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.DiscountType, validation.When(m.DiscountType != "", validation.In("percent", "fixed_cart", "fixed_product").Error("无效的折扣类型"))),
|
||||
validation.Field(&m.Amount,
|
||||
validation.Min(0.0).Error("金额不能小于 {{.threshold}}"),
|
||||
validation.When(m.DiscountType == "percent", validation.Max(100.0).Error("折扣比例不能大于 {{.threshold}}")),
|
||||
),
|
||||
validation.Field(&m.MinimumAmount, validation.Min(0.0).Error("最小金额不能小于 {{.threshold}}")),
|
||||
)
|
||||
}
|
||||
|
||||
// Update Update a coupon
|
||||
func (s couponService) Update(id int, req UpdateCouponRequest) (item entity.Coupon, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/coupons/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete a coupon
|
||||
|
||||
func (s couponService) Delete(id int, force bool) (item entity.Coupon, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/coupons/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update coupons
|
||||
|
||||
type BatchCouponsCreateItem = CreateCouponRequest
|
||||
type BatchCouponsUpdateItem struct {
|
||||
ID string `json:"id"`
|
||||
BatchCouponsCreateItem
|
||||
}
|
||||
|
||||
type BatchCouponsRequest struct {
|
||||
Create []BatchCouponsCreateItem `json:"create,omitempty"`
|
||||
Update []BatchCouponsUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchCouponsRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchCouponsResult struct {
|
||||
Create []entity.Coupon `json:"create"`
|
||||
Update []entity.Coupon `json:"update"`
|
||||
Delete []entity.Coupon `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete coupons
|
||||
func (s couponService) Batch(req BatchCouponsRequest) (res BatchCouponsResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/coupons/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
149
coupon_test.go
Normal file
149
coupon_test.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/jsonx"
|
||||
"git.cloudyne.io/go/hiscaler-gox/randx"
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCouponService_All(t *testing.T) {
|
||||
params := CouponsQueryParams{Search: ""}
|
||||
params.PerPage = 100
|
||||
params.Order = SortDesc
|
||||
items, _, _, _, err := wooClient.Services.Coupon.All(params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Coupon.All error: %s", err.Error())
|
||||
} else {
|
||||
t.Logf("items = %s", jsonx.ToPrettyJson(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCouponService_One(t *testing.T) {
|
||||
couponId := 4
|
||||
item, err := wooClient.Services.Coupon.One(couponId)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Coupon.One error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, couponId, item.ID, "coupon id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCouponService_Create(t *testing.T) {
|
||||
code := strings.ToLower(randx.Letter(8, false))
|
||||
req := CreateCouponRequest{
|
||||
Code: code,
|
||||
DiscountType: "percent",
|
||||
Amount: 1,
|
||||
IndividualUse: false,
|
||||
ExcludeSaleItems: false,
|
||||
MinimumAmount: 2,
|
||||
}
|
||||
item, err := wooClient.Services.Coupon.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Coupon.Create error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, code, item.Code, "code")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCouponService_CreateUpdateDelete(t *testing.T) {
|
||||
code := gofakeit.LetterN(8)
|
||||
req := CreateCouponRequest{
|
||||
Code: code,
|
||||
DiscountType: "percent",
|
||||
Amount: 1,
|
||||
IndividualUse: false,
|
||||
ExcludeSaleItems: false,
|
||||
MinimumAmount: 2,
|
||||
}
|
||||
var oldItem, newItem entity.Coupon
|
||||
var err error
|
||||
oldItem, err = wooClient.Services.Coupon.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Coupon.Create error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, code, oldItem.Code, "code")
|
||||
}
|
||||
|
||||
newItem, err = wooClient.Services.Coupon.One(oldItem.ID)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Customer.One(%d) error: %s", oldItem.ID, err.Error())
|
||||
} else {
|
||||
updateReq := UpdateCouponRequest{
|
||||
Amount: 11,
|
||||
IndividualUse: true,
|
||||
ExcludeSaleItems: true,
|
||||
MinimumAmount: 22,
|
||||
}
|
||||
newItem, err = wooClient.Services.Coupon.Update(oldItem.ID, updateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Coupon.Update error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, oldItem.Code, newItem.Code, "code")
|
||||
assert.Equal(t, 11.0, newItem.Amount, "Amount")
|
||||
assert.Equal(t, true, newItem.IndividualUse, "IndividualUse")
|
||||
assert.Equal(t, true, newItem.ExcludeSaleItems, "ExcludeSaleItems")
|
||||
assert.Equal(t, 22.0, newItem.MinimumAmount, "MinimumAmount")
|
||||
}
|
||||
|
||||
// Only change amount
|
||||
updateReq = UpdateCouponRequest{Amount: 11.23}
|
||||
newItem, err = wooClient.Services.Coupon.Update(oldItem.ID, updateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Coupon.Update error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, 11.23, newItem.Amount, "Amount")
|
||||
assert.Equal(t, true, newItem.IndividualUse, "IndividualUse")
|
||||
assert.Equal(t, true, newItem.ExcludeSaleItems, "ExcludeSaleItems")
|
||||
assert.Equal(t, 22.0, newItem.MinimumAmount, "MinimumAmount")
|
||||
}
|
||||
|
||||
_, err = wooClient.Services.Coupon.Delete(oldItem.ID, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Coupon.Delete(%d) error: %s", oldItem.ID, err.Error())
|
||||
} else {
|
||||
_, err = wooClient.Services.Coupon.One(oldItem.ID)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("wooClient.Services.Coupon.Delete(%d) failed", oldItem.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCouponService_Batch(t *testing.T) {
|
||||
n := 3
|
||||
createRequests := make([]BatchCouponsCreateItem, n)
|
||||
codes := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
code := strings.ToLower(gofakeit.LetterN(8))
|
||||
req := BatchCouponsCreateItem{
|
||||
Code: code,
|
||||
DiscountType: "percent",
|
||||
Amount: float64(i),
|
||||
IndividualUse: false,
|
||||
ExcludeSaleItems: false,
|
||||
MinimumAmount: float64(i),
|
||||
}
|
||||
createRequests[i] = req
|
||||
codes[i] = req.Code
|
||||
}
|
||||
batchReq := BatchCouponsRequest{
|
||||
Create: createRequests,
|
||||
}
|
||||
result, err := wooClient.Services.Coupon.Batch(batchReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Coupon.Batch() error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, n, len(result.Create), "Batch create return len")
|
||||
returnCodes := make([]string, 0)
|
||||
for _, d := range result.Create {
|
||||
returnCodes = append(returnCodes, d.Code)
|
||||
}
|
||||
assert.Equal(t, codes, returnCodes, "check codes is equal")
|
||||
}
|
||||
219
customer.go
Normal file
219
customer.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// https://woocommerce.github.io/woocommerce-rest-api-docs/?php#customers
|
||||
|
||||
type customerService service
|
||||
|
||||
type CustomersQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
Email string `url:"email,omitempty"`
|
||||
Role string `url:"role,omitempty"`
|
||||
}
|
||||
|
||||
func (m CustomersQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "include", "name", "registered_date").Error("无效的排序字段"))),
|
||||
validation.Field(&m.Email, validation.When(m.Email != "", is.EmailFormat.Error("无效的邮箱"))),
|
||||
validation.Field(&m.Role, validation.When(m.Role != "", validation.In("all", "administrator", "editor", "author", "contributor", "subscriber", "shop_manager").Error("无效的角色"))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all customers
|
||||
func (s customerService) All(params CustomersQueryParams) (items []entity.Customer, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/customers")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a customer
|
||||
func (s customerService) One(id int) (item entity.Customer, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/customers/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateCustomerRequest Create customer request
|
||||
type CreateCustomerRequest struct {
|
||||
Email string `json:"email,omitempty"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Billing *entity.Billing `json:"billing,omitempty"`
|
||||
Shipping *entity.Shipping `json:"shipping,omitempty"`
|
||||
MetaData []entity.Meta `json:"meta_data,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateCustomerRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Email,
|
||||
validation.Required.Error("邮箱不能为空"),
|
||||
is.EmailFormat.Error("无效的邮箱"),
|
||||
),
|
||||
validation.Field(&m.FirstName, validation.Required.Error("姓不能为空")),
|
||||
validation.Field(&m.LastName, validation.Required.Error("名不能为空")),
|
||||
validation.Field(&m.Username, validation.Required.Error("登录帐号不能为空")),
|
||||
validation.Field(&m.Password, validation.Required.Error("登录密码不能为空")),
|
||||
validation.Field(&m.Billing),
|
||||
)
|
||||
}
|
||||
|
||||
// Create create a customer
|
||||
func (s customerService) Create(req CreateCustomerRequest) (item entity.Customer, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/customers")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update customer
|
||||
|
||||
type UpdateCustomerRequest struct {
|
||||
Email string `json:"email,omitempty"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Billing *entity.Billing `json:"billing,omitempty"`
|
||||
Shipping *entity.Shipping `json:"shipping,omitempty"`
|
||||
MetaData []entity.Meta `json:"meta_data,omitempty"`
|
||||
}
|
||||
|
||||
func (m UpdateCustomerRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Email, validation.When(m.Email != "", is.EmailFormat.Error("无效的邮箱"))),
|
||||
validation.Field(&m.Billing),
|
||||
)
|
||||
}
|
||||
|
||||
// Update update a customer
|
||||
func (s customerService) Update(id int, req UpdateCustomerRequest) (item entity.Customer, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/customers/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete Delete a customer
|
||||
|
||||
type customerDeleteParams struct {
|
||||
Force bool `json:"force,omitempty"`
|
||||
Reassign []int `json:"reassign,omitempty"`
|
||||
}
|
||||
|
||||
func (s customerService) Delete(id int, params customerDeleteParams) (item entity.Customer, err error) {
|
||||
resp, err := s.httpClient.R().SetBody(params).Delete(fmt.Sprintf("/customers/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update customers
|
||||
|
||||
type BatchCreateCustomerRequest = CreateCustomerRequest
|
||||
|
||||
type BatchUpdateCustomerRequest struct {
|
||||
ID string `json:"id"`
|
||||
BatchCreateCustomerRequest
|
||||
}
|
||||
|
||||
type BatchCustomerRequest struct {
|
||||
Create []BatchCreateCustomerRequest `json:"create,omitempty"`
|
||||
Update []BatchUpdateCustomerRequest `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchCustomerRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchCustomerResult struct {
|
||||
Create []entity.Customer `json:"create"`
|
||||
Update []entity.Customer `json:"update"`
|
||||
Delete []entity.Customer `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete customers
|
||||
func (s customerService) Batch(req BatchCustomerRequest) (res BatchCustomerResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/customers/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Downloads retrieve a customer downloads
|
||||
func (s customerService) Downloads(customerId int) (items []entity.CustomerDownload, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/customers/%d/downloads", customerId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
189
customer_test.go
Normal file
189
customer_test.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/jsonx"
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getCustomerId(t *testing.T) {
|
||||
t.Log("Execute getCustomerId test")
|
||||
params := CustomersQueryParams{}
|
||||
params.Page = 1
|
||||
params.PerPage = 1
|
||||
items, _, _, _, err := wooClient.Services.Customer.All(params)
|
||||
if err != nil || len(items) == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
if len(items) == 0 {
|
||||
t.Fatalf("getCustomerId not found one customer")
|
||||
}
|
||||
mainId = items[0].ID
|
||||
}
|
||||
|
||||
func TestCustomerService_All(t *testing.T) {
|
||||
params := CustomersQueryParams{}
|
||||
_, _, _, _, err := wooClient.Services.Customer.All(params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Customer.All error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomerService_One(t *testing.T) {
|
||||
t.Run("getCustomerId", getCustomerId)
|
||||
item, err := wooClient.Services.Customer.One(mainId)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Customer.One error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, mainId, item.ID, "customer id")
|
||||
}
|
||||
|
||||
func TestCustomerService_Create(t *testing.T) {
|
||||
gofakeit.Seed(0)
|
||||
address := gofakeit.Address()
|
||||
req := CreateCustomerRequest{
|
||||
Email: gofakeit.Email(),
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
Username: gofakeit.Username(),
|
||||
Password: gofakeit.Password(true, true, true, false, false, 10),
|
||||
MetaData: nil,
|
||||
Billing: &entity.Billing{
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
Company: gofakeit.Company(),
|
||||
Address1: address.Address,
|
||||
Address2: "",
|
||||
City: address.City,
|
||||
State: address.State,
|
||||
Postcode: address.Zip,
|
||||
Country: address.Country,
|
||||
Email: gofakeit.Email(),
|
||||
Phone: gofakeit.Phone(),
|
||||
},
|
||||
}
|
||||
item, err := wooClient.Services.Customer.Create(req)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Customer.Create error: %s", err.Error())
|
||||
} else {
|
||||
t.Logf("item = %#v", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomerService_CreateUpdateDelete(t *testing.T) {
|
||||
gofakeit.Seed(0)
|
||||
// Create
|
||||
var oldItem, newItem entity.Customer
|
||||
var err error
|
||||
address := gofakeit.Address()
|
||||
req := CreateCustomerRequest{
|
||||
Email: gofakeit.Email(),
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
Username: gofakeit.Username(),
|
||||
Password: gofakeit.Password(true, true, true, false, false, 10),
|
||||
MetaData: nil,
|
||||
Billing: &entity.Billing{
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
Company: gofakeit.Company(),
|
||||
Address1: address.Address,
|
||||
Address2: "",
|
||||
City: address.City,
|
||||
State: address.State,
|
||||
Postcode: address.Zip,
|
||||
Country: address.Country,
|
||||
Email: gofakeit.Email(),
|
||||
Phone: gofakeit.Phone(),
|
||||
},
|
||||
}
|
||||
oldItem, err = wooClient.Services.Customer.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Customer.Create error: %s", err.Error())
|
||||
}
|
||||
|
||||
// Update
|
||||
afterData := struct {
|
||||
email string
|
||||
billingFirstName string
|
||||
billingLastName string
|
||||
}{
|
||||
email: gofakeit.Email(),
|
||||
billingFirstName: gofakeit.FirstName(),
|
||||
billingLastName: gofakeit.LastName(),
|
||||
}
|
||||
updateReq := UpdateCustomerRequest{
|
||||
Email: afterData.email,
|
||||
Billing: &entity.Billing{
|
||||
FirstName: afterData.billingFirstName,
|
||||
LastName: afterData.billingLastName,
|
||||
},
|
||||
}
|
||||
newItem, err = wooClient.Services.Customer.Update(oldItem.ID, updateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Customer.Update error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, afterData.email, newItem.Email, "email")
|
||||
assert.Equal(t, afterData.billingFirstName, newItem.Billing.FirstName, "billing first name")
|
||||
assert.Equal(t, afterData.billingLastName, newItem.Billing.LastName, "billing last name")
|
||||
}
|
||||
|
||||
// Delete
|
||||
_, err = wooClient.Services.Customer.Delete(oldItem.ID, customerDeleteParams{Force: true})
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Customer.Delete(%d) error: %s", oldItem.ID, err.Error())
|
||||
}
|
||||
|
||||
// Query check is exists
|
||||
_, err = wooClient.Services.Customer.One(oldItem.ID)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("wooClient.Services.Customer.Delete(%d) failed", oldItem.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomerService_Batch(t *testing.T) {
|
||||
n := 3
|
||||
createRequests := make([]BatchCreateCustomerRequest, n)
|
||||
emails := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
req := BatchCreateCustomerRequest{
|
||||
Email: gofakeit.Email(),
|
||||
FirstName: gofakeit.FirstName(),
|
||||
LastName: gofakeit.LastName(),
|
||||
Username: gofakeit.Username(),
|
||||
Password: gofakeit.Password(true, true, true, false, false, 10),
|
||||
Billing: nil,
|
||||
Shipping: nil,
|
||||
MetaData: nil,
|
||||
}
|
||||
createRequests[i] = req
|
||||
emails[i] = req.Email
|
||||
}
|
||||
batchReq := BatchCustomerRequest{
|
||||
Create: createRequests,
|
||||
}
|
||||
result, err := wooClient.Services.Customer.Batch(batchReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Customer.Batch() error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, n, len(result.Create), "Batch create return len")
|
||||
returnEmails := make([]string, 0)
|
||||
for _, d := range result.Create {
|
||||
returnEmails = append(returnEmails, d.Email)
|
||||
}
|
||||
assert.Equal(t, emails, returnEmails, "check emails is equal")
|
||||
}
|
||||
|
||||
func TestCustomerService_Downloads(t *testing.T) {
|
||||
// todo
|
||||
items, err := wooClient.Services.Customer.Downloads(0)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Customer.Downloads() error: %s", err.Error())
|
||||
} else {
|
||||
t.Logf("items = %s", jsonx.ToPrettyJson(items))
|
||||
}
|
||||
}
|
||||
114
data.go
Normal file
114
data.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type dataService service
|
||||
|
||||
// All list all data
|
||||
func (s dataService) All() (items []entity.Data, err error) {
|
||||
resp, err := s.httpClient.R().Get("/data")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Continents list all continents
|
||||
func (s dataService) Continents() (items []entity.Continent, err error) {
|
||||
resp, err := s.httpClient.R().Get("/data/continents")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Continent retrieve continent data
|
||||
func (s dataService) Continent(code string) (item entity.Continent, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/data/continents/%s", code))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Countries list all countries
|
||||
func (s dataService) Countries() (items []entity.Country, err error) {
|
||||
resp, err := s.httpClient.R().Get("/data/countries")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Country retrieve country data
|
||||
func (s dataService) Country(code string) (item entity.Country, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/data/countries/%s", code))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Currencies list all currencies
|
||||
func (s dataService) Currencies() (items []entity.Currency, err error) {
|
||||
resp, err := s.httpClient.R().Get("/data/currencies")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Currency retrieve currency data
|
||||
func (s dataService) Currency(code string) (item entity.Currency, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/data/currencies/%s", code))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CurrentCurrency retrieve current currency
|
||||
func (s dataService) CurrentCurrency() (item entity.Currency, err error) {
|
||||
resp, err := s.httpClient.R().Get("/data/currencies/current")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
32
data_test.go
Normal file
32
data_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDataService_All(t *testing.T) {
|
||||
_, err := wooClient.Services.Data.All()
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
func TestDataService_Countries(t *testing.T) {
|
||||
_, err := wooClient.Services.Data.Countries()
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
func TestDataService_Currencies(t *testing.T) {
|
||||
_, err := wooClient.Services.Data.Currencies()
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
func TestDataService_Continents(t *testing.T) {
|
||||
_, err := wooClient.Services.Data.Continents()
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
func TestDataService_Continent(t *testing.T) {
|
||||
_, err := wooClient.Services.Data.Continent("AF")
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
29
entity/billing.go
Normal file
29
entity/billing.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
)
|
||||
|
||||
// Billing order billing properties
|
||||
type Billing struct {
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Company string `json:"company,omitempty"`
|
||||
Address1 string `json:"address_1,omitempty"`
|
||||
Address2 string `json:"address_2,omitempty"`
|
||||
City string `json:"city,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Postcode string `json:"postcode,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
}
|
||||
|
||||
func (m Billing) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Email, validation.When(m.Email != "", is.EmailFormat.Error("无效的邮箱"))),
|
||||
validation.Field(&m.FirstName, validation.Required.Error("姓不能为空")),
|
||||
validation.Field(&m.LastName, validation.Required.Error("名不能为空")),
|
||||
)
|
||||
}
|
||||
32
entity/coupon.go
Normal file
32
entity/coupon.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package entity
|
||||
|
||||
// Coupon coupon properties
|
||||
type Coupon struct {
|
||||
ID int `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Amount float64 `json:"amount"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
DateModified string `json:"date_modified"`
|
||||
DateModifiedGMT string `json:"date_modified_gmt"`
|
||||
DiscountType string `json:"discount_type"`
|
||||
Description string `json:"description"`
|
||||
DateExpires string `json:"date_expires"`
|
||||
DateExpiresGMT string `json:"date_expires_gmt"`
|
||||
UsageCount int `json:"usage_count"`
|
||||
IndividualUse bool `json:"individual_use"`
|
||||
ProductIDs []int `json:"product_ids"`
|
||||
ExcludedProductIDs []int `json:"excluded_product_ids"`
|
||||
UsageLimit int `json:"usage_limit"`
|
||||
UsageLimitPerUser int `json:"usage_limit_per_user"`
|
||||
LimitUsageToXItems int `json:"limit_usage_to_x_items"`
|
||||
FreeShipping bool `json:"free_shipping"`
|
||||
ProductCategories []int `json:"product_categories"`
|
||||
ExcludedProductCategories []int `json:"excluded_product_categories"`
|
||||
ExcludeSaleItems bool `json:"exclude_sale_items"`
|
||||
MinimumAmount float64 `json:"minimum_amount"`
|
||||
MaximumAmount float64 `json:"maximum_amount"`
|
||||
EmailRestrictions []string `json:"email_restrictions"`
|
||||
UsedBy []int `json:"used_by"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
21
entity/customer.go
Normal file
21
entity/customer.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package entity
|
||||
|
||||
// Customer customer properties
|
||||
type Customer struct {
|
||||
ID int `json:"id"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
DateModified string `json:"date_modified"`
|
||||
DateModifiedGMT string `json:"date_modified_gmt"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
Role string `json:"role"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Billing Billing `json:"billing"`
|
||||
Shipping Shipping `json:"shipping"`
|
||||
IsPayingCustomer bool `json:"is_paying_customer"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
18
entity/customer_download.go
Normal file
18
entity/customer_download.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package entity
|
||||
|
||||
// https://woocommerce.github.io/woocommerce-rest-api-docs/?php#retrieve-customer-downloads
|
||||
|
||||
// CustomerDownload customer download properties
|
||||
type CustomerDownload struct {
|
||||
DownloadId string `json:"download_id"`
|
||||
DownloadURL string `json:"download_url"`
|
||||
ProductId string `json:"product_id"`
|
||||
ProductName string `json:"product_name"`
|
||||
DownloadName string `json:"download_name"`
|
||||
OrderId int `json:"order_id"`
|
||||
OrderKey string `json:"order_key"`
|
||||
DownloadRemaining string `json:"download_remaining"`
|
||||
AccessExpires string `json:"access_expires"`
|
||||
AccessExpiresGMT string `json:"access_expires_gmt"`
|
||||
File CustomerDownloadFile `json:"file"`
|
||||
}
|
||||
8
entity/customer_download_file.go
Normal file
8
entity/customer_download_file.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package entity
|
||||
|
||||
// https://woocommerce.github.io/woocommerce-rest-api-docs/?php#retrieve-customer-downloads
|
||||
|
||||
type CustomerDownloadFile struct {
|
||||
Name string `json:"name"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
48
entity/data.go
Normal file
48
entity/data.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package entity
|
||||
|
||||
// Data data properties
|
||||
type Data struct {
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// Continent continent properties
|
||||
type Continent struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Countries []ContinentCountry `json:"countries"` // Only code, name, []state?
|
||||
}
|
||||
|
||||
// ContinentCountry continent country properties
|
||||
type ContinentCountry struct {
|
||||
Code string `json:"code"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
CurrencyPos string `json:"currency_pos"`
|
||||
DecimalSep string `json:"decimal_sep"`
|
||||
DimensionUnit string `json:"dimension_unit"`
|
||||
Name string `json:"name"`
|
||||
NumDecimals int `json:"num_decimals"`
|
||||
States []State `json:"states"`
|
||||
ThousandSep string `json:"thousand_sep"`
|
||||
WeightUnit string `json:"weight_unit"`
|
||||
}
|
||||
|
||||
// State state properties
|
||||
type State struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Country country properties
|
||||
type Country struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
States []State `json:"states"`
|
||||
}
|
||||
|
||||
// Currency currency properties
|
||||
type Currency struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
}
|
||||
13
entity/image.go
Normal file
13
entity/image.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package entity
|
||||
|
||||
// ProductImage product iamge properties
|
||||
type ProductImage struct {
|
||||
ID int `json:"id"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
DateModified string `json:"date_modified"`
|
||||
DateModifiedGMT string `json:"date_modified_gmt"`
|
||||
Src string `json:"src"`
|
||||
Name string `json:"name"`
|
||||
Alt string `json:"alt"`
|
||||
}
|
||||
7
entity/meta.go
Normal file
7
entity/meta.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package entity
|
||||
|
||||
type Meta struct {
|
||||
ID int `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
91
entity/order.go
Normal file
91
entity/order.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package entity
|
||||
|
||||
type LineItem struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ProductId int `json:"product_id"`
|
||||
VariationId int `json:"variation_id"`
|
||||
Quantity int `json:"quantity"`
|
||||
TaxClass string `json:"tax_class"`
|
||||
SubTotal float64 `json:"subtotal"`
|
||||
SubTotalTax float64 `json:"subtotal_tax"`
|
||||
Total float64 `json:"total"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
Taxes []Tax `json:"taxes"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
SKU string `json:"sku"`
|
||||
Price float64 `json:"price"`
|
||||
ParentName string `json:"parent_name"`
|
||||
}
|
||||
|
||||
type FeeLine struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
TaxClass string `json:"tax_class"`
|
||||
TaxStatus string `json:"tax_status"`
|
||||
Total float64 `json:"total"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
Taxes []Tax `json:"taxes"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
|
||||
type CouponLine struct {
|
||||
ID int `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Discount float64 `json:"discount"`
|
||||
DiscountTax float64 `json:"discount_tax"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
|
||||
type Refund struct {
|
||||
ID int `json:"id"`
|
||||
Reason string `json:"reason"`
|
||||
Total float64 `json:"total"`
|
||||
}
|
||||
|
||||
// Order order properties
|
||||
type Order struct {
|
||||
ID int `json:"id"`
|
||||
ParentId int `json:"parent_id"`
|
||||
Number string `json:"number"`
|
||||
OrderKey string `json:"order_key"`
|
||||
CreatedVia string `json:"created_via"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
Currency string `json:"currency"`
|
||||
CurrencySymbol string `json:"currency_symbol"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
DateModified string `json:"date_modified"`
|
||||
DateModifiedGMT string `json:"date_modified_gmt"`
|
||||
DiscountTotal float64 `json:"discount_total"`
|
||||
DiscountTax float64 `json:"discount_tax"`
|
||||
ShippingTotal float64 `json:"shipping_total"`
|
||||
ShippingTax float64 `json:"shipping_tax"`
|
||||
CartTax float64 `json:"cart_tax"`
|
||||
Total float64 `json:"total"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
PricesIncludeTax bool `json:"prices_include_tax"`
|
||||
CustomerId int `json:"customer_id"`
|
||||
CustomerIpAddress string `json:"customer_ip_address"`
|
||||
CustomerUserAgent string `json:"customer_user_agent"`
|
||||
CustomerNote string `json:"customer_note"`
|
||||
Billing Billing `json:"billing"`
|
||||
Shipping Shipping `json:"shipping"`
|
||||
PaymentMethod string `json:"payment_method"`
|
||||
PaymentMethodTitle string `json:"payment_method_title"`
|
||||
TransactionId string `json:"transaction_id"`
|
||||
DatePaid string `json:"date_paid"`
|
||||
DatePaidGMT string `json:"date_paid_gmt"`
|
||||
DateCompleted string `json:"date_completed"`
|
||||
DateCompletedGMT string `json:"date_completed_gmt"`
|
||||
CartHash string `json:"cart_hash"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
LineItems []LineItem `json:"line_items"`
|
||||
TaxLines []TaxLine `json:"tax_lines"`
|
||||
ShippingLines []ShippingLine `json:"shipping_lines"`
|
||||
FeeLines []FeeLine `json:"fee_lines"`
|
||||
CouponLines []CouponLine `json:"coupon_lines"`
|
||||
Refunds []Refund `json:"refunds"`
|
||||
SetPaid bool `json:"set_paid"`
|
||||
}
|
||||
12
entity/order_note.go
Normal file
12
entity/order_note.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package entity
|
||||
|
||||
// OrderNote order note properties
|
||||
type OrderNote struct {
|
||||
ID int `json:"id"`
|
||||
Author string `json:"author"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
Note string `json:"note"`
|
||||
CustomerNote bool `json:"customer_note"`
|
||||
AddedByUser bool `json:"added_by_user"`
|
||||
}
|
||||
34
entity/order_refund.go
Normal file
34
entity/order_refund.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package entity
|
||||
|
||||
// OrderRefund order refund properties
|
||||
type OrderRefund struct {
|
||||
ID int `json:"id"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
Amount float64 `json:"amount"`
|
||||
Reason string `json:"reason"`
|
||||
RefundedBy int `json:"refunded_by"`
|
||||
RefundedPayment bool `json:"refunded_payment"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
LineItems []OrderRefundLineItem `json:"line_items"`
|
||||
APIRefund bool `json:"api_refund"`
|
||||
}
|
||||
|
||||
// OrderRefundLineItem order refund line item properties
|
||||
type OrderRefundLineItem struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ProductId int `json:"product_id"`
|
||||
VariationId int `json:"variation_id"`
|
||||
Quantity int `json:"quantity"`
|
||||
TaxClass int `json:"tax_class"`
|
||||
SubTotal float64 `json:"subtotal"`
|
||||
SubTotalTax float64 `json:"subtotal_tax"`
|
||||
Total float64 `json:"total"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
Taxes []Tax `json:"taxes"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
SKU string `json:"sku"`
|
||||
Price float64 `json:"price"`
|
||||
RefundTotal float64 `json:"refund_total"`
|
||||
}
|
||||
25
entity/payment_gateway.go
Normal file
25
entity/payment_gateway.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package entity
|
||||
|
||||
// PaymentGateway payment gateway properties
|
||||
type PaymentGateway struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Order int `json:"order"`
|
||||
Enabled bool `json:"enabled"`
|
||||
MethodTitle string `json:"method_title"`
|
||||
MethodDescription string `json:"method_description"`
|
||||
MethodSupports []string `json:"method_supports"`
|
||||
Settings map[string]PaymentGatewaySetting `json:"settings"`
|
||||
}
|
||||
|
||||
type PaymentGatewaySetting struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Default string `json:"default"`
|
||||
Tip string `json:"tip"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
}
|
||||
8
entity/porduct_default_attribute.go
Normal file
8
entity/porduct_default_attribute.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package entity
|
||||
|
||||
// ProductDefaultAttribute product default attribute properties
|
||||
type ProductDefaultAttribute struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Option string `json:"option"`
|
||||
}
|
||||
86
entity/product.go
Normal file
86
entity/product.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package entity
|
||||
|
||||
type ProductDimension struct {
|
||||
Length float64 `json:"length"`
|
||||
Width float64 `json:"width"`
|
||||
Height float64 `json:"height"`
|
||||
}
|
||||
|
||||
// Product product properties
|
||||
type Product struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Permalink string `json:"permalink"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
DateModified string `json:"date_modified"`
|
||||
DateModifiedGMT string `json:"date_modified_gmt"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
Featured bool `json:"featured"`
|
||||
CatalogVisibility string `json:"catalog_visibility"`
|
||||
Description string `json:"description"`
|
||||
ShortDescription string `json:"short_description"`
|
||||
SKU string `json:"sku"`
|
||||
Price float64 `json:"price"`
|
||||
RegularPrice float64 `json:"regular_price"`
|
||||
SalePrice float64 `json:"sale_price"`
|
||||
DateOnSaleFrom string `json:"date_on_sale_from"`
|
||||
DateOnSaleFromGMT string `json:"date_on_sale_from_gmt"`
|
||||
DateOnSaleTo string `json:"date_on_sale_to"`
|
||||
DateOnSaleToGMT string `json:"date_on_sale_to_gmt"`
|
||||
PriceHtml string `json:"price_html"`
|
||||
OnSale bool `json:"on_sale"`
|
||||
Purchasable bool `json:"purchasable"`
|
||||
TotalSales int `json:"total_sales"`
|
||||
Virtual bool `json:"virtual"`
|
||||
Downloadable bool `json:"downloadable"`
|
||||
Downloads []ProductDownload `json:"downloads"`
|
||||
DownloadLimit int `json:"download_limit"`
|
||||
DownloadExpiry int `json:"download_expiry"`
|
||||
ExternalUrl string `json:"external_url"`
|
||||
ButtonText string `json:"button_text"`
|
||||
TaxStatus string `json:"tax_status"`
|
||||
TaxClass string `json:"tax_class"`
|
||||
ManageStock bool `json:"manage_stock"`
|
||||
StockQuantity int `json:"stock_quantity"`
|
||||
StockStatus string `json:"stock_status"`
|
||||
Backorders string `json:"backorders"`
|
||||
BackordersAllowed bool `json:"backorders_allowed"`
|
||||
Backordered bool `json:"backordered"`
|
||||
SoldIndividually bool `json:"sold_individually"`
|
||||
Weight float64 `json:"weight"`
|
||||
Dimensions *ProductDimension `json:"dimensions"`
|
||||
ShippingRequired bool `json:"shipping_required"`
|
||||
ShippingTaxable bool `json:"shipping_taxable"`
|
||||
ShippingClass string `json:"shipping_class"`
|
||||
ShippingClassId int `json:"shipping_class_id"`
|
||||
ReviewsAllowed bool `json:"reviews_allowed"`
|
||||
AverageRating float64 `json:"average_rating"`
|
||||
RatingCount int `json:"rating_count"`
|
||||
RelatedIds []int `json:"related_ids"`
|
||||
UpsellIds []int `json:"upsell_ids"`
|
||||
CrossSellIds []int `json:"cross_sell_ids"`
|
||||
ParentId int `json:"parent_id"`
|
||||
PurchaseNote string `json:"purchase_note"`
|
||||
Categories []ProductCategory `json:"categories"`
|
||||
Tags []ProductTag `json:"tags"`
|
||||
Images []ProductImage `json:"images"`
|
||||
Attributes []ProductAttributeItem `json:"attributes"`
|
||||
DefaultAttributes []ProductDefaultAttribute `json:"default_attributes"`
|
||||
Variations []int `json:"variations"`
|
||||
GroupedProducts []int `json:"grouped_products"`
|
||||
MenuOrder int `json:"menu_order"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
|
||||
// ProductAttributeItem product attribute properties
|
||||
type ProductAttributeItem struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Position int `json:"position"`
|
||||
Visible bool `json:"visible"`
|
||||
Variation bool `json:"variation"`
|
||||
Options []string `json:"options"`
|
||||
}
|
||||
11
entity/product_attribute.go
Normal file
11
entity/product_attribute.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package entity
|
||||
|
||||
// ProductAttribute product attribute properties
|
||||
type ProductAttribute struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Type string `json:"type"`
|
||||
OrderBy string `json:"order_by"`
|
||||
HasArchives bool `json:"has_archives"`
|
||||
}
|
||||
11
entity/product_attribute_term.go
Normal file
11
entity/product_attribute_term.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package entity
|
||||
|
||||
// ProductAttributeTerm product attribute term properties
|
||||
type ProductAttributeTerm struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
MenuOrder int `json:"menu_order"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
14
entity/product_category.go
Normal file
14
entity/product_category.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package entity
|
||||
|
||||
// ProductCategory product category properties
|
||||
type ProductCategory struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Parent int `json:"parent"`
|
||||
Description string `json:"description"`
|
||||
Display string `json:"display"`
|
||||
Image *ProductImage `json:"image,omitempty"`
|
||||
MenuOrder int `json:"menu_order"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
8
entity/product_download.go
Normal file
8
entity/product_download.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package entity
|
||||
|
||||
// ProductDownload product download properties
|
||||
type ProductDownload struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
15
entity/product_review.go
Normal file
15
entity/product_review.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package entity
|
||||
|
||||
// ProductReview product review properties
|
||||
type ProductReview struct {
|
||||
ID int `json:"id"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
ProductId int `json:"product_id"`
|
||||
Status string `json:"status"`
|
||||
Reviewer string `json:"reviewer"`
|
||||
ReviewerEmail string `json:"reviewer_email"`
|
||||
Review string `json:"review"`
|
||||
Rating int `json:"rating"`
|
||||
Verified bool `json:"verified"`
|
||||
}
|
||||
10
entity/product_shipping_class.go
Normal file
10
entity/product_shipping_class.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package entity
|
||||
|
||||
// ProductShippingClass product shipping class properties
|
||||
type ProductShippingClass struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
10
entity/product_tag.go
Normal file
10
entity/product_tag.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package entity
|
||||
|
||||
// ProductTag product tag properties
|
||||
type ProductTag struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
55
entity/product_variation.go
Normal file
55
entity/product_variation.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ProductVariationAttribute product variation attribute properties
|
||||
type ProductVariationAttribute struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Option string `json:"option"`
|
||||
}
|
||||
|
||||
// ProductVariation product variation properties
|
||||
type ProductVariation struct {
|
||||
ID int `json:"id"`
|
||||
DateCreate time.Time `json:"date_create,omitempty"`
|
||||
DateCreateGMT time.Time `json:"date_create_gmt,omitempty"`
|
||||
DateModified time.Time `json:"date_modified,omitempty"`
|
||||
DateModifiedGMT time.Time `json:"date_modified_gmt,omitempty"`
|
||||
Description string `json:"description"`
|
||||
Permalink string `json:"permalink"`
|
||||
SKU string `json:"sku"`
|
||||
Price float64 `json:"price"`
|
||||
RegularPrice float64 `json:"regular_price"`
|
||||
SalePrice float64 `json:"sale_price"`
|
||||
DateOnSaleFrom time.Time `json:"date_on_sale_from"`
|
||||
DateOnSaleFromGMT time.Time `json:"date_on_sale_from_gmt"`
|
||||
DateOnSaleTo time.Time `json:"date_on_sale_to"`
|
||||
DateOnSaleToGMT time.Time `json:"date_on_sale_to_gmt"`
|
||||
OnSale bool `json:"on_sale"`
|
||||
Status string `json:"status"`
|
||||
Purchasable bool `json:"purchasable"`
|
||||
Virtual bool `json:"virtual"`
|
||||
Downloadable bool `json:"downloadable"`
|
||||
Downloads []ProductDownload `json:"downloads"`
|
||||
DownloadLimit int `json:"download_limit"`
|
||||
DownloadExpiry int `json:"download_expiry"`
|
||||
TaxStatus string `json:"tax_status"`
|
||||
TaxClass string `json:"tax_class"`
|
||||
ManageStock bool `json:"manage_stock"`
|
||||
StockQuantity int `json:"stock_quantity"`
|
||||
StockStatus string `json:"stock_status"`
|
||||
Backorders string `json:"backorders"`
|
||||
BackordersAllowed bool `json:"backorders_allowed"`
|
||||
Backordered bool `json:"backordered"`
|
||||
Weight float64 `json:"weight"`
|
||||
// ProductDimension *request.VariationDimensionsRequest `json:"dimensions"`
|
||||
ShippingClass string `json:"shipping_class"`
|
||||
ShippingClassId int `json:"shipping_class_id"`
|
||||
Image *ProductImage `json:"image"`
|
||||
Attributes []ProductVariationAttribute `json:"attributes"`
|
||||
MenuOrder int `json:"menu_order"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
57
entity/report.go
Normal file
57
entity/report.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package entity
|
||||
|
||||
// Report report properties
|
||||
type Report struct {
|
||||
Slug string `json:"slug"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type SaleReport struct {
|
||||
TotalSales float64 `json:"total_sales"`
|
||||
NetSales float64 `json:"net_sales"`
|
||||
AverageSales string `json:"average_sales"`
|
||||
TotalOrders int `json:"total_orders"`
|
||||
TotalItems int `json:"total_items"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
TotalShipping float64 `json:"total_shipping"`
|
||||
TotalRefunds int `json:"total_refunds"`
|
||||
TotalDiscount int `json:"total_discount"`
|
||||
TotalGroupedBy string `json:"total_grouped_by"`
|
||||
Totals map[string]struct {
|
||||
Sales float64 `json:"sales"`
|
||||
Orders int `json:"orders"`
|
||||
Items int `json:"items"`
|
||||
Tax float64 `json:"tax"`
|
||||
Shipping float64 `json:"shipping"`
|
||||
Discount float64 `json:"discount"`
|
||||
Customers int `json:"customers"`
|
||||
} `json:"totals"`
|
||||
}
|
||||
|
||||
// TopSellerReport top sellers report properties
|
||||
type TopSellerReport struct {
|
||||
Title string `json:"title"`
|
||||
ProductId int `json:"product_id"`
|
||||
Quantity int `json:"quantity"`
|
||||
}
|
||||
|
||||
type TotalReport struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
Total float64 `json:"total"`
|
||||
}
|
||||
|
||||
// CouponTotal coupon total properties
|
||||
type CouponTotal TotalReport
|
||||
|
||||
// CustomerTotal customer total properties
|
||||
type CustomerTotal TotalReport
|
||||
|
||||
// OrderTotal order total properties
|
||||
type OrderTotal TotalReport
|
||||
|
||||
// ProductTotal product total properties
|
||||
type ProductTotal TotalReport
|
||||
|
||||
// ReviewTotal review total properties
|
||||
type ReviewTotal TotalReport
|
||||
10
entity/setting_group.go
Normal file
10
entity/setting_group.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package entity
|
||||
|
||||
// SettingGroup setting group properties
|
||||
type SettingGroup struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
ParentId string `json:"parent_id"`
|
||||
SubGroups []string `json:"sub_groups"`
|
||||
}
|
||||
15
entity/setting_option.go
Normal file
15
entity/setting_option.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package entity
|
||||
|
||||
// SettingOption setting option properties
|
||||
type SettingOption struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
Value string `json:"value"`
|
||||
Default string `json:"default"`
|
||||
Tip string `json:"tip"`
|
||||
PlaceHolder string `json:"place_holder"`
|
||||
Type string `json:"type"`
|
||||
Options map[string]string `json:"options"`
|
||||
GroupId string `json:"group_id"`
|
||||
}
|
||||
23
entity/shipping.go
Normal file
23
entity/shipping.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package entity
|
||||
|
||||
type Shipping struct {
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Company string `json:"company,omitempty"`
|
||||
Address1 string `json:"address_1,omitempty"`
|
||||
Address2 string `json:"address_2,omitempty"`
|
||||
City string `json:"city,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Postcode string `json:"postcode,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
}
|
||||
|
||||
type ShippingLine struct {
|
||||
ID int `json:"id"`
|
||||
MethodTitle string `json:"method_title"`
|
||||
MethodId string `json:"method_id"`
|
||||
Total float64 `json:"total"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
Taxes []Tax `json:"taxes"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
12
entity/shipping_method.go
Normal file
12
entity/shipping_method.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package entity
|
||||
|
||||
type ShippingMethod struct {
|
||||
InstanceId int `json:"instance_id"`
|
||||
Title string `json:"title"`
|
||||
Order int `json:"order"`
|
||||
Enabled bool `json:"enabled"`
|
||||
MethodId string `json:"method_id"`
|
||||
MethodTitle string `json:"method_title"`
|
||||
MethodDescription string `json:"method_description"`
|
||||
Settings []ShippingZoneMethodSetting `json:"settings"`
|
||||
}
|
||||
8
entity/shipping_zone.go
Normal file
8
entity/shipping_zone.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package entity
|
||||
|
||||
// ShippingZone shipping zone properties
|
||||
type ShippingZone struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Order int `json:"order"`
|
||||
}
|
||||
7
entity/shipping_zone_location.go
Normal file
7
entity/shipping_zone_location.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package entity
|
||||
|
||||
// ShippingZoneLocation shipping zone location
|
||||
type ShippingZoneLocation struct {
|
||||
Code string `json:"code"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
16
entity/shipping_zone_method.go
Normal file
16
entity/shipping_zone_method.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package entity
|
||||
|
||||
// ShippingZoneMethod shipping zone method properties
|
||||
type ShippingZoneMethod = ShippingMethod
|
||||
|
||||
// ShippingZoneMethodSetting shipping zone method setting properties
|
||||
type ShippingZoneMethodSetting struct {
|
||||
ID int `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Default string `json:"default"`
|
||||
Tip string `json:"tip"`
|
||||
PlaceHolder string `json:"place_holder"`
|
||||
}
|
||||
11
entity/system_status.go
Normal file
11
entity/system_status.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package entity
|
||||
|
||||
type SystemStatus struct {
|
||||
Environment SystemStatusEnvironment `json:"environment"`
|
||||
Database SystemStatusDatabase `json:"database"`
|
||||
ActivePlugins []string `json:"active_plugins"`
|
||||
Theme SystemStatusTheme `json:"theme"`
|
||||
Settings SystemStatusSetting `json:"settings"`
|
||||
Security SystemStatusSecurity `json:"security"`
|
||||
Pages []string `json:"pages"`
|
||||
}
|
||||
9
entity/system_status_database.go
Normal file
9
entity/system_status_database.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package entity
|
||||
|
||||
// SystemStatusDatabase System status database properties
|
||||
type SystemStatusDatabase struct {
|
||||
WCDatabaseVersion string `json:"wc_database_version"`
|
||||
DatabasePrefix string `json:"database_prefix"`
|
||||
MaxmindGEOIPDatabase string `json:"maxmind_geoip_database"`
|
||||
DatabaseTables []string `json:"database_tables"`
|
||||
}
|
||||
34
entity/system_status_environment.go
Normal file
34
entity/system_status_environment.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package entity
|
||||
|
||||
// SystemStatusEnvironment System status environment properties
|
||||
type SystemStatusEnvironment struct {
|
||||
HomeURL string `json:"home_url"`
|
||||
SiteURL string `json:"site_url"`
|
||||
WCVersion string `json:"wc_version"`
|
||||
LogDirectory string `json:"log_directory"`
|
||||
LogDirectoryWritable bool `json:"log_directory_writable"`
|
||||
WPVersion string `json:"wp_version"`
|
||||
WPMultisite bool `json:"wp_multisite"`
|
||||
WPMemoryLimit int `json:"wp_memory_limit"`
|
||||
WPDebugMode bool `json:"wp_debug_mode"`
|
||||
WPCron bool `json:"wp_cron"`
|
||||
Language string `json:"language"`
|
||||
ServerInfo string `json:"server_info"`
|
||||
PHPVersion string `json:"php_version"`
|
||||
PHPPostMaxSize int `json:"php_post_max_size"`
|
||||
PHPMaxExecutionTime int `json:"php_max_execution_time"`
|
||||
PHPMaxInputVars int `json:"php_max_input_vars"`
|
||||
CURLVersion string `json:"curl_version"`
|
||||
SuhosinInstalled bool `json:"suhosin_installed"`
|
||||
MaxUploadSize int `json:"max_upload_size"`
|
||||
MySQLVersion string `json:"my_sql_version"`
|
||||
DefaultTimezone string `json:"default_timezone"`
|
||||
FSockOpenOrCurlEnabled bool `json:"fsockopen_or_curl_enabled"`
|
||||
SOAPClientEnabled bool `json:"soap_client_enabled"`
|
||||
GzipEnabled bool `json:"gzip_enabled"`
|
||||
MbStringEnabled bool `json:"mbstring_enabled"`
|
||||
RemotePostSuccessful bool `json:"remote_post_successful"`
|
||||
RemotePostResponse string `json:"remote_post_response"`
|
||||
RemoteGetSuccessful bool `json:"remote_get_successful"`
|
||||
RemoteGetResponse string `json:"remote_get_response"`
|
||||
}
|
||||
7
entity/system_status_security.go
Normal file
7
entity/system_status_security.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package entity
|
||||
|
||||
// SystemStatusSecurity System status security properties
|
||||
type SystemStatusSecurity struct {
|
||||
SecureConnection bool `json:"secure_connection"`
|
||||
HideErrors bool `json:"hide_errors"`
|
||||
}
|
||||
15
entity/system_status_setting.go
Normal file
15
entity/system_status_setting.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package entity
|
||||
|
||||
// SystemStatusSetting System status setting properties
|
||||
type SystemStatusSetting struct {
|
||||
APIEnabled bool `json:"api_enabled"`
|
||||
ForceSSL bool `json:"force_ssl"`
|
||||
Currency string `json:"currency"`
|
||||
CurrencySymbol string `json:"currency_symbol"`
|
||||
CurrencyPosition string `json:"currency_position"`
|
||||
ThousandSeparator string `json:"thousand_separator"`
|
||||
DecimalSeparator string `json:"decimal_separator"`
|
||||
NumberOfDecimals int `json:"number_of_decimals"`
|
||||
GeolocationEnabled bool `json:"geolocation_enabled"`
|
||||
Taxonomies []string `json:"taxonomies"`
|
||||
}
|
||||
17
entity/system_status_theme.go
Normal file
17
entity/system_status_theme.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package entity
|
||||
|
||||
// SystemStatusTheme System status theme properties
|
||||
type SystemStatusTheme struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
VersionLatest string `json:"version_latest"`
|
||||
AuthorURL string `json:"author_url"`
|
||||
IsChildTheme bool `json:"is_child_theme"`
|
||||
HasWooCommerceSupport bool `json:"has_woo_commerce_support"`
|
||||
HasWooCommerceFile bool `json:"has_woo_commerce_file"`
|
||||
HasOutdatedTemplates bool `json:"has_outdated_templates"`
|
||||
Overrides []string `json:"overrides"`
|
||||
ParentName string `json:"parent_name"`
|
||||
ParentVersion string `json:"parent_version"`
|
||||
ParentAuthorURL string `json:"parent_author_url"`
|
||||
}
|
||||
12
entity/system_status_tool.go
Normal file
12
entity/system_status_tool.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package entity
|
||||
|
||||
// SystemStatusTool system status tool properties
|
||||
type SystemStatusTool struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Action string `json:"action"`
|
||||
Description string `json:"description"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Confirm bool `json:"confirm"`
|
||||
}
|
||||
7
entity/tax_class.go
Normal file
7
entity/tax_class.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package entity
|
||||
|
||||
// TaxClass tax class properties
|
||||
type TaxClass struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
19
entity/tax_rate.go
Normal file
19
entity/tax_rate.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package entity
|
||||
|
||||
// TaxRate tax rate properties
|
||||
type TaxRate struct {
|
||||
ID int `json:"id"`
|
||||
Country string `json:"country"`
|
||||
State string `json:"state"`
|
||||
Postcode string `json:"postcode"`
|
||||
City string `json:"city"`
|
||||
Postcodes []string `json:"postcodes"`
|
||||
Cities []string `json:"cities"`
|
||||
Rate string `json:"rate"`
|
||||
Name string `json:"name"`
|
||||
Priority int `json:"priority"`
|
||||
Compound bool `json:"compound"`
|
||||
Shipping bool `json:"shipping"`
|
||||
Order int `json:"order"`
|
||||
Class string `json:"class"`
|
||||
}
|
||||
23
entity/taxes.go
Normal file
23
entity/taxes.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package entity
|
||||
|
||||
type Tax struct {
|
||||
ID int `json:"id"`
|
||||
RateCode string `json:"rate_code"`
|
||||
RateId string `json:"rate_id"`
|
||||
Label string `json:"label"`
|
||||
Compound bool `json:"compound"`
|
||||
TaxTotal float64 `json:"tax_total"`
|
||||
ShippingTaxTotal float64 `json:"shipping_tax_total"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
|
||||
type TaxLine struct {
|
||||
ID int `json:"id"`
|
||||
RateCode string `json:"rate_code"`
|
||||
RateId string `json:"rate_id"`
|
||||
Label string `json:"label"`
|
||||
Compound bool `json:"compound"`
|
||||
TaxTotal float64 `json:"tax_total"`
|
||||
ShippingTaxTotal float64 `json:"shipping_tax_total"`
|
||||
MetaData []Meta `json:"meta_data"`
|
||||
}
|
||||
18
entity/webhook.go
Normal file
18
entity/webhook.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package entity
|
||||
|
||||
// Webhook webhook properties
|
||||
type Webhook struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Topic string `json:"topic"`
|
||||
Resource string `json:"resource"`
|
||||
Event string `json:"event"`
|
||||
Hooks []string `json:"hooks"`
|
||||
DeliveryURL string `json:"delivery_url"`
|
||||
Secret string `json:"secret"`
|
||||
DateCreated string `json:"date_created"`
|
||||
DateCreatedGMT string `json:"date_created_gmt"`
|
||||
DateModified string `json:"date_modified"`
|
||||
DateModifiedGMT string `json:"date_modified_gmt"`
|
||||
}
|
||||
14
go.mod
Normal file
14
go.mod
Normal file
@@ -0,0 +1,14 @@
|
||||
module git.cloudyne.io/go/woogo
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
||||
github.com/brianvoe/gofakeit/v6 v6.16.0
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/google/go-querystring v1.1.0
|
||||
git.cloudyne.io/go/hiscaler-gox v1.1.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/stretchr/testify v1.7.0
|
||||
)
|
||||
208
order.go
Normal file
208
order.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type orderService service
|
||||
|
||||
// OrdersQueryParams orders query params
|
||||
type OrdersQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
After string `url:"after,omitempty"`
|
||||
Before string `url:"before,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
Parent []int `url:"parent,omitempty"`
|
||||
ParentExclude []int `url:"parent_exclude,omitempty"`
|
||||
Status []string `url:"status,omitempty"`
|
||||
Customer int `url:"customer,omitempty"`
|
||||
Product int `url:"product,omitempty"`
|
||||
DecimalPoint int `url:"dp,omitempty"`
|
||||
}
|
||||
|
||||
func (m OrdersQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Before, validation.When(m.Before != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.After, validation.When(m.After != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "date", "include", "title", "slug").Error("无效的排序字段"))),
|
||||
validation.Field(&m.Status, validation.When(len(m.Status) > 0, validation.By(func(value interface{}) error {
|
||||
statuses, ok := value.([]string)
|
||||
if !ok {
|
||||
return errors.New("无效的状态值")
|
||||
}
|
||||
validStatuses := []string{"any", "pending", "processing", "on-hold", "completed", "cancelled", "refunded", "failed ", "trash"}
|
||||
for _, status := range statuses {
|
||||
valid := false
|
||||
for _, validStatus := range validStatuses {
|
||||
if status == validStatus {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("无效的状态值:%s", status)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))),
|
||||
)
|
||||
}
|
||||
|
||||
// All list all orders
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// params := OrdersQueryParams{
|
||||
// After: "2022-06-10",
|
||||
// }
|
||||
// params.PerPage = 100
|
||||
// for {
|
||||
// orders, total, totalPages, isLastPage, err := wooClient.Services.Order.All(params)
|
||||
// if err != nil {
|
||||
// break
|
||||
// }
|
||||
// fmt.Println(fmt.Sprintf("Page %d/%d", total, totalPages))
|
||||
// // read orders
|
||||
// for _, order := range orders {
|
||||
// _ = order
|
||||
// }
|
||||
// if err != nil || isLastPage {
|
||||
// break
|
||||
// }
|
||||
// params.Page++
|
||||
// }
|
||||
func (s orderService) All(params OrdersQueryParams) (items []entity.Order, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
params.After = ToISOTimeString(params.After, false, true)
|
||||
params.Before = ToISOTimeString(params.Before, true, false)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/orders")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve an order
|
||||
func (s orderService) One(id int) (item entity.Order, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/orders/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create order
|
||||
|
||||
type CreateOrderRequest struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
CurrencySymbol string `json:"currency_symbol,omitempty"`
|
||||
PricesIncludeTax bool `json:"prices_include_tax,omitempty"`
|
||||
CustomerId int `json:"customer_id,omitempty"`
|
||||
CustomerNote string `json:"customer_note,omitempty"`
|
||||
Billing *entity.Billing `json:"billing,omitempty"`
|
||||
Shipping *entity.Shipping `json:"shipping,omitempty"`
|
||||
PaymentMethod string `json:"payment_method,omitempty"`
|
||||
PaymentMethodTitle string `json:"payment_method_title,omitempty"`
|
||||
TransactionId string `json:"transaction_id,omitempty"`
|
||||
MetaData []entity.Meta `json:"meta_data,omitempty"`
|
||||
LineItems []entity.LineItem `json:"line_items,omitempty"`
|
||||
TaxLines []entity.TaxLine `json:"tax_lines,omitempty"`
|
||||
ShippingLines []entity.ShippingLine `json:"shipping_lines,omitempty"`
|
||||
FeeLines []entity.FeeLine `json:"fee_lines,omitempty"`
|
||||
CouponLines []entity.CouponLine `json:"coupon_lines,omitempty"`
|
||||
SetPaid bool `json:"set_paid,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateOrderRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Status, validation.When(m.Status != "", validation.In("pending", "processing", "on-hold", "completed", "cancelled", "refunded", "failed", "trash").Error("无效的状态"))),
|
||||
)
|
||||
}
|
||||
|
||||
func (s orderService) Create(req CreateOrderRequest) (item entity.Order, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/orders")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update order
|
||||
|
||||
type UpdateOrderRequest = CreateOrderRequest
|
||||
|
||||
func (s orderService) Update(id int, req UpdateOrderRequest) (item entity.Order, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/orders/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete delete an order
|
||||
func (s orderService) Delete(id int, force bool) (item entity.Order, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/orders/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
97
order_note.go
Normal file
97
order_note.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/google/go-querystring/query"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type orderNoteService service
|
||||
|
||||
type OrderNotesQueryParams struct {
|
||||
queryParams
|
||||
Type string `url:"type,omitempty"`
|
||||
}
|
||||
|
||||
func (m OrderNotesQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Type, validation.When(m.Type != "", validation.In("any", "customer", "internal").Error("无效的类型"))),
|
||||
)
|
||||
}
|
||||
|
||||
func (s orderNoteService) All(orderId int, params OrderNotesQueryParams) (items []entity.OrderNote, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
urlValues, _ := query.Values(params)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(urlValues).Get(fmt.Sprintf("/orders/%d/notes", orderId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s orderNoteService) One(orderId, noteId int) (item entity.OrderNote, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/orders/%d/notes/%d", orderId, noteId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create order note
|
||||
|
||||
type CreateOrderNoteRequest struct {
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
func (m CreateOrderNoteRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Note, validation.Required.Error("内容不能为空")),
|
||||
)
|
||||
}
|
||||
|
||||
func (s orderNoteService) Create(orderId int, req CreateOrderNoteRequest) (item entity.OrderNote, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Post(fmt.Sprintf("/orders/%d/notes", orderId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s orderNoteService) Delete(orderId, noteId int, force bool) (item entity.OrderNote, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/orders/%d/notes/%d", orderId, noteId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
74
order_note_test.go
Normal file
74
order_note_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/jsonx"
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOrderNoteService_All(t *testing.T) {
|
||||
t.Run("getOrderId", getOrderId)
|
||||
params := OrderNotesQueryParams{}
|
||||
items, _, _, _, err := wooClient.Services.OrderNote.All(orderId, params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.OrderNote.All error: %s", err.Error())
|
||||
} else {
|
||||
if len(items) > 0 {
|
||||
noteId = items[0].ID
|
||||
}
|
||||
t.Logf("items = %s", jsonx.ToPrettyJson(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderNoteService_Create(t *testing.T) {
|
||||
note := gofakeit.Address().Address
|
||||
req := CreateOrderNoteRequest{
|
||||
Note: note,
|
||||
}
|
||||
item, err := wooClient.Services.OrderNote.Create(orderId, req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.OrderNote.Create error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, note, item.Note, "note")
|
||||
noteId = item.ID
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderNoteService_One(t *testing.T) {
|
||||
t.Run("TestOrderNoteService_All", TestOrderNoteService_All)
|
||||
item, err := wooClient.Services.OrderNote.One(orderId, noteId)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.OrderNote.One(%d, %d) error: %s", orderId, noteId, err.Error())
|
||||
} else {
|
||||
assert.Equal(t, noteId, item.ID, "note id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderNoteService_CreateDelete(t *testing.T) {
|
||||
t.Run("getOrderId", getOrderId)
|
||||
note := gofakeit.Address().Address
|
||||
req := CreateOrderNoteRequest{
|
||||
Note: note,
|
||||
}
|
||||
item, err := wooClient.Services.OrderNote.Create(orderId, req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.OrderNote.Create error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, note, item.Note, "note")
|
||||
noteId = item.ID
|
||||
}
|
||||
|
||||
// Delete
|
||||
_, err = wooClient.Services.OrderNote.Delete(orderId, noteId, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.OrderNote.Delete(%d, %d, %v) error: %s", orderId, noteId, true, err.Error())
|
||||
} else {
|
||||
_, err = wooClient.Services.OrderNote.One(orderId, noteId)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("wooClient.Services.OrderNote.Delete(%d, %d, %v) failed", orderId, noteId, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
132
order_refund.go
Normal file
132
order_refund.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type orderRefundService service
|
||||
|
||||
// List all order refunds
|
||||
|
||||
type OrderRefundsQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
After string `url:"after,omitempty"`
|
||||
Before string `url:"before,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
Parent []int `url:"parent,omitempty"`
|
||||
ParentExclude []int `url:"parent_exclude,omitempty"`
|
||||
DecimalPoint int `url:"dp,omitempty"`
|
||||
}
|
||||
|
||||
func (m OrderRefundsQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Before, validation.When(m.Before != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.After, validation.When(m.After != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "date", "include", "title", "slug").Error("无效的排序值"))),
|
||||
)
|
||||
}
|
||||
|
||||
func (s orderRefundService) All(orderId int, params OrderRefundsQueryParams) (items []entity.OrderRefund, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
params.After = ToISOTimeString(params.After, false, true)
|
||||
params.Before = ToISOTimeString(params.Before, true, false)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get(fmt.Sprintf("/orders/%d/refunds", orderId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create an order refund
|
||||
|
||||
type CreateOrderRefundRequest struct {
|
||||
Amount float64 `json:"amount,string"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
RefundedBy int `json:"refunded_by,omitempty"`
|
||||
MetaData []entity.Meta `json:"meta_data,omitempty"`
|
||||
LineItems []entity.OrderRefundLineItem `json:"line_items,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateOrderRefundRequest) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s orderRefundService) Create(orderId int, req CreateOrderRefundRequest) (item entity.OrderRefund, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Post(fmt.Sprintf("/orders/%d/refunds", orderId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve an order refund
|
||||
func (s orderRefundService) One(orderId, refundId, decimalPoint int) (item entity.OrderRefund, err error) {
|
||||
if decimalPoint <= 0 || decimalPoint >= 6 {
|
||||
decimalPoint = 2
|
||||
}
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]int{"dp": decimalPoint}).
|
||||
Get(fmt.Sprintf("/orders/%d/refunds/%d", orderId, refundId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete delete an order refund
|
||||
func (s orderRefundService) Delete(orderId, refundId int, force bool) (item entity.OrderRefund, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/orders/%d/refunds/%d", orderId, refundId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
80
order_refund_test.go
Normal file
80
order_refund_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/jsonx"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOrderRefundService_All(t *testing.T) {
|
||||
t.Run("getOrderId", getOrderId)
|
||||
params := OrderRefundsQueryParams{}
|
||||
items, _, _, _, err := wooClient.Services.OrderRefund.All(orderId, params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.OrderRefund.All error: %s", err.Error())
|
||||
} else {
|
||||
if len(items) > 0 {
|
||||
childId = items[0].ID
|
||||
}
|
||||
t.Logf("items = %s", jsonx.ToPrettyJson(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderRefundService_Create(t *testing.T) {
|
||||
t.Run("getOrderId", getOrderId)
|
||||
req := CreateOrderRefundRequest{
|
||||
Amount: 1,
|
||||
Reason: "product is lost",
|
||||
RefundedBy: 0,
|
||||
MetaData: nil,
|
||||
LineItems: nil,
|
||||
}
|
||||
item, err := wooClient.Services.OrderRefund.Create(mainId, req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.OrderRefund.Create error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, 100.00, item.Amount, "refund amount")
|
||||
childId = item.ID
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderRefundService_One(t *testing.T) {
|
||||
t.Run("TestOrderRefundService_All", TestOrderRefundService_All)
|
||||
item, err := wooClient.Services.OrderRefund.One(mainId, childId, 2)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.OrderRefund.One(%d, %d) error: %s", orderId, noteId, err.Error())
|
||||
} else {
|
||||
assert.Equal(t, childId, item.ID, "note id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderRefundService_CreateDelete(t *testing.T) {
|
||||
t.Run("getOrderId", getOrderId)
|
||||
req := CreateOrderRefundRequest{
|
||||
Amount: 100,
|
||||
Reason: "product is lost",
|
||||
RefundedBy: 0,
|
||||
MetaData: nil,
|
||||
LineItems: nil,
|
||||
}
|
||||
item, err := wooClient.Services.OrderRefund.Create(mainId, req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.OrderRefund.Create error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, 100.00, item.Amount, "refund amount")
|
||||
noteId = item.ID
|
||||
}
|
||||
|
||||
// Delete
|
||||
_, err = wooClient.Services.OrderNote.Delete(mainId, childId, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.OrderRefund.Delete(%d, %d, %v) error: %s", mainId, childId, true, err.Error())
|
||||
} else {
|
||||
_, err = wooClient.Services.OrderNote.One(mainId, childId)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("wooClient.Services.OrderRefund.Delete(%d, %d, %v) failed", mainId, childId, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
98
order_test.go
Normal file
98
order_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Query orders
|
||||
func ExampleAll() {
|
||||
params := OrdersQueryParams{
|
||||
After: "2022-06-10",
|
||||
}
|
||||
params.PerPage = 100
|
||||
for {
|
||||
orders, total, totalPages, isLastPage, err := wooClient.Services.Order.All(params)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
fmt.Println(fmt.Sprintf("Page %d/%d", total, totalPages))
|
||||
// read orders
|
||||
for _, order := range orders {
|
||||
_ = order
|
||||
}
|
||||
if err != nil || isLastPage {
|
||||
break
|
||||
}
|
||||
params.Page++
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderService_All(t *testing.T) {
|
||||
params := OrdersQueryParams{
|
||||
After: "2022-06-10",
|
||||
}
|
||||
params.PerPage = 100
|
||||
items, _, _, isLastPage, err := wooClient.Services.Order.All(params)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Order.All error: %s", err.Error())
|
||||
}
|
||||
if len(items) > 0 {
|
||||
orderId = items[0].ID
|
||||
}
|
||||
assert.Equal(t, true, isLastPage, "check isLastPage")
|
||||
}
|
||||
|
||||
func TestOrderService_AllByArrayParams(t *testing.T) {
|
||||
params := OrdersQueryParams{
|
||||
Status: []string{"completed"},
|
||||
Include: []int{914, 849},
|
||||
}
|
||||
params.PerPage = 300
|
||||
_, _, _, isLastPage, err := wooClient.Services.Order.All(params)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Order.All By Array Params error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, true, isLastPage, "check isLastPage")
|
||||
}
|
||||
|
||||
func TestOrderService_One(t *testing.T) {
|
||||
item, err := wooClient.Services.Order.One(orderId)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Order.One(%d) error: %s", orderId, err.Error())
|
||||
} else {
|
||||
assert.Equal(t, orderId, item.ID, "order id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderService_Create(t *testing.T) {
|
||||
req := CreateOrderRequest{}
|
||||
item, err := wooClient.Services.Order.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Order.Create error: %s", err.Error())
|
||||
}
|
||||
orderId = item.ID
|
||||
}
|
||||
|
||||
func TestOrderService_Update(t *testing.T) {
|
||||
t.Run("getOrderId", getOrderId)
|
||||
req := UpdateOrderRequest{
|
||||
PaymentMethod: "paypal",
|
||||
PaymentMethodTitle: "Paypal",
|
||||
}
|
||||
item, err := wooClient.Services.Order.Update(orderId, req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Order.Update error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, orderId, item.ID, "order id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderService_Delete(t *testing.T) {
|
||||
_, err := wooClient.Services.Order.Delete(orderId, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Order.Delete(%d, true) error: %s", orderId, err.Error())
|
||||
}
|
||||
}
|
||||
69
payment_gateway.go
Normal file
69
payment_gateway.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type paymentGatewayService service
|
||||
|
||||
func (s paymentGatewayService) All() (items []entity.PaymentGateway, err error) {
|
||||
resp, err := s.httpClient.R().Get("/payment_gateways")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve a payment gateway
|
||||
func (s paymentGatewayService) One(id string) (item entity.PaymentGateway, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/payment_gateways/%s", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
type UpdatePaymentGatewayRequest struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Order int `json:"order,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
MethodTitle string `json:"method_title,omitempty"`
|
||||
MethodDescription string `json:"method_description,omitempty"`
|
||||
MethodSupports []string `json:"method_supports,omitempty"`
|
||||
Settings map[string]entity.PaymentGatewaySetting `json:"settings,omitempty"`
|
||||
}
|
||||
|
||||
func (m UpdatePaymentGatewayRequest) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s paymentGatewayService) Update(id string, req UpdatePaymentGatewayRequest) (item entity.PaymentGateway, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Put(fmt.Sprintf("/payment_gateways/%s", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
57
payment_gateway_test.go
Normal file
57
payment_gateway_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var paymentGatewayId string
|
||||
|
||||
func TestPaymentGatewayService_All(t *testing.T) {
|
||||
items, err := wooClient.Services.PaymentGateway.All()
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.PaymentGateway.All error: %s", err.Error())
|
||||
}
|
||||
if len(items) > 0 {
|
||||
paymentGatewayId = items[0].ID
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaymentGatewayService_One(t *testing.T) {
|
||||
t.Run("TestPaymentGatewayService_All", TestPaymentGatewayService_All)
|
||||
item, err := wooClient.Services.PaymentGateway.One(paymentGatewayId)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Coupon.PaymentGateway error: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, paymentGatewayId, item.ID, "payment gateway id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaymentGatewayService_Update(t *testing.T) {
|
||||
t.Run("TestPaymentGatewayService_All", TestPaymentGatewayService_All)
|
||||
|
||||
var oldItem, newItem entity.PaymentGateway
|
||||
var err error
|
||||
oldItem, err = wooClient.Services.PaymentGateway.One(paymentGatewayId)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.PaymentGateway.One error: %s", err.Error())
|
||||
}
|
||||
|
||||
req := UpdatePaymentGatewayRequest{}
|
||||
newItem, err = wooClient.Services.PaymentGateway.Update(paymentGatewayId, req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.PaymentGateway.Update error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, oldItem, newItem, "all no change")
|
||||
|
||||
// Change title
|
||||
req.Title = gofakeit.RandomString([]string{"A", "B", "C", "D", "E", "F", "G"})
|
||||
newItem, err = wooClient.Services.PaymentGateway.Update(paymentGatewayId, req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.PaymentGateway.Update error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, req.Title, newItem.Title, "title")
|
||||
}
|
||||
204
product.go
Normal file
204
product.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productService service
|
||||
|
||||
// Products
|
||||
|
||||
type ProductsQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
After string `url:"after,omitempty"`
|
||||
Before string `url:"before,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
Parent []int `url:"parent,omitempty"`
|
||||
ParentExclude []int `url:"parent_exclude,omitempty"`
|
||||
Slug string `url:"slug,omitempty"`
|
||||
Status string `url:"status,omitempty"`
|
||||
Type string `url:"type,omitempty"`
|
||||
SKU string `url:"sku,omitempty"`
|
||||
Featured bool `url:"featured,omitempty"`
|
||||
Category string `url:"category,omitempty"`
|
||||
Tag string `url:"tag,omitempty"`
|
||||
ShippingClass string `url:"shipping_class,omitempty"`
|
||||
Attribute string `url:"attribute,omitempty"`
|
||||
AttributeTerm string `url:"attribute_term,omitempty"`
|
||||
TaxClass string `url:"tax_class,omitempty"`
|
||||
OnSale bool `url:"on_sale,omitempty"`
|
||||
MinPrice float64 `url:"min_price,string,omitempty"`
|
||||
MaxPrice float64 `url:"max_price,string,omitempty"`
|
||||
StockStatus string `url:"stock_status,omitempty"`
|
||||
}
|
||||
|
||||
func (m ProductsQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Before, validation.When(m.Before != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.After, validation.When(m.After != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "include", "title", "slug", "price", "popularity", "rating").Error("无效的排序字段"))),
|
||||
validation.Field(&m.Status, validation.When(m.Status != "", validation.In("any", "draft", "pending", "private", "publish").Error("无效的状态"))),
|
||||
validation.Field(&m.Type, validation.When(m.Type != "", validation.In("simple", "grouped", "external", "variable").Error("无效的类型"))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all products
|
||||
func (s productService) All(params ProductsQueryParams) (items []entity.Product, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
params.After = ToISOTimeString(params.After, false, true)
|
||||
params.Before = ToISOTimeString(params.Before, true, false)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/products")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a product
|
||||
func (s productService) One(id int) (item entity.Product, err error) {
|
||||
var res entity.Product
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
if err = jsoniter.Unmarshal(resp.Body(), &res); err == nil {
|
||||
item = res
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateProductRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Featured bool `json:"featured,omitempty"`
|
||||
CatalogVisibility string `json:"catalog_visibility,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ShortDescription string `json:"short_description,omitempty"`
|
||||
SKU string `json:"sku,omitempty"`
|
||||
RegularPrice float64 `json:"regular_price,string,omitempty"`
|
||||
SalePrice float64 `json:"sale_price,string,omitempty"`
|
||||
DateOnSaleFrom string `json:"date_on_sale_from,omitempty"`
|
||||
DateOnSaleFromGMT string `json:"date_on_sale_from_gmt,omitempty"`
|
||||
DateOnSaleTo string `json:"date_on_sale_to,omitempty"`
|
||||
DateOnSaleToGMT string `json:"date_on_sale_to_gmt,omitempty"`
|
||||
Virtual bool `json:"virtual,omitempty"`
|
||||
Downloadable bool `json:"downloadable,omitempty"`
|
||||
Downloads []entity.ProductDownload `json:"downloads,omitempty"`
|
||||
DownloadLimit int `json:"download_limit,omitempty"`
|
||||
DownloadExpiry int `json:"download_expiry,omitempty"`
|
||||
ExternalUrl string `json:"external_url,omitempty"`
|
||||
ButtonText string `json:"button_text,omitempty"`
|
||||
TaxStatus string `json:"tax_status,omitempty"`
|
||||
TaxClass string `json:"tax_class,omitempty"`
|
||||
ManageStock bool `json:"manage_stock,omitempty"`
|
||||
StockQuantity int `json:"stock_quantity,omitempty"`
|
||||
StockStatus string `json:"stock_status,omitempty"`
|
||||
Backorders string `json:"backorders,omitempty"`
|
||||
SoldIndividually bool `json:"sold_individually,omitempty"`
|
||||
Weight string `json:"weight,omitempty"`
|
||||
Dimensions *entity.ProductDimension `json:"dimensions,omitempty"`
|
||||
ShippingClass string `json:"shipping_class,omitempty"`
|
||||
ReviewsAllowed bool `json:"reviews_allowed,omitempty"`
|
||||
UpsellIds []int `json:"upsell_ids,omitempty"`
|
||||
CrossSellIds []int `json:"cross_sell_ids,omitempty"`
|
||||
ParentId int `json:"parent_id,omitempty"`
|
||||
PurchaseNote string `json:"purchase_note,omitempty"`
|
||||
Categories []entity.ProductCategory `json:"categories,omitempty"`
|
||||
Tags []entity.ProductTag `json:"tags,omitempty"`
|
||||
Images []entity.ProductImage `json:"images,omitempty"`
|
||||
Attributes []entity.ProductAttribute `json:"attributes,omitempty"`
|
||||
DefaultAttributes []entity.ProductDefaultAttribute `json:"default_attributes,omitempty"`
|
||||
GroupedProducts []int `json:"grouped_products,omitempty"`
|
||||
MenuOrder int `json:"menu_order,omitempty"`
|
||||
MetaData []entity.Meta `json:"meta_data,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateProductRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Name, validation.Required.Error("商品名称不能为空")),
|
||||
validation.Field(&m.Type, validation.When(m.Type != "", validation.In("simple", "grouped", "external ", "variable").Error("无效的类型"))),
|
||||
validation.Field(&m.Status, validation.When(m.Status != "", validation.In("draft", "pending", "private", "publish").Error("无效的状态"))),
|
||||
validation.Field(&m.CatalogVisibility, validation.When(m.CatalogVisibility != "", validation.In("visible", "catalog", "search", "hidden").Error("无效的目录可见性"))),
|
||||
validation.Field(&m.TaxStatus, validation.When(m.TaxStatus != "", validation.In("taxable", "shipping ", "none").Error("无效的税务状态"))),
|
||||
validation.Field(&m.StockStatus, validation.When(m.StockStatus != "", validation.In("instock", "outofstock ", "onbackorder").Error("无效的库存状态"))),
|
||||
validation.Field(&m.Backorders, validation.When(m.Backorders != "", validation.In("yes", "no ", "notify").Error("无效的缺货订单状态"))),
|
||||
)
|
||||
}
|
||||
|
||||
// Create create a product
|
||||
func (s productService) Create(req CreateProductRequest) (item entity.Product, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
type UpdateProductRequest = CreateProductRequest
|
||||
|
||||
func (s productService) Update(id int, req UpdateProductRequest) (item entity.Product, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete delete a product
|
||||
func (s productService) Delete(id int, force bool) (item entity.Product, err error) {
|
||||
resp, err := s.httpClient.R().SetBody(map[string]bool{"force": force}).Delete(fmt.Sprintf("/products/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
164
product_attribute.go
Normal file
164
product_attribute.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productAttributeService service
|
||||
|
||||
type ProductAttributesQueryParams struct {
|
||||
queryParams
|
||||
}
|
||||
|
||||
func (m ProductAttributesQueryParams) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// All List all product attributes
|
||||
func (s productAttributeService) All(params ProductAttributesQueryParams) (items []entity.ProductAttribute, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/products/attributes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a product attribute
|
||||
func (s productAttributeService) One(id int) (item entity.ProductAttribute, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/attributes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateProductAttributeRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
OrderBy string `json:"order_by,omitempty"`
|
||||
HasArchives bool `json:"has_archives,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateProductAttributeRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("menu_order", "name", "name_num", "id").Error("无效的排序方式"))),
|
||||
)
|
||||
}
|
||||
|
||||
// Create Create a product attribute
|
||||
func (s productAttributeService) Create(req CreateProductAttributeRequest) (item entity.ProductAttribute, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/attributes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateProductAttributeRequest = CreateProductAttributeRequest
|
||||
|
||||
// Update Update a product attribute
|
||||
func (s productAttributeService) Update(id int, req UpdateProductAttributeRequest) (item entity.ProductAttribute, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/attributes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete a product attribute
|
||||
|
||||
func (s productAttributeService) Delete(id int, force bool) (item entity.ProductAttribute, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/attributes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update product attributes
|
||||
|
||||
type BatchProductAttributesCreateItem = CreateProductAttributeRequest
|
||||
type BatchProductAttributesUpdateItem struct {
|
||||
ID string `json:"id"`
|
||||
BatchProductAttributesCreateItem
|
||||
}
|
||||
|
||||
type BatchProductAttributesRequest struct {
|
||||
Create []BatchProductAttributesCreateItem `json:"create,omitempty"`
|
||||
Update []BatchProductAttributesUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchProductAttributesRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchProductAttributesResult struct {
|
||||
Create []entity.ProductAttribute `json:"create"`
|
||||
Update []entity.ProductAttribute `json:"update"`
|
||||
Delete []entity.ProductAttribute `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete product attributes
|
||||
func (s productAttributeService) Batch(req BatchProductAttributesRequest) (res BatchProductAttributesResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/attributes/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
170
product_attribute_term.go
Normal file
170
product_attribute_term.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productAttributeTermService service
|
||||
|
||||
type ProductAttributeTermsQueryParaTerms struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
HideEmpty bool `url:"hide_empty,omitempty"`
|
||||
Parent int `url:"parent,omitempty"`
|
||||
Product int `url:"product,omitempty"`
|
||||
Slug string `url:"slug,omitempty"`
|
||||
}
|
||||
|
||||
func (m ProductAttributeTermsQueryParaTerms) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "include", "name", "slug", "term_group", "description", "count").Error("无效的排序类型"))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all product attribute terms
|
||||
func (s productAttributeTermService) All(attributeId int, params ProductAttributeTermsQueryParaTerms) (items []entity.ProductAttributeTerm, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get(fmt.Sprintf("/products/attributes/%d/terms", attributeId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a product attribute
|
||||
func (s productAttributeTermService) One(attributeId, termId int) (item entity.ProductAttributeTerm, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/attributes/%d/terms/%d", attributeId, termId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateProductAttributeTermRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
MenuOrder int `json:"menu_order,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateProductAttributeTermRequest) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create Create a product attribute term
|
||||
func (s productAttributeTermService) Create(attributeId int, req CreateProductAttributeTermRequest) (item entity.ProductAttributeTerm, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post(fmt.Sprintf("/products/attributes/%d/terms", attributeId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateProductAttributeTermRequest = CreateProductAttributeTermRequest
|
||||
|
||||
// Update Update a product attribute term
|
||||
func (s productAttributeTermService) Update(attributeId, termId int, req UpdateProductAttributeTermRequest) (item entity.ProductAttributeTerm, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/attributes/%d/terms/%d", attributeId, termId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete a product attribute term
|
||||
|
||||
func (s productAttributeTermService) Delete(attributeId, termId int, force bool) (item entity.ProductAttributeTerm, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/attributes/%d/terms/%d", attributeId, termId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update product attribute terms
|
||||
|
||||
type BatchProductAttributeTermsCreateItem = CreateProductAttributeTermRequest
|
||||
type BatchProductAttributeTermsUpdateItem struct {
|
||||
ID string `json:"id"`
|
||||
BatchProductAttributeTermsCreateItem
|
||||
}
|
||||
|
||||
type BatchProductAttributeTermsRequest struct {
|
||||
Create []BatchProductAttributeTermsCreateItem `json:"create,omitempty"`
|
||||
Update []BatchProductAttributeTermsUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchProductAttributeTermsRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchProductAttributeTermsResult struct {
|
||||
Create []entity.ProductAttributeTerm `json:"create"`
|
||||
Update []entity.ProductAttributeTerm `json:"update"`
|
||||
Delete []entity.ProductAttributeTerm `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete product attribute terms
|
||||
func (s productAttributeTermService) Batch(attributeId int, req BatchProductAttributeTermsRequest) (res BatchProductAttributeTermsResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post(fmt.Sprintf("/products/attributes/%d/batch", attributeId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
173
product_category.go
Normal file
173
product_category.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productCategoryService service
|
||||
|
||||
type ProductCategoriesQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
HideEmpty bool `url:"hide_empty,omitempty"`
|
||||
Parent int `url:"parent,omitempty"`
|
||||
Product int `url:"product,omitempty"`
|
||||
Slug string `url:"slug,omitempty"`
|
||||
}
|
||||
|
||||
func (m ProductCategoriesQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "include", "name", "slug", "term_group", "description", "count").Error("无效的排序字段"))),
|
||||
)
|
||||
}
|
||||
|
||||
func (s productCategoryService) All(params ProductCategoriesQueryParams) (items []entity.ProductCategory, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/products/categories")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s productCategoryService) One(id int) (item entity.ProductCategory, err error) {
|
||||
var res entity.ProductCategory
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/categories/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
if err = jsoniter.Unmarshal(resp.Body(), &res); err == nil {
|
||||
item = res
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增商品标签
|
||||
|
||||
type UpsertProductCategoryRequest struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Parent int `json:"parent,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Display string `json:"display,omitempty"`
|
||||
Image *entity.ProductImage `json:"image,omitempty"`
|
||||
MenuOrder int `json:"menu_order,omitempty"`
|
||||
}
|
||||
|
||||
type CreateProductCategoryRequest = UpsertProductCategoryRequest
|
||||
type UpdateProductCategoryRequest = UpsertProductCategoryRequest
|
||||
|
||||
func (m UpsertProductCategoryRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Name,
|
||||
validation.Required.Error("分类名称不能为空"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (s productCategoryService) Create(req CreateProductCategoryRequest) (item entity.ProductCategory, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/categories")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s productCategoryService) Update(id int, req UpdateProductCategoryRequest) (item entity.ProductCategory, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/categories/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s productCategoryService) Delete(id int, force bool) (item entity.ProductCategory, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/categories/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch category create,update and delete operation
|
||||
|
||||
type BatchProductCategoriesCreateItem = UpsertProductCategoryRequest
|
||||
type BatchProductCategoriesUpdateItem struct {
|
||||
ID int `json:"id"`
|
||||
UpsertProductTagRequest
|
||||
}
|
||||
type BatchProductCategoriesRequest struct {
|
||||
Create []BatchProductCategoriesCreateItem `json:"create,omitempty"`
|
||||
Update []BatchProductCategoriesUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchProductCategoriesRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchProductCategoriesResult struct {
|
||||
Create []entity.ProductTag `json:"create"`
|
||||
Update []entity.ProductTag `json:"update"`
|
||||
Delete []entity.ProductTag `json:"delete"`
|
||||
}
|
||||
|
||||
func (s productCategoryService) Batch(req BatchProductCategoriesRequest) (res BatchProductCategoriesResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/categories/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
92
product_category_test.go
Normal file
92
product_category_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/jsonx"
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProductCategoryService_All(t *testing.T) {
|
||||
params := ProductCategoriesQueryParams{}
|
||||
items, _, _, _, err := wooClient.Services.ProductCategory.All(params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.ProductCategory.All: %s", err.Error())
|
||||
} else {
|
||||
t.Logf("Items: %s", jsonx.ToPrettyJson(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductCategoryService_One(t *testing.T) {
|
||||
item, err := wooClient.Services.ProductCategory.One(15)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.ProductCategory.One: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, 15, item.ID, "one")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductCategoryService_CreateUpdateDelete(t *testing.T) {
|
||||
name := gofakeit.BeerName()
|
||||
req := CreateProductCategoryRequest{
|
||||
Name: name,
|
||||
}
|
||||
item, err := wooClient.Services.ProductCategory.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductCategory.Create: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, name, item.Name, "product category name")
|
||||
categoryId := item.ID
|
||||
|
||||
// Update
|
||||
newName := gofakeit.BeerName()
|
||||
updateReq := UpdateProductCategoryRequest{
|
||||
Name: newName,
|
||||
}
|
||||
item, err = wooClient.Services.ProductCategory.Update(categoryId, updateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductCategory.Update: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, newName, item.Name, "product category name")
|
||||
|
||||
// Delete
|
||||
_, err = wooClient.Services.ProductCategory.Delete(categoryId, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductCategory.Delete: %s", err.Error())
|
||||
}
|
||||
|
||||
// Check is exists
|
||||
_, err = wooClient.Services.ProductCategory.One(categoryId)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("%d is not deleted, error: %s", categoryId, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductCategoryService_Batch(t *testing.T) {
|
||||
n := 3
|
||||
createRequests := make([]BatchProductCategoriesCreateItem, n)
|
||||
names := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
req := BatchProductCategoriesCreateItem{
|
||||
Name: gofakeit.Word(),
|
||||
Description: gofakeit.Address().Address,
|
||||
}
|
||||
createRequests[i] = req
|
||||
names[i] = req.Name
|
||||
}
|
||||
batchReq := BatchProductCategoriesRequest{
|
||||
Create: createRequests,
|
||||
}
|
||||
result, err := wooClient.Services.ProductCategory.Batch(batchReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductCategory.Batch() error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, n, len(result.Create), "Batch create return len")
|
||||
returnNames := make([]string, 0)
|
||||
for _, d := range result.Create {
|
||||
returnNames = append(returnNames, d.Name)
|
||||
}
|
||||
assert.Equal(t, names, returnNames, "check names is equal")
|
||||
}
|
||||
192
product_review.go
Normal file
192
product_review.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productReviewService service
|
||||
|
||||
type ProductReviewsQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
After string `url:"after,omitempty"`
|
||||
Before string `url:"before,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
Reviewer []int `url:"reviewer,omitempty"`
|
||||
ReviewerExclude []int `url:"reviewer_exclude,omitempty"`
|
||||
ReviewerEmail []string `url:"reviewer_email,omitempty"`
|
||||
Product []int `url:"product,omitempty"`
|
||||
Status string `url:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (m ProductReviewsQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Before, validation.When(m.Before != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.After, validation.When(m.After != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "date", "date_gmt", "slug", "include", "product").Error("无效的排序方式"))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all product reviews
|
||||
func (s productReviewService) All(params ProductReviewsQueryParams) (items []entity.ProductReview, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
params.After = ToISOTimeString(params.After, false, true)
|
||||
params.Before = ToISOTimeString(params.Before, true, false)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/products/reviews")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a product review
|
||||
func (s productReviewService) One(id int) (item entity.ProductReview, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/reviews/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateProductReviewRequest struct {
|
||||
ProductId int `json:"product_id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Reviewer string `json:"reviewer,omitempty"`
|
||||
ReviewerEmail string `json:"reviewer_email,omitempty"`
|
||||
Review string `json:"review,omitempty"`
|
||||
Rating int `json:"rating,omitempty"`
|
||||
Verified bool `json:"verified,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateProductReviewRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Status, validation.When(m.Status != "", validation.In("approved", "hold", "spam", "unspam", "trash", "untrash").Error("无效的状态"))),
|
||||
validation.Field(&m.Rating,
|
||||
validation.Min(0).Error("评级最小为 {{threshold}}"),
|
||||
validation.Min(5).Error("评级最大为 {{threshold}}"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Create Create a product review
|
||||
func (s productReviewService) Create(req CreateProductReviewRequest) (item entity.ProductReview, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/reviews")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateProductReviewRequest = CreateProductReviewRequest
|
||||
|
||||
// Update Update a product review
|
||||
func (s productReviewService) Update(id int, req UpdateProductReviewRequest) (item entity.ProductReview, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/reviews/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete a product review
|
||||
|
||||
func (s productReviewService) Delete(id int, force bool) (item entity.ProductReview, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/reviews/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update product reviews
|
||||
|
||||
type BatchProductReviewsCreateItem = CreateProductReviewRequest
|
||||
type BatchProductReviewsUpdateItem struct {
|
||||
ID string `json:"id"`
|
||||
BatchProductReviewsCreateItem
|
||||
}
|
||||
|
||||
type BatchProductReviewsRequest struct {
|
||||
Create []BatchProductReviewsCreateItem `json:"create,omitempty"`
|
||||
Update []BatchProductReviewsUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchProductReviewsRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchProductReviewsResult struct {
|
||||
Create []entity.ProductReview `json:"create"`
|
||||
Update []entity.ProductReview `json:"update"`
|
||||
Delete []entity.ProductReview `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete product reviews
|
||||
func (s productReviewService) Batch(req BatchProductReviewsRequest) (res BatchProductReviewsResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/reviews/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
170
product_shipping_class.go
Normal file
170
product_shipping_class.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productShippingClassService service
|
||||
|
||||
type ProductShippingClassesQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
HideEmpty bool `url:"hide_empty,omitempty"`
|
||||
Parent int `url:"parent,omitempty"`
|
||||
Product int `url:"product,omitempty"`
|
||||
Slug string `url:"slug,omitempty"`
|
||||
}
|
||||
|
||||
func (m ProductShippingClassesQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "include", "name", "slug", "term_group", "description", "count").Error("无效的排序类型"))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all product shipping class
|
||||
func (s productShippingClassService) All(params ProductShippingClassesQueryParams) (items []entity.ProductShippingClass, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/products/shipping_classes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a product shipping class
|
||||
func (s productShippingClassService) One(id int) (item entity.ProductShippingClass, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/shipping_classes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateProductShippingClassRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateProductShippingClassRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Name, validation.Required.Error("名称不能为空")),
|
||||
)
|
||||
}
|
||||
|
||||
// Create Create a product shipping class
|
||||
func (s productShippingClassService) Create(req CreateProductShippingClassRequest) (item entity.ProductShippingClass, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/shipping_classes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateProductShippingClassRequest = CreateProductShippingClassRequest
|
||||
|
||||
// Update Update a product shipping class
|
||||
func (s productShippingClassService) Update(id int, req UpdateProductShippingClassRequest) (item entity.ProductShippingClass, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/shipping_classes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete a product shipping class
|
||||
func (s productShippingClassService) Delete(id int, force bool) (item entity.ProductShippingClass, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/shipping_classes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update product shipping classes
|
||||
|
||||
type BatchProductShippingClassesCreateItem = CreateProductShippingClassRequest
|
||||
type BatchProductShippingClassesUpdateItem struct {
|
||||
ID string `json:"id"`
|
||||
BatchProductShippingClassesCreateItem
|
||||
}
|
||||
|
||||
type BatchProductShippingClassesRequest struct {
|
||||
Create []BatchProductShippingClassesCreateItem `json:"create,omitempty"`
|
||||
Update []BatchProductShippingClassesUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchProductShippingClassesRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchProductShippingClassesResult struct {
|
||||
Create []entity.ProductShippingClass `json:"create"`
|
||||
Update []entity.ProductShippingClass `json:"update"`
|
||||
Delete []entity.ProductShippingClass `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete product shipping classes
|
||||
func (s productShippingClassService) Batch(req BatchProductShippingClassesRequest) (res BatchProductShippingClassesResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/shipping_classes/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
169
product_tag.go
Normal file
169
product_tag.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productTagService service
|
||||
|
||||
type ProductTagsQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
HideEmpty bool `url:"hide_empty,omitempty"`
|
||||
Product int `url:"product,omitempty"`
|
||||
Slug string `url:"slug,omitempty"`
|
||||
}
|
||||
|
||||
func (m ProductTagsQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "include", "name", "slug", "term_group", "description", "count").Error("无效的排序字段"))),
|
||||
)
|
||||
}
|
||||
|
||||
func (s productTagService) All(params ProductTagsQueryParams) (items []entity.ProductTag, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/products/tags")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s productTagService) One(id int) (item entity.ProductTag, err error) {
|
||||
var res entity.ProductTag
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/tags/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
if err = jsoniter.Unmarshal(resp.Body(), &res); err == nil {
|
||||
item = res
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增商品标签
|
||||
|
||||
type UpsertProductTagRequest struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
type CreateProductTagRequest = UpsertProductTagRequest
|
||||
type UpdateProductTagRequest = UpsertProductTagRequest
|
||||
|
||||
func (m UpsertProductTagRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Name,
|
||||
validation.Required.Error("标签名称不能为空"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (s productTagService) Create(req CreateProductTagRequest) (item entity.ProductTag, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/tags")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s productTagService) Update(id int, req UpdateProductTagRequest) (item entity.ProductTag, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/tags/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s productTagService) Delete(id int, force bool) (item entity.ProductTag, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/tags/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch tag create,update and delete operation
|
||||
|
||||
type BatchProductTagsCreateItem = UpsertProductTagRequest
|
||||
|
||||
type BatchProductTagsUpdateItem struct {
|
||||
ID int `json:"id"`
|
||||
UpsertProductTagRequest
|
||||
}
|
||||
type BatchProductTagsRequest struct {
|
||||
Create []BatchProductTagsCreateItem `json:"create,omitempty"`
|
||||
Update []BatchProductTagsUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchProductTagsRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchProductTagsResult struct {
|
||||
Create []entity.ProductTag `json:"create"`
|
||||
Update []entity.ProductTag `json:"update"`
|
||||
Delete []entity.ProductTag `json:"delete"`
|
||||
}
|
||||
|
||||
func (s productTagService) Batch(req BatchProductTagsRequest) (res BatchProductTagsResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/tags/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
91
product_tag_test.go
Normal file
91
product_tag_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/jsonx"
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProductTagService_All(t *testing.T) {
|
||||
params := ProductTagsQueryParams{}
|
||||
items, _, _, _, err := wooClient.Services.ProductTag.All(params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.ProductTag.All: %s", err.Error())
|
||||
} else {
|
||||
t.Logf("Items: %s", jsonx.ToPrettyJson(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductTagService_One(t *testing.T) {
|
||||
item, err := wooClient.Services.ProductTag.One(51)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.ProductTag.One: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, 51, item.ID, "one")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductTagService_CreateUpdateDelete(t *testing.T) {
|
||||
name := gofakeit.BeerName()
|
||||
req := CreateProductTagRequest{
|
||||
Name: name,
|
||||
}
|
||||
item, err := wooClient.Services.ProductTag.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductTag.Create: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, name, item.Name, "product tag name")
|
||||
tagId := item.ID
|
||||
|
||||
// Update
|
||||
newName := gofakeit.BeerName()
|
||||
updateReq := UpdateProductTagRequest{
|
||||
Name: newName,
|
||||
}
|
||||
item, err = wooClient.Services.ProductTag.Update(tagId, updateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductTag.Update: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, newName, item.Name, "product tag name")
|
||||
|
||||
// Delete
|
||||
_, err = wooClient.Services.ProductTag.Delete(tagId, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductTag.Delete: %s", err.Error())
|
||||
}
|
||||
|
||||
// Check is exists
|
||||
_, err = wooClient.Services.ProductTag.One(tagId)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("%d is not deleted, error: %s", tagId, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductTagService_Batch(t *testing.T) {
|
||||
n := 3
|
||||
createRequests := make([]BatchProductTagsCreateItem, n)
|
||||
names := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
req := BatchProductTagsCreateItem{
|
||||
Name: gofakeit.Word(),
|
||||
}
|
||||
createRequests[i] = req
|
||||
names[i] = req.Name
|
||||
}
|
||||
batchReq := BatchProductTagsRequest{
|
||||
Create: createRequests,
|
||||
}
|
||||
result, err := wooClient.Services.ProductTag.Batch(batchReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.ProductTag.Batch() error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, n, len(result.Create), "Batch create return len")
|
||||
returnNames := make([]string, 0)
|
||||
for _, d := range result.Create {
|
||||
returnNames = append(returnNames, d.Name)
|
||||
}
|
||||
assert.Equal(t, names, returnNames, "check names is equal")
|
||||
}
|
||||
106
product_test.go
Normal file
106
product_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProductService_All(t *testing.T) {
|
||||
params := ProductsQueryParams{}
|
||||
items, _, _, _, err := wooClient.Services.Product.All(params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Product.All: %s", err.Error())
|
||||
} else {
|
||||
if len(items) > 0 {
|
||||
mainId = items[0].ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductService_One(t *testing.T) {
|
||||
t.Run("TestProductService_All", TestProductService_All)
|
||||
product, err := wooClient.Services.Product.One(mainId)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Product.One: %s", err.Error())
|
||||
} else {
|
||||
assert.Equal(t, mainId, product.ID, "product id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductService_CreateUpdateDelete(t *testing.T) {
|
||||
name := gofakeit.Word()
|
||||
req := CreateProductRequest{
|
||||
Name: name,
|
||||
}
|
||||
item, err := wooClient.Services.Product.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Product.Create error: %s", err.Error())
|
||||
}
|
||||
productId := item.ID
|
||||
assert.Equal(t, name, item.Name, "product name")
|
||||
name = gofakeit.Word()
|
||||
updateReq := UpdateProductRequest{
|
||||
Name: name,
|
||||
}
|
||||
_, err = wooClient.Services.Product.Update(productId, updateReq)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Product.Update error: %s", err.Error())
|
||||
}
|
||||
item, err = wooClient.Services.Product.One(productId)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Product.One error: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, name, item.Name, "product name")
|
||||
|
||||
// Delete
|
||||
_, err = wooClient.Services.Product.Delete(productId, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Product.Delete error: %s", err.Error())
|
||||
}
|
||||
_, err = wooClient.Services.Product.One(productId)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("wooClient.Services.Product.Delete(%d) failed", productId)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductService_All_Filter(t *testing.T) {
|
||||
name := gofakeit.Word()
|
||||
sku := "SKU WITH SPACES"
|
||||
|
||||
req := CreateProductRequest{
|
||||
Name: name,
|
||||
SKU: sku,
|
||||
}
|
||||
item, err := wooClient.Services.Product.Create(req)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Product.Create error: %s", err.Error())
|
||||
}
|
||||
productId := item.ID
|
||||
assert.Equal(t, name, item.Name, "product name")
|
||||
name = gofakeit.Word()
|
||||
|
||||
params := ProductsQueryParams{
|
||||
SKU: sku,
|
||||
}
|
||||
items, _, _, _, err := wooClient.Services.Product.All(params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.Product.All: %s", err.Error())
|
||||
} else {
|
||||
if len(items) == 0 {
|
||||
t.Fatalf("wooClient.Services.Product.All error")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete
|
||||
_, err = wooClient.Services.Product.Delete(productId, true)
|
||||
if err != nil {
|
||||
t.Fatalf("wooClient.Services.Product.Delete error: %s", err.Error())
|
||||
}
|
||||
_, err = wooClient.Services.Product.One(productId)
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("wooClient.Services.Product.Delete(%d) failed", productId)
|
||||
}
|
||||
}
|
||||
227
product_variation.go
Normal file
227
product_variation.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type productVariationService service
|
||||
|
||||
// Product variations
|
||||
|
||||
type ProductVariationsQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search,omitempty"`
|
||||
After string `url:"after,omitempty"`
|
||||
Before string `url:"before,omitempty"`
|
||||
Exclude []int `url:"exclude,omitempty"`
|
||||
Include []int `url:"include,omitempty"`
|
||||
Parent []int `url:"parent,omitempty"`
|
||||
ParentExclude []int `url:"parent_exclude,omitempty"`
|
||||
Slug string `url:"slug,omitempty"`
|
||||
Status string `url:"status,omitempty"`
|
||||
SKU string `url:"sku,omitempty"`
|
||||
TaxClass string `url:"tax_class,omitempty"`
|
||||
OnSale string `url:"on_sale,omitempty"`
|
||||
MinPrice float64 `url:"min_price,omitempty"`
|
||||
MaxPrice float64 `url:"max_price,omitempty"`
|
||||
StockStatus string `url:"stock_status,omitempty"`
|
||||
}
|
||||
|
||||
func (m ProductVariationsQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Before, validation.When(m.Before != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.After, validation.When(m.After != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "title", "include", "date", "slug").Error("无效的排序字段"))),
|
||||
validation.Field(&m.Status, validation.When(m.Status != "", validation.In("any", "draft", "pending", "private", "publish").Error("Invalid status value"))),
|
||||
validation.Field(&m.TaxClass, validation.When(m.TaxClass != "", validation.In("standard", "reduced-rate", "zero-rate").Error("Invalid tax class"))),
|
||||
validation.Field(&m.StockStatus, validation.When(m.StockStatus != "", validation.In("instock", "outofstock", "onbackorder").Error("Invalid stock status"))),
|
||||
validation.Field(&m.MinPrice, validation.Min(0.0)),
|
||||
validation.Field(&m.MaxPrice, validation.Min(m.MinPrice)),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all product variations
|
||||
func (s productVariationService) All(productId int, params ProductVariationsQueryParams) (items []entity.ProductVariation, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
params.After = ToISOTimeString(params.After, false, true)
|
||||
params.Before = ToISOTimeString(params.Before, true, false)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get(fmt.Sprintf("/products/%d/variations", productId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve a product variation
|
||||
func (s productVariationService) One(productId, variationId int) (item entity.ProductVariation, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/%d/variations/%d", productId, variationId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateProductVariationRequest struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
SKU string `json:"sku,omitempty"`
|
||||
RegularPrice float64 `json:"regular_price,string,omitempty"`
|
||||
SalePrice float64 `json:"sale_price,string,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Virtual bool `json:"virtual,omitempty"`
|
||||
Downloadable bool `json:"downloadable,omitempty"`
|
||||
Downloads []entity.ProductDownload `json:"downloads,omitempty"`
|
||||
DownloadLimit int `json:"download_limit,omitempty"`
|
||||
DownloadExpiry int `json:"download_expiry,omitempty"`
|
||||
TaxStatus string `json:"tax_status,omitempty"`
|
||||
TaxClass string `json:"tax_class,omitempty"`
|
||||
ManageStock bool `json:"manage_stock,omitempty"`
|
||||
StockQuantity int `json:"stock_quantity,omitempty"`
|
||||
StockStatus string `json:"stock_status,omitempty"`
|
||||
Backorders string `json:"backorders,omitempty"`
|
||||
Weight float64 `json:"weight,string,omitempty"`
|
||||
Dimension *entity.ProductDimension `json:"dimensions,omitempty"`
|
||||
ShippingClass string `json:"shipping_class,omitempty"`
|
||||
Image *entity.ProductImage `json:"image,omitempty"`
|
||||
Attributes []entity.ProductVariationAttribute `json:"attributes,omitempty"`
|
||||
MenuOrder int `json:"menu_order,omitempty"`
|
||||
MetaData []entity.Meta `json:"meta_data,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateProductVariationRequest) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s productVariationService) Create(productId int, req CreateProductVariationRequest) (item entity.ProductVariation, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Post(fmt.Sprintf("/products/%d/variations", productId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
type UpdateProductVariationRequest = CreateProductVariationRequest
|
||||
|
||||
func (s productVariationService) Update(productId int, req UpdateProductVariationRequest) (item entity.ProductVariation, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Put(fmt.Sprintf("/products/%d/variations", productId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete
|
||||
|
||||
func (s productVariationService) Delete(productId, variationId int, force bool) (item entity.ProductVariation, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/%d/variations/%d", productId, variationId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
} else {
|
||||
err = ErrorWrap(resp.StatusCode(), "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch Update
|
||||
|
||||
type BatchProductVariationsCreateItem = CreateProductVariationRequest
|
||||
type BatchProductVariationsUpdateItem struct {
|
||||
ID int `json:"id"`
|
||||
CreateProductVariationRequest
|
||||
}
|
||||
|
||||
type BatchProductVariationsRequest struct {
|
||||
Create []BatchProductVariationsCreateItem `json:"create,omitempty"`
|
||||
Update []BatchProductVariationsUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchProductVariationsRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchProductVariationsResult struct {
|
||||
Create []entity.ProductVariation `json:"create"`
|
||||
Update []entity.ProductVariation `json:"update"`
|
||||
Delete []entity.ProductVariation `json:"delete"`
|
||||
}
|
||||
|
||||
func (s productVariationService) Batch(req BatchProductVariationsRequest) (res BatchProductVariationsResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/variations/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
17
product_variation_test.go
Normal file
17
product_variation_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/jsonx"
|
||||
)
|
||||
|
||||
func TestProductVariationService_All(t *testing.T) {
|
||||
params := ProductVariationsQueryParams{}
|
||||
items, _, _, _, err := wooClient.Services.ProductVariation.All(1, params)
|
||||
if err != nil {
|
||||
t.Errorf("wooClient.Services.ProductVariation.All: %s", err.Error())
|
||||
} else {
|
||||
t.Logf("items: %s", jsonx.ToPrettyJson(items))
|
||||
}
|
||||
}
|
||||
66
query_params.go
Normal file
66
query_params.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
const (
|
||||
SortAsc = "asc"
|
||||
SortDesc = "desc"
|
||||
)
|
||||
|
||||
const (
|
||||
ViewContext = "view"
|
||||
EditContext = "edit"
|
||||
)
|
||||
|
||||
type queryParams struct {
|
||||
Page int `url:"page,omitempty"`
|
||||
PerPage int `url:"per_page,omitempty"`
|
||||
Offset int `url:"offset,omitempty"`
|
||||
Order string `url:"order,omitempty"`
|
||||
OrderBy string `url:"order_by,omitempty"`
|
||||
Context string `url:"context,omitempty"`
|
||||
}
|
||||
|
||||
func (q *queryParams) TidyVars() *queryParams {
|
||||
if q.Page <= 0 {
|
||||
q.Page = 1
|
||||
}
|
||||
if q.PerPage <= 0 {
|
||||
q.PerPage = 10
|
||||
} else if q.PerPage > 100 {
|
||||
q.PerPage = 100
|
||||
}
|
||||
if q.Offset < 0 {
|
||||
q.Offset = 0
|
||||
}
|
||||
|
||||
if q.Order == "" {
|
||||
q.Order = SortAsc
|
||||
} else {
|
||||
q.Order = strings.ToLower(q.Order)
|
||||
if q.Order != SortDesc {
|
||||
q.OrderBy = SortAsc
|
||||
}
|
||||
}
|
||||
|
||||
if q.Context == "" {
|
||||
q.Context = ViewContext
|
||||
} else {
|
||||
q.Context = strings.ToLower(q.Context)
|
||||
if q.Context != EditContext {
|
||||
q.Context = ViewContext
|
||||
}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// change to url.values
|
||||
func toValues(i interface{}) (values url.Values) {
|
||||
values, _ = query.Values(i)
|
||||
return
|
||||
}
|
||||
181
report.go
Normal file
181
report.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
"github.com/araddon/dateparse"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type reportService service
|
||||
|
||||
type ReportsQueryParams struct {
|
||||
Context string `url:"context,omitempty"`
|
||||
Period string `url:"period,omitempty"`
|
||||
DateMin string `url:"date_min,omitempty"`
|
||||
DateMax string `url:"date_max,omitempty"`
|
||||
}
|
||||
|
||||
func (m ReportsQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Period, validation.When(m.Period != "", validation.In("week", "month", "last_month", "year").Error("无效的报表周期"))),
|
||||
validation.Field(&m.DateMin,
|
||||
validation.Required.Error("报表开始时间不能为空"),
|
||||
validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}),
|
||||
),
|
||||
validation.Field(&m.DateMax,
|
||||
validation.Required.Error("报表结束时间不能为空"),
|
||||
validation.By(func(value interface{}) (err error) {
|
||||
dateStr, _ := value.(string)
|
||||
err = IsValidateTime(dateStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dateMin, err := dateparse.ParseAny(m.DateMin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dateMax, err := dateparse.ParseAny(m.DateMax)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if dateMax.Before(dateMin) {
|
||||
return fmt.Errorf("结束时间不能小于 %s", m.DateMin)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// All list all reports
|
||||
func (s reportService) All() (items []entity.Report, err error) {
|
||||
resp, err := s.httpClient.R().Get("/reports")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sales reports
|
||||
|
||||
type SalesReportsQueryParams = ReportsQueryParams
|
||||
|
||||
// SalesReports list all sales reports
|
||||
func (s reportService) SalesReports(params SalesReportsQueryParams) (items []entity.SaleReport, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.DateMin = ToISOTimeString(params.DateMin, true, false)
|
||||
params.DateMax = ToISOTimeString(params.DateMax, false, true)
|
||||
resp, err := s.httpClient.R().
|
||||
SetQueryParamsFromValues(toValues(params)).
|
||||
Get("/reports/sales")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TopSellerReports list all sales reports
|
||||
|
||||
type TopSellerReportsQueryParams = SalesReportsQueryParams
|
||||
|
||||
func (s reportService) TopSellerReports(params SalesReportsQueryParams) (items []entity.TopSellerReport, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.DateMin = ToISOTimeString(params.DateMin, true, false)
|
||||
params.DateMax = ToISOTimeString(params.DateMax, false, true)
|
||||
resp, err := s.httpClient.R().
|
||||
SetQueryParamsFromValues(toValues(params)).
|
||||
Get("/reports/top_sellers")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CouponTotals retrieve coupons totals
|
||||
func (s reportService) CouponTotals() (items []entity.CouponTotal, err error) {
|
||||
resp, err := s.httpClient.R().Get("/reports/coupons/totals")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CustomerTotals retrieve customer totals
|
||||
func (s reportService) CustomerTotals() (items []entity.CustomerTotal, err error) {
|
||||
resp, err := s.httpClient.R().Get("/reports/customers/totals")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OrderTotals retrieve customer totals
|
||||
func (s reportService) OrderTotals() (items []entity.OrderTotal, err error) {
|
||||
resp, err := s.httpClient.R().Get("/reports/orders/totals")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ProductTotals retrieve product totals
|
||||
func (s reportService) ProductTotals() (items []entity.OrderTotal, err error) {
|
||||
resp, err := s.httpClient.R().Get("/reports/products/totals")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReviewTotals retrieve review totals
|
||||
func (s reportService) ReviewTotals() (items []entity.OrderTotal, err error) {
|
||||
resp, err := s.httpClient.R().Get("/reports/reviews/totals")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
21
report_test.go
Normal file
21
report_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReportService_All(t *testing.T) {
|
||||
_, err := wooClient.Services.Report.All()
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
|
||||
func TestReportService_SalesReports(t *testing.T) {
|
||||
req := SalesReportsQueryParams{
|
||||
DateMin: "2022-01-01",
|
||||
DateMax: "2022-01-01",
|
||||
}
|
||||
_, err := wooClient.Services.Report.SalesReports(req)
|
||||
assert.Equal(t, nil, err)
|
||||
}
|
||||
20
setting.go
Normal file
20
setting.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type settingService service
|
||||
|
||||
func (s settingService) Groups() (items []entity.SettingGroup, err error) {
|
||||
resp, err := s.httpClient.R().Get("/settings")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
68
setting_option.go
Normal file
68
setting_option.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// https://woocommerce.github.io/woocommerce-rest-api-docs/?php#setting-options
|
||||
|
||||
type settingOptionService service
|
||||
|
||||
// All list all setting options
|
||||
func (s settingOptionService) All(settingId string) (items []entity.SettingOption, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/settings/%s", settingId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve a setting option
|
||||
func (s settingOptionService) One(groupId, optionId string) (item entity.SettingOption, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/settings/%s/%s", groupId, optionId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type UpdateSettingOptionRequest struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (m UpdateSettingOptionRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Value, validation.Required.Error("设置值不能为空")),
|
||||
)
|
||||
}
|
||||
|
||||
func (s settingOptionService) Update(groupId, optionId string, req UpdateSettingOptionRequest) (item entity.SettingOption, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Put(fmt.Sprintf("/settings/%s/%s", groupId, optionId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
36
shipping_method.go
Normal file
36
shipping_method.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type shippingMethodService service
|
||||
|
||||
// All list all shipping methods
|
||||
func (s shippingMethodService) All() (items []entity.ShippingMethod, err error) {
|
||||
resp, err := s.httpClient.R().Get("/shipping_methods")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve a shipping method
|
||||
func (s shippingMethodService) One(id int) (item entity.ShippingMethod, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/shipping_methods/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
100
shipping_zone.go
Normal file
100
shipping_zone.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type shippingZoneService service
|
||||
|
||||
// All list all shipping zones
|
||||
func (s shippingZoneService) All() (items []entity.ShippingZone, err error) {
|
||||
resp, err := s.httpClient.R().Get("/shipping/zones")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve a shipping zone
|
||||
func (s shippingZoneService) One(id int) (item entity.ShippingZone, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/shipping/zones/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateShippingZoneRequest struct {
|
||||
Name string `json:"name"`
|
||||
Order int `json:"order"`
|
||||
}
|
||||
|
||||
func (m CreateShippingZoneRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Name, validation.Required.Error("名称不能为空")),
|
||||
validation.Field(&m.Order, validation.Min(0).Error("排序值不能小于 {{.threshold}}")),
|
||||
)
|
||||
}
|
||||
|
||||
func (s shippingZoneService) Create(req CreateShippingZoneRequest) (item entity.ShippingZone, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/shipping/zones")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
type UpdateShippingZoneRequest = CreateShippingZoneRequest
|
||||
|
||||
func (s shippingZoneService) Update(id int, req UpdateShippingZoneRequest) (item entity.ShippingZone, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/shipping/zones/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete delete a shipping zone
|
||||
func (s shippingZoneService) Delete(id int, force bool) (item entity.ShippingZone, err error) {
|
||||
resp, err := s.httpClient.R().SetBody(map[string]bool{"force": force}).Delete(fmt.Sprintf("/shipping/zones/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
66
shipping_zone_location.go
Normal file
66
shipping_zone_location.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type shippingZoneLocationService service
|
||||
|
||||
// All list all shipping zone locations
|
||||
func (s shippingZoneLocationService) All(shippingZoneId int) (items []entity.ShippingZoneLocation, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/shipping/zones/%d/locations", shippingZoneId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
type UpdateShippingZoneLocationsRequest []entity.ShippingZoneLocation
|
||||
|
||||
func (m UpdateShippingZoneLocationsRequest) Validate() error {
|
||||
return validation.Validate(m, validation.Required.Error("待更新数据不能为空"),
|
||||
validation.By(func(value interface{}) error {
|
||||
items, ok := value.([]entity.ShippingZoneLocation)
|
||||
if !ok {
|
||||
return errors.New("待更新数据错误")
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
err := validation.ValidateStruct(&item,
|
||||
validation.Field(&item.Code, validation.Required.Error("代码不能为空")),
|
||||
validation.Field(&item.Type, validation.In("postcode", "country", "state", "continent").Error("类型错误")),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (s shippingZoneLocationService) Update(shippingZoneId int, req UpdateShippingZoneLocationsRequest) (items []entity.ShippingZoneLocation, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/shipping/zones/%d/locations", shippingZoneId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
116
shipping_zone_method.go
Normal file
116
shipping_zone_method.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type shippingZoneMethodService service
|
||||
|
||||
// All list all shipping zone methods
|
||||
func (s shippingZoneMethodService) All(zoneId int) (items []entity.ShippingZoneMethod, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/shipping/zones/%d/methods", zoneId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One retrieve a shipping zone method
|
||||
func (s shippingZoneMethodService) One(zoneId, methodId int) (item entity.ShippingZoneMethod, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/shipping/zones/%d/methods/%d", zoneId, methodId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Include include a shipping method to a shipping zone
|
||||
|
||||
type ShippingZoneMethodIncludeRequest struct {
|
||||
MethodId string `json:"method_id"`
|
||||
}
|
||||
|
||||
func (m ShippingZoneMethodIncludeRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.MethodId, validation.Required.Error("配送方式不能为空")),
|
||||
)
|
||||
}
|
||||
|
||||
func (s shippingZoneMethodService) Include(zoneId int, req ShippingZoneMethodIncludeRequest) (item entity.ShippingZoneMethod, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Post(fmt.Sprintf("/shipping/zones/%d/methods", zoneId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update
|
||||
|
||||
type UpdateShippingZoneMethodSetting struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type UpdateShippingZoneMethodRequest struct {
|
||||
Order int `json:"order"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Settings UpdateShippingZoneMethodSetting `json:"settings"`
|
||||
}
|
||||
|
||||
func (m UpdateShippingZoneMethodRequest) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s shippingZoneMethodService) Update(zoneId, methodId int, req UpdateShippingZoneMethodRequest) (item entity.ShippingZoneMethod, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(req).
|
||||
Put(fmt.Sprintf("/shipping/zones/%d/methods/%d", zoneId, methodId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete delete a shipping zone
|
||||
func (s shippingZoneMethodService) Delete(zoneId, methodId int, force bool) (item entity.ShippingZone, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/shipping/zones/%d/%d", zoneId, methodId))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
20
system_status.go
Normal file
20
system_status.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type systemStatusService service
|
||||
|
||||
func (s systemStatusService) All() (item entity.SystemStatus, err error) {
|
||||
resp, err := s.httpClient.R().Get("/system_status")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
46
system_status_tool.go
Normal file
46
system_status_tool.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type systemStatusToolService service
|
||||
|
||||
func (s systemStatusToolService) All() (item entity.SystemStatusTool, err error) {
|
||||
resp, err := s.httpClient.R().Get("/system_status/tools")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s systemStatusToolService) One(id string) (item entity.SystemStatusTool, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/system_status/tools/%s", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s systemStatusToolService) Run(id string) (item entity.SystemStatusTool, err error) {
|
||||
resp, err := s.httpClient.R().Put(fmt.Sprintf("/system_status/tools/%s", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
74
tax_class.go
Normal file
74
tax_class.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type taxClassService service
|
||||
|
||||
// All List all tax classes
|
||||
func (s taxClassService) All() (items []entity.TaxClass, err error) {
|
||||
resp, err := s.httpClient.R().Get("/tax/classes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create tax class request
|
||||
|
||||
type CreateTaxClassRequest struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (m CreateTaxClassRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Name, validation.Required.Error("名称不能为空")),
|
||||
)
|
||||
}
|
||||
|
||||
// Create Create a tax class
|
||||
func (s taxClassService) Create(req CreateTaxClassRequest) (item entity.TaxClass, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/taxes/classes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete Delete a tax classes
|
||||
func (s taxClassService) Delete(slug string, force bool) (item entity.TaxClass, err error) {
|
||||
slug = strings.TrimSpace(slug)
|
||||
if slug == "" {
|
||||
err = errors.New("slug 参数不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(map[string]bool{"force": force}).Delete(fmt.Sprintf("/taxes/classes/%s", slug))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
172
tax_rate.go
Normal file
172
tax_rate.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type taxRateService service
|
||||
|
||||
type TaxRatesQueryParams struct {
|
||||
queryParams
|
||||
Class string `url:"class,omitempty"`
|
||||
}
|
||||
|
||||
func (m TaxRatesQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.OrderBy, validation.When(m.OrderBy != "", validation.In("id", "order", "priority").Error("无效的排序字段"))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all tax rate
|
||||
func (s taxRateService) All(params TaxRatesQueryParams) (items []entity.TaxRate, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/taxes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a tax rate
|
||||
func (s taxRateService) One(id int) (item entity.TaxRate, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/taxes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateTaxRateRequest struct {
|
||||
Country string `json:"country,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Postcode string `json:"postcode,omitempty"`
|
||||
City string `json:"city,omitempty"`
|
||||
Postcodes []string `json:"postcodes,omitempty"`
|
||||
Cities []string `json:"cities,omitempty"`
|
||||
Rate string `json:"rate,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
Compound bool `json:"compound,omitempty"`
|
||||
Shipping bool `json:"shipping,omitempty"`
|
||||
Order int `json:"order,omitempty"`
|
||||
Class string `json:"class,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateTaxRateRequest) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create Create a product attribute
|
||||
func (s taxRateService) Create(req CreateTaxRateRequest) (item entity.TaxRate, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/taxes")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateTaxRateRequest = CreateTaxRateRequest
|
||||
|
||||
// Update Update a tax rate
|
||||
func (s taxRateService) Update(id int, req UpdateTaxRateRequest) (item entity.TaxRate, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/taxes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete a tax rate
|
||||
func (s taxRateService) Delete(id int, force bool) (item entity.TaxRate, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/taxes/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update tax rates
|
||||
|
||||
type BatchTaxRatesCreateItem = CreateTaxRateRequest
|
||||
type BatchTaxRatesUpdateItem struct {
|
||||
ID string `json:"id"`
|
||||
BatchTaxRatesCreateItem
|
||||
}
|
||||
|
||||
type BatchTaxRatesRequest struct {
|
||||
Create []BatchTaxRatesCreateItem `json:"create,omitempty"`
|
||||
Update []BatchTaxRatesUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchTaxRatesRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchTaxRatesResult struct {
|
||||
Create []entity.TaxRate `json:"create"`
|
||||
Update []entity.TaxRate `json:"update"`
|
||||
Delete []entity.TaxRate `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete tax rates
|
||||
func (s taxRateService) Batch(req BatchTaxRatesRequest) (res BatchTaxRatesResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/taxes/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
52
utils.go
Normal file
52
utils.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.cloudyne.io/go/woogo/constant"
|
||||
"github.com/araddon/dateparse"
|
||||
)
|
||||
|
||||
// ToISOTimeString Convert to iso time string
|
||||
// If date format is invalid, then return original value
|
||||
// If dateStr include time part, and you set addMinTimeString/addMaxTimeString to true,
|
||||
// but still return original dateStr value.
|
||||
func ToISOTimeString(dateStr string, addMinTimeString, addMaxTimeString bool) (s string) {
|
||||
dateStr = strings.TrimSpace(dateStr)
|
||||
if dateStr == "" {
|
||||
return
|
||||
}
|
||||
|
||||
s = dateStr
|
||||
format, err := dateparse.ParseFormat(dateStr)
|
||||
if err == nil && (format == constant.DateFormat || format == constant.DatetimeFormat || format == constant.WooDatetimeFormat) {
|
||||
if strings.Index(dateStr, " ") == -1 {
|
||||
if addMinTimeString {
|
||||
dateStr += " 00:00:00"
|
||||
}
|
||||
if addMaxTimeString {
|
||||
dateStr += " 23:59:59"
|
||||
}
|
||||
}
|
||||
if t, err := dateparse.ParseAny(dateStr); err == nil {
|
||||
return t.Format(time.RFC3339)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// IsValidateTime Is validate time
|
||||
func IsValidateTime(dateStr string) error {
|
||||
format, err := dateparse.ParseFormat(dateStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch format {
|
||||
case constant.DateFormat, constant.DatetimeFormat, constant.WooDatetimeFormat:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("%s 日期格式无效", dateStr)
|
||||
}
|
||||
}
|
||||
25
utils_test.go
Normal file
25
utils_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestToISOTimeString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tag string
|
||||
date string
|
||||
addMin bool
|
||||
addMax bool
|
||||
expected string
|
||||
}{
|
||||
{"min", "2020-01-01", true, false, "2020-01-01T00:00:00Z"},
|
||||
{"has time", "2020-01-01 01:02:03", true, false, "2020-01-01T01:02:03Z"},
|
||||
{"bad format", "2020-01-0101:02:03", true, false, "2020-01-0101:02:03"},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
s := ToISOTimeString(testCase.date, testCase.addMin, testCase.addMax)
|
||||
assert.Equal(t, testCase.expected, s, testCase.tag)
|
||||
}
|
||||
}
|
||||
183
webhook.go
Normal file
183
webhook.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.cloudyne.io/go/woogo/entity"
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type webhookService service
|
||||
|
||||
type WebhooksQueryParams struct {
|
||||
queryParams
|
||||
Search string `url:"search"`
|
||||
After string `url:"after"`
|
||||
Before string `url:"before"`
|
||||
Exclude []int `url:"exclude"`
|
||||
Include []int `url:"include"`
|
||||
Status string `url:"status"`
|
||||
}
|
||||
|
||||
func (m WebhooksQueryParams) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.Before, validation.When(m.Before != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
validation.Field(&m.After, validation.When(m.After != "", validation.By(func(value interface{}) error {
|
||||
dateStr, _ := value.(string)
|
||||
return IsValidateTime(dateStr)
|
||||
}))),
|
||||
)
|
||||
}
|
||||
|
||||
// All List all webhooks
|
||||
func (s webhookService) All(params WebhooksQueryParams) (items []entity.Webhook, total, totalPages int, isLastPage bool, err error) {
|
||||
if err = params.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params.TidyVars()
|
||||
params.After = ToISOTimeString(params.After, false, true)
|
||||
params.Before = ToISOTimeString(params.Before, true, false)
|
||||
resp, err := s.httpClient.R().SetQueryParamsFromValues(toValues(params)).Get("/products/webhooks")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &items)
|
||||
total, totalPages, isLastPage = parseResponseTotal(params.Page, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// One Retrieve a webhook
|
||||
func (s webhookService) One(id int) (item entity.Webhook, err error) {
|
||||
resp, err := s.httpClient.R().Get(fmt.Sprintf("/products/webhooks/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create
|
||||
|
||||
type CreateWebhookRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
DeliveryURL string `json:"delivery_url,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
func (m CreateWebhookRequest) Validate() error {
|
||||
return validation.ValidateStruct(&m,
|
||||
validation.Field(&m.DeliveryURL, validation.When(m.DeliveryURL != "", is.URL.Error("投递 URL 格式错误"))),
|
||||
validation.Field(&m.Status, validation.When(m.Status != "", validation.In("active", "paused", "disabled").Error("无效的状态值"))),
|
||||
)
|
||||
}
|
||||
|
||||
// Create Create a product attribute
|
||||
func (s webhookService) Create(req CreateWebhookRequest) (item entity.Webhook, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/webhooks")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UpdateWebhookRequest = CreateWebhookRequest
|
||||
|
||||
// Update Update a webhook
|
||||
func (s webhookService) Update(id int, req UpdateWebhookRequest) (item entity.Webhook, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Put(fmt.Sprintf("/products/webhooks/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete a webhook
|
||||
|
||||
func (s webhookService) Delete(id int, force bool) (item entity.Webhook, err error) {
|
||||
resp, err := s.httpClient.R().
|
||||
SetBody(map[string]bool{"force": force}).
|
||||
Delete(fmt.Sprintf("/products/webhooks/%d", id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &item)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Batch update webhooks
|
||||
|
||||
type BatchWebhooksCreateItem = CreateWebhookRequest
|
||||
type BatchWebhooksUpdateItem struct {
|
||||
ID string `json:"id"`
|
||||
BatchWebhooksCreateItem
|
||||
}
|
||||
|
||||
type BatchWebhooksRequest struct {
|
||||
Create []BatchWebhooksCreateItem `json:"create,omitempty"`
|
||||
Update []BatchWebhooksUpdateItem `json:"update,omitempty"`
|
||||
Delete []int `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
func (m BatchWebhooksRequest) Validate() error {
|
||||
if len(m.Create) == 0 && len(m.Update) == 0 && len(m.Delete) == 0 {
|
||||
return errors.New("无效的请求数据")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BatchWebhooksResult struct {
|
||||
Create []entity.Webhook `json:"create"`
|
||||
Update []entity.Webhook `json:"update"`
|
||||
Delete []entity.Webhook `json:"delete"`
|
||||
}
|
||||
|
||||
// Batch Batch create/update/delete webhooks
|
||||
func (s webhookService) Batch(req BatchWebhooksRequest) (res BatchWebhooksResult, err error) {
|
||||
if err = req.Validate(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.R().SetBody(req).Post("/products/webhooks/batch")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.IsSuccess() {
|
||||
err = jsoniter.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return
|
||||
}
|
||||
335
woo.go
Normal file
335
woo.go
Normal file
@@ -0,0 +1,335 @@
|
||||
// package woogo is a Woo Commerce lib.
|
||||
//
|
||||
// Quick start:
|
||||
//
|
||||
// b, err := os.ReadFile("./config/config_test.json")
|
||||
// if err != nil {
|
||||
// panic(fmt.Sprintf("Read config error: %s", err.Error()))
|
||||
// }
|
||||
// var c config.Config
|
||||
// err = jsoniter.Unmarshal(b, &c)
|
||||
// if err != nil {
|
||||
// panic(fmt.Sprintf("Parse config file error: %s", err.Error()))
|
||||
// }
|
||||
//
|
||||
// wooClient = NewClient(c)
|
||||
// // Query an order
|
||||
// order, err := wooClient.Services.Order.One(1)
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// } else {
|
||||
// fmt.Println(fmt.Sprintf("%#v", order))
|
||||
// }
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"git.cloudyne.io/go/hiscaler-gox/inx"
|
||||
"git.cloudyne.io/go/hiscaler-gox/stringx"
|
||||
"git.cloudyne.io/go/woogo/config"
|
||||
"github.com/go-resty/resty/v2"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/json-iterator/go/extra"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "1.0.3"
|
||||
UserAgent = "WooCommerce API Client-Golang/" + Version
|
||||
HashAlgorithm = "HMAC-SHA256"
|
||||
)
|
||||
|
||||
// https://woocommerce.github.io/woocommerce-rest-api-docs/?php#request-response-format
|
||||
const (
|
||||
BadRequestError = 400 // 错误的请求
|
||||
UnauthorizedError = 401 // 身份验证或权限错误
|
||||
NotFoundError = 404 // 访问资源不存在
|
||||
InternalServerError = 500 // 服务器内部错误
|
||||
MethodNotImplementedError = 501 // 方法未实现
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("WooCommerce: not found")
|
||||
|
||||
func init() {
|
||||
extra.RegisterFuzzyDecoders()
|
||||
}
|
||||
|
||||
type WooCommerce struct {
|
||||
Debug bool // Is debug mode
|
||||
Logger *log.Logger // Log
|
||||
Services services // WooCommerce API services
|
||||
}
|
||||
|
||||
type service struct {
|
||||
debug bool // Is debug mode
|
||||
logger *log.Logger // Log
|
||||
httpClient *resty.Client // HTTP client
|
||||
}
|
||||
|
||||
type services struct {
|
||||
Coupon couponService
|
||||
Customer customerService
|
||||
Order orderService
|
||||
OrderNote orderNoteService
|
||||
OrderRefund orderRefundService
|
||||
Product productService
|
||||
ProductVariation productVariationService
|
||||
ProductAttribute productAttributeService
|
||||
ProductAttributeTerm productAttributeTermService
|
||||
ProductCategory productCategoryService
|
||||
ProductShippingClass productShippingClassService
|
||||
ProductTag productTagService
|
||||
ProductReview productReviewService
|
||||
Report reportService
|
||||
TaxRate taxRateService
|
||||
TaxClass taxClassService
|
||||
Webhook webhookService
|
||||
Setting settingService
|
||||
SettingOption settingOptionService
|
||||
PaymentGateway paymentGatewayService
|
||||
ShippingZone shippingZoneService
|
||||
ShippingZoneLocation shippingZoneLocationService
|
||||
ShippingZoneMethod shippingZoneMethodService
|
||||
ShippingMethod shippingMethodService
|
||||
SystemStatus systemStatusService
|
||||
SystemStatusTool systemStatusToolService
|
||||
Data dataService
|
||||
}
|
||||
|
||||
// OAuth signature
|
||||
func oauthSignature(config config.Config, method, endpoint string, params url.Values) string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(config.ConsumerSecret)
|
||||
if config.Version != "v1" && config.Version != "v2" {
|
||||
sb.WriteByte('&')
|
||||
}
|
||||
consumerSecret := sb.String()
|
||||
|
||||
sb.Reset()
|
||||
sb.WriteString(method)
|
||||
sb.WriteByte('&')
|
||||
sb.WriteString(url.QueryEscape(endpoint))
|
||||
sb.WriteByte('&')
|
||||
sb.WriteString(url.QueryEscape(params.Encode()))
|
||||
mac := hmac.New(sha256.New, stringx.ToBytes(consumerSecret))
|
||||
mac.Write(stringx.ToBytes(sb.String()))
|
||||
signatureBytes := mac.Sum(nil)
|
||||
return base64.StdEncoding.EncodeToString(signatureBytes)
|
||||
}
|
||||
|
||||
// NewClient Creates a new WooCommerce client
|
||||
//
|
||||
// You must give a config with NewClient method params.
|
||||
// After you can operate data use this client.
|
||||
func NewClient(config config.Config) *WooCommerce {
|
||||
logger := log.New(os.Stdout, "[ WooCommerce ] ", log.LstdFlags|log.Llongfile)
|
||||
wooClient := &WooCommerce{
|
||||
Debug: config.Debug,
|
||||
Logger: logger,
|
||||
}
|
||||
// Add default value
|
||||
if config.Version == "" {
|
||||
config.Version = "v3"
|
||||
} else {
|
||||
config.Version = strings.ToLower(config.Version)
|
||||
if !inx.StringIn(config.Version, "v1", "v2", "v3") {
|
||||
config.Version = "v3"
|
||||
}
|
||||
}
|
||||
if config.Timeout < 2 {
|
||||
config.Timeout = 2
|
||||
}
|
||||
|
||||
httpClient := resty.New().
|
||||
SetDebug(config.Debug).
|
||||
SetBaseURL(strings.TrimRight(config.URL, "/") + "/wp-json/wc/" + config.Version).
|
||||
SetHeaders(map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"User-Agent": UserAgent,
|
||||
}).
|
||||
SetAllowGetMethodPayload(true).
|
||||
SetTimeout(config.Timeout * time.Second).
|
||||
SetTransport(&http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL},
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: config.Timeout * time.Second,
|
||||
}).DialContext,
|
||||
}).
|
||||
OnBeforeRequest(func(client *resty.Client, request *resty.Request) error {
|
||||
params := url.Values{}
|
||||
for k, vs := range request.QueryParam {
|
||||
var v string
|
||||
switch len(vs) {
|
||||
case 0:
|
||||
continue
|
||||
case 1:
|
||||
v = vs[0]
|
||||
default:
|
||||
// if is array params, must convert to string, example: status=1&status=2 replace to status=1,2
|
||||
v = strings.Join(vs, ",")
|
||||
}
|
||||
params.Set(k, v)
|
||||
}
|
||||
if strings.HasPrefix(config.URL, "https") {
|
||||
// basicAuth
|
||||
if config.AddAuthenticationToURL {
|
||||
params.Add("consumer_key", config.ConsumerKey)
|
||||
params.Add("consumer_secret", config.ConsumerSecret)
|
||||
} else {
|
||||
// Set to header
|
||||
client.SetAuthScheme("Basic").
|
||||
SetAuthToken(fmt.Sprintf("%s %s", config.ConsumerKey, config.ConsumerSecret))
|
||||
}
|
||||
} else {
|
||||
// oAuth
|
||||
params.Add("oauth_consumer_key", config.ConsumerKey)
|
||||
params.Add("oauth_timestamp", strconv.Itoa(int(time.Now().Unix())))
|
||||
nonce := make([]byte, 16)
|
||||
rand.Read(nonce)
|
||||
sha1Nonce := fmt.Sprintf("%x", sha1.Sum(nonce))
|
||||
params.Add("oauth_nonce", sha1Nonce)
|
||||
params.Add("oauth_signature_method", HashAlgorithm)
|
||||
params.Add("oauth_signature", oauthSignature(config, request.Method, client.BaseURL+request.URL, params))
|
||||
}
|
||||
request.QueryParam = params
|
||||
return nil
|
||||
}).
|
||||
OnAfterResponse(func(client *resty.Client, response *resty.Response) (err error) {
|
||||
if response.IsError() {
|
||||
r := struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
if err = jsoniter.Unmarshal(response.Body(), &r); err == nil {
|
||||
err = ErrorWrap(response.StatusCode(), r.Message)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
logger.Printf("OnAfterResponse error: %s", err.Error())
|
||||
}
|
||||
return
|
||||
})
|
||||
if config.Debug {
|
||||
httpClient.EnableTrace()
|
||||
}
|
||||
|
||||
jsoniter.RegisterTypeDecoderFunc("float64", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
switch iter.WhatIsNext() {
|
||||
case jsoniter.StringValue:
|
||||
var t float64
|
||||
v := strings.TrimSpace(iter.ReadString())
|
||||
if v != "" {
|
||||
var err error
|
||||
if t, err = strconv.ParseFloat(v, 64); err != nil {
|
||||
iter.Error = err
|
||||
return
|
||||
}
|
||||
}
|
||||
*((*float64)(ptr)) = t
|
||||
default:
|
||||
*((*float64)(ptr)) = iter.ReadFloat64()
|
||||
}
|
||||
})
|
||||
httpClient.JSONMarshal = jsoniter.Marshal
|
||||
httpClient.JSONUnmarshal = jsoniter.Unmarshal
|
||||
xService := service{
|
||||
debug: config.Debug,
|
||||
logger: logger,
|
||||
httpClient: httpClient,
|
||||
}
|
||||
wooClient.Services = services{
|
||||
Coupon: (couponService)(xService),
|
||||
Customer: (customerService)(xService),
|
||||
Order: (orderService)(xService),
|
||||
OrderNote: (orderNoteService)(xService),
|
||||
OrderRefund: (orderRefundService)(xService),
|
||||
Product: (productService)(xService),
|
||||
ProductVariation: (productVariationService)(xService),
|
||||
ProductAttribute: (productAttributeService)(xService),
|
||||
ProductAttributeTerm: (productAttributeTermService)(xService),
|
||||
ProductCategory: (productCategoryService)(xService),
|
||||
ProductShippingClass: (productShippingClassService)(xService),
|
||||
ProductTag: (productTagService)(xService),
|
||||
ProductReview: (productReviewService)(xService),
|
||||
Report: (reportService)(xService),
|
||||
TaxRate: (taxRateService)(xService),
|
||||
TaxClass: (taxClassService)(xService),
|
||||
Webhook: (webhookService)(xService),
|
||||
Setting: (settingService)(xService),
|
||||
SettingOption: (settingOptionService)(xService),
|
||||
PaymentGateway: (paymentGatewayService)(xService),
|
||||
ShippingZone: (shippingZoneService)(xService),
|
||||
ShippingZoneLocation: (shippingZoneLocationService)(xService),
|
||||
ShippingZoneMethod: (shippingZoneMethodService)(xService),
|
||||
ShippingMethod: (shippingMethodService)(xService),
|
||||
SystemStatus: (systemStatusService)(xService),
|
||||
SystemStatusTool: (systemStatusToolService)(xService),
|
||||
Data: (dataService)(xService),
|
||||
}
|
||||
return wooClient
|
||||
}
|
||||
|
||||
// Parse response header, get total and total pages, and check it is last page
|
||||
func parseResponseTotal(currentPage int, resp *resty.Response) (total, totalPages int, isLastPage bool) {
|
||||
if currentPage == 0 {
|
||||
currentPage = 1
|
||||
}
|
||||
value := resp.Header().Get("X-Wp-Total")
|
||||
if value != "" {
|
||||
total, _ = strconv.Atoi(value)
|
||||
}
|
||||
|
||||
value = resp.Header().Get("X-Wp-Totalpages")
|
||||
if value != "" {
|
||||
totalPages, _ = strconv.Atoi(value)
|
||||
}
|
||||
isLastPage = currentPage >= totalPages
|
||||
return
|
||||
}
|
||||
|
||||
// ErrorWrap wrap an error, if status code is 200, return nil, otherwise return an error
|
||||
func ErrorWrap(code int, message string) error {
|
||||
if code == http.StatusOK {
|
||||
return nil
|
||||
}
|
||||
|
||||
if code == NotFoundError {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
message = strings.TrimSpace(message)
|
||||
if message == "" {
|
||||
switch code {
|
||||
case BadRequestError:
|
||||
message = "Bad request"
|
||||
case UnauthorizedError:
|
||||
message = "Unauthorized operation, please confirm whether you have permission"
|
||||
case NotFoundError:
|
||||
message = "Resource not found"
|
||||
case InternalServerError:
|
||||
message = "Server internal error"
|
||||
case MethodNotImplementedError:
|
||||
message = "method not implemented"
|
||||
default:
|
||||
message = "Unknown error"
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%d: %s", code, message)
|
||||
}
|
||||
90
woo_test.go
Normal file
90
woo_test.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package woogo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.cloudyne.io/go/woogo/config"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
var wooClient *WooCommerce
|
||||
|
||||
var orderId, noteId int
|
||||
var mainId, childId int
|
||||
|
||||
// Operate data use WooCommerce for golang
|
||||
func Example() {
|
||||
b, err := os.ReadFile("./config/config_test.json")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Read config error: %s", err.Error()))
|
||||
}
|
||||
var c config.Config
|
||||
err = jsoniter.Unmarshal(b, &c)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Parse config file error: %s", err.Error()))
|
||||
}
|
||||
|
||||
wooClient = NewClient(c)
|
||||
// Query an order
|
||||
order, err := wooClient.Services.Order.One(1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println(fmt.Sprintf("%#v", order))
|
||||
}
|
||||
|
||||
// Query orders
|
||||
params := OrdersQueryParams{
|
||||
After: "2022-06-10",
|
||||
}
|
||||
params.PerPage = 100
|
||||
for {
|
||||
orders, total, totalPages, isLastPage, err := wooClient.Services.Order.All(params)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
fmt.Println(fmt.Sprintf("Page %d/%d", total, totalPages))
|
||||
// read orders
|
||||
for _, order := range orders {
|
||||
_ = order
|
||||
}
|
||||
if err != nil || isLastPage {
|
||||
break
|
||||
}
|
||||
params.Page++
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleErrorWrap() {
|
||||
err := ErrorWrap(200, "Ok")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getOrderId(t *testing.T) {
|
||||
t.Log("Execute getOrderId test")
|
||||
items, _, _, _, err := wooClient.Services.Order.All(OrdersQueryParams{})
|
||||
if err != nil || len(items) == 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
orderId = items[0].ID
|
||||
mainId = items[0].ID
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
b, err := os.ReadFile("./config/config_test.json")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Read config error: %s", err.Error()))
|
||||
}
|
||||
var c config.Config
|
||||
err = jsoniter.Unmarshal(b, &c)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Parse config file error: %s", err.Error()))
|
||||
}
|
||||
|
||||
wooClient = NewClient(c)
|
||||
m.Run()
|
||||
}
|
||||
Reference in New Issue
Block a user