Compare commits

...

133 commits

Author SHA1 Message Date
c62a02aa1f
Merge pull request #100 from dendd1/master
fix for clear cart method
2025-04-04 15:14:19 +03:00
Суханов Данила
871459c8b7 fix for clear cart method 2025-04-04 15:12:04 +03:00
d0b0dd59d6
Merge pull request #99 from dendd1/master
support /customer-interaction/site/cart
2025-04-01 14:51:34 +03:00
Суханов Данила
cc83657f32 support /customer-interaction/site/cart 2025-04-01 14:49:13 +03:00
53e2ab5130
Merge pull request #98 from Neur0toxine/fix/costs_filter_field_types
Fix costs filter field types
2025-03-18 17:20:29 +03:00
0123057e86 fix lint issues 2025-03-18 17:18:06 +03:00
a8cae8b200 fix type in the tests 2025-03-18 17:11:38 +03:00
5fa64ff23e
Merge pull request #97 from Neur0toxine/rate-limit
rate limiter
2025-03-18 17:02:23 +03:00
e2113a640e fix linter 2025-03-18 17:00:42 +03:00
2f33b56cd3 rate limiter 2025-03-18 16:53:45 +03:00
Pavel Tsayukov
8892fe6895 Fix types of fields in CostsFilter 2025-02-18 14:55:13 +03:00
Pavel Tsayukov
908f16b173 Fix wrong name of query key for OrderExternalIds field 2025-02-18 14:55:12 +03:00
2859073353
Merge pull request #95 from Neur0toxine/fix-regionId-cityId
fix regionId & cityId unmarshaling error
2025-01-31 11:50:00 +03:00
a482cd1a2f update linter config 2025-01-31 11:47:59 +03:00
92a5741c84 fix ci 2025-01-31 11:41:22 +03:00
634ec386b1 fix regionId & cityId unmarshaling error 2025-01-31 11:38:47 +03:00
d101ddb097
Merge pull request #94 from RenCurs/transport-action
add mg transport action
2024-12-04 10:32:34 +03:00
Ruslan Efanov
3186470ed9 add mg transport action 2024-12-04 10:24:19 +03:00
904796f97a
Support for urlLike filter in GET /api/v5/store/products 2024-09-25 11:29:48 +03:00
Vlasov
2f0f55be42 Added support for urlLike filter in GET /api/v5/store/products 2024-09-24 18:38:29 +03:00
c79e6c0497
Fix loyalty account request fields 2024-09-11 15:01:53 +03:00
2587dd786a
add offers method 2024-09-11 14:56:04 +03:00
Ruslan Efanov
2333dbf493 add offers method 2024-09-11 14:54:33 +03:00
Aleksandr Kokockin
d195460141 rename expireDate field 2024-09-10 12:47:44 +03:00
Aleksandr Kokockin
e6fc8f1e0e rename page field 2024-09-10 12:47:24 +03:00
c9e5b1f79d
add mgTransport callbacks 2024-09-04 13:34:37 +03:00
3282ab045e
Merge branch 'master' into add-mgTransport-callbacks 2024-09-04 13:25:44 +03:00
4398f85214
add field refreshToken for mg integrations 2024-09-02 11:23:29 +03:00
Ruslan Efanov
77b49e04ab add field refreshToken for mg integrations 2024-09-02 11:13:24 +03:00
29062b8bf0 add title field to ChatVisitedPage 2024-03-20 15:01:10 +03:00
7c8e142cab country & city fields instead of ip field 2024-03-19 12:01:02 +03:00
e28631dcb2 fieldalignment fix 2024-03-19 12:01:02 +03:00
a4de6df146 fix field names 2024-03-19 12:01:02 +03:00
81a09e24d4 callbacks structs 2024-03-19 12:01:02 +03:00
b5e7c3ff33 add mgTransport callbacks 2024-03-19 12:01:02 +03:00
76135226fb
add services support 2023-12-28 12:55:00 +03:00
a72a57fbe1 add services support 2023-12-28 12:52:45 +03:00
c2a33378b8
add new API methods & new parameters 2023-09-06 15:44:19 +03:00
Danila
583362bfe3 Adding functionality from recent updates to the library 2023-09-06 07:40:46 +03:00
Danila
ab648cd06a Adding functionality from recent updates to the library 2023-09-05 18:09:36 +03:00
Danila
5c6d2ebead Adding functionality from recent updates to the library 2023-09-05 15:06:08 +03:00
407ecf5066
add mg settings to settings response 2023-08-11 12:23:51 +03:00
Kirill Sukhorukov
2875b8620a add mg settings to settings response 2023-08-10 12:25:39 +03:00
b445dfdfe5
add new order field DialogID and fix custom fields marshaling 2023-08-01 08:01:12 +03:00
1e9692ec15
Update marshaling.go 2023-07-31 16:52:08 +03:00
Kirill Sukhorukov
0ed90e8351 add new order field DialogID and fix custom fields marshaling 2023-07-27 14:06:03 +03:00
076ce77bdb
fix for template api methods 2023-05-29 15:08:31 +03:00
Ruslan Efanov
8377a8789d fix model fields 2023-05-26 17:19:44 +03:00
280f078632
add methods for mg templates 2023-05-24 11:20:05 +03:00
Ruslan Efanov
e6efd56497 fix test 2023-05-23 15:17:38 +03:00
Ruslan Efanov
9bd3d646fc correct templates methods 2023-05-23 13:09:05 +03:00
Ефанов Руслан
16e2bc304c add methods for mg templates 2023-05-17 17:51:33 +03:00
d08ed4e1b2
Add multi dictionary custom field support 2023-05-03 21:51:13 +03:00
Vlasov
afb7c1b881 Add multi dictionary custom field support 2023-05-03 18:04:53 +03:00
e513134df9
update linter and it's rules 2023-01-24 17:42:34 +03:00
5b7ed8697e
update types with new fields 2023-01-24 17:42:13 +03:00
107a4d150b update types with new fields 2023-01-24 11:13:20 +03:00
a51bab6df4
Add Settings field NonWorkingDays 2023-01-17 17:52:31 +03:00
Ruslan Efanov
49905ab9c6 update linter and it's rules 2022-12-30 11:19:20 +03:00
06b0395a93
/api/v5/notifications/send support 2022-12-29 16:20:23 +03:00
67d72f2fe1 /api/v5/notifications/send support 2022-12-29 16:17:29 +03:00
Vragov Roman
bafdf24755 Add Settings field NonWorkingDays 2022-12-26 16:18:03 +03:00
6c5eb72848
add support for more methods 2022-12-09 14:37:12 +03:00
Ruslan Efanov
141c247482 fix linter errors 2022-12-09 12:34:16 +03:00
Ruslan Efanov
22cbdd7fcf add parameter of failed entity in response for upload methods for orders and customers 2022-12-09 11:38:21 +03:00
Ruslan Efanov
1bd5b77b3f refactoring
correct example for methods
fix linters error
add `/api/v5/orders/{externalId}/delivery/cancel`, `/api/v5/store/product-groups/create`, `/api/v5/store/product-groups/{externalId}/edit`
2022-12-08 17:31:08 +03:00
Ruslan Efanov
ddc2b3f785 add /api/v5/loyalty/loyalties, /api/v5/loyalty/loyalties/{id}
add field in `deliveryPaymentTypes` for ` /api/v5/reference/delivery-types`
2022-12-08 13:32:54 +03:00
Ruslan Efanov
90f790e148 add /api/v5/loyalty/accounts 2022-12-07 17:40:52 +03:00
Ruslan Efanov
921d4c1295 Add/edit methods
add `/api/v5/store/products/batch/create`, `/api/v5/store/products/batch/edit`
add fields `isError` and `isPreprocessing` for statuses of integration delivery
add field `deliveryType` for response of creating integration module with inegration delivery
add `/api/v5/loyalty/account/create`, `/api/v5/loyalty/account/{id}`, `/api/v5/loyalty/account/{id}/edit`
add `/api/v5/loyalty/account/{id}/activate`, `/api/v5/loyalty/account/{id}/bonus/credit`
add `/api/v5/loyalty/account/{id}/bonus/{status}/details`
2022-12-07 15:06:59 +03:00
Ruslan Efanov
2f6ab28d85 add method of batch products operations 2022-12-07 15:06:59 +03:00
573cbb9679
remove omitempty for active field in json tag for IntegrationModule 2022-12-07 09:38:59 +03:00
Ruslan Efanov
50e25d61bc change type on active field 2022-12-06 22:27:56 +03:00
Ruslan Efanov
a29e2419ff remove omitempty for active field in json tag for IntegrationModule struct 2022-12-06 17:35:32 +03:00
37df181264
/api/v5/loyalty/bonus/operations method 2022-08-09 17:06:34 +03:00
26e66ab630
paidAtFrom and paidAtTo order filter fields 2022-08-09 17:04:49 +03:00
c482546e07
remove unnecessary spaces from tags 2022-08-09 16:50:06 +03:00
Tyschitskaya Maria
2117664f1f fix docs for loyalty bonus operations methods 2022-08-09 15:27:01 +03:00
Tyschitskaya Maria
5e45297368 loyalty bonus operations methods 2022-08-09 13:45:11 +03:00
defernest
6f7fb5ceb9 add 'paidAt<From|To>' filter for orders request 2022-06-15 18:53:33 +03:00
Alex Lushpai
f6b4a15f78
'Ordering' field to 'Site' definition 2022-05-24 12:28:19 +03:00
Alexander Kozlov
e316e3565c added 'Ordering' field to 'Site' definition 2022-05-23 11:38:20 +03:00
Alex Lushpai
995d19007d
В метод Setting добавлены настройки рабочего времени 2022-04-29 13:13:38 +03:00
Vragov Roman
1355012c58 В метод Setting добавлены настройки рабочего времени 2022-04-29 12:27:47 +03:00
pm14kas
06433b7c19
updated Site definition (#66)
Co-authored-by: Alexander Kozlov <kozlov_a@intaro.email>
2022-04-28 11:18:18 +03:00
Alex Lushpai
b39f9d3f69
Remove omitempty from SuccessfulResponse type 2022-04-27 17:11:47 +03:00
gridnev
3b5f7826ba Remove omitempty from SuccessfulResponse type 2022-04-27 16:25:34 +03:00
Alex Lushpai
b0a082d2bc
disable unused linter for tests 2022-04-22 09:38:38 +03:00
Tyschitskaya Maria
94d281d2c8 disable unused linter for tests 2022-04-21 12:26:07 +03:00
Alex Lushpai
9c7c641217
Trim base URL in the constructor 2022-04-18 12:57:35 +03:00
31ede381e4 trim base URL in the constructor 2022-04-18 12:23:18 +03:00
cd567755e7
Fix for the UpdateScopes method 2022-04-13 17:32:26 +03:00
735103b9bd
Сorrect implementation for update-scopes method
* correct implementation for update-scopes method
* update CI
2022-04-12 15:37:15 +03:00
tishmaria90
1309825638
Add update scopes method (#61)
* add update scopes method

* apiKey field

* example fix

Co-authored-by: Maria Tyschitskaya <tyschitskaya@retailcrm.ru>
2022-04-01 17:22:53 +03:00
Akolzin Dmitry
54810c21cd
APISystemInfo method (#60) 2022-02-03 18:09:33 +03:00
Alex Gridnev
1e51894cf4
Add BillingInfo model to ResponseInfo (#59) 2021-12-15 13:09:57 +03:00
cdbf1d6e33
Structures for the one-step connection (#58) 2021-11-17 16:57:35 +03:00
Alex Lushpai
8cfe7034e6
fix order[items][][properties][] unmarshaling for empty values 2021-11-08 13:42:26 +03:00
726f90f29c fix order[items][][properties][] unmarshaling for empty values 2021-11-08 13:25:56 +03:00
Alex Lushpai
e9b2a04a4a
Fix incorrect marshaling for some string maps 2021-10-27 17:25:15 +03:00
2ffbcde857 fix incorrect marshaling for some string maps 2021-10-27 17:18:50 +03:00
97b1abf470
Correct v2 (#55)
* refactor library and upgrade version
* remove useless environment variable
* remove unsupported versions from go.mod
* fixes for error handling & data types
* different improvements for errors
* fixes for types and tests
* better coverage, fix error with the unmarshalers
2021-10-27 15:49:06 +03:00
Alex Lushpai
269764175e
Update gock 2021-10-26 15:56:23 +03:00
a3314c4bfa update gock 2021-10-26 15:45:16 +03:00
Alex Lushpai
a6cbd20518
Add scopes to credentials method response 2021-10-26 13:39:25 +03:00
Akolzin Dmitry
2b9f12f262 add scopes to credentials method response 2021-10-26 13:16:58 +03:00
Alex Lushpai
8b0e5dafc2
Merge pull request #52 from Neur0toxine/update-test-matrix
add go 1.17 to test matrix
2021-09-06 16:47:15 +03:00
Alex Lushpai
c9bc7892bc
Merge pull request #51 from Neur0toxine/add-housing-field
closes #50 - add housing field to Address struct
2021-09-06 16:44:51 +03:00
5ca00138b2 add go 1.17 to test matrix 2021-09-06 16:39:14 +03:00
0daa3fe879 closes #50 - add housing field to Address struct 2021-09-06 16:37:43 +03:00
RenCurs
baddbe95b2
Remove errs package, change logic for error processing (#48) 2021-04-26 12:09:21 +03:00
03fc70d679
Switch to go modules & update linter config (#49)
* update linter action config & fix for tests
* removed deprecate linters from the config
* switch to go modules
2021-04-16 14:25:25 +03:00
Yura
d551e91985
Serializating the Tag structure (#47)
closes #46
2021-03-05 14:49:13 +03:00
Alex Lushpai
e68199efb3
Static code analysis 2021-02-09 22:20:23 +03:00
58dfe0f16f Static code analysis 2021-02-09 16:10:58 +03:00
Akolzin Dmitry
7fc96e5542
fixes DeliveryTracking method (#44) 2021-01-29 14:29:44 +03:00
Alex Lushpai
3d71115827
Update product name, cleanup annotations (#43) 2020-12-15 13:33:24 +03:00
Akolzin Dmitry
c24cb50d9e
Move CI to GitHub Actions (#42) 2020-11-26 13:28:34 +03:00
Alex Lushpai
3a3b4590c3
Enhanced the capabilities of OrderDeliveryData 2020-08-06 14:09:56 +03:00
Daniel Weiser
56a053a61d UnmarshalJSON refactoring 2020-08-05 19:29:25 +03:00
Daniel Weiser
3be09aff75 Closes #33; New error messages 2020-08-05 17:54:47 +03:00
Daniel Weiser
f46bffa9b3 Enhanced the capabilities of OrderDeliveryData 2020-08-05 17:34:38 +03:00
Alex Lushpai
e9c6bf7474
New field Tags in Customer struct 2020-08-04 15:49:06 +03:00
Daniel Weiser
05ac38e199 fix Customer struct 2020-08-04 13:13:52 +03:00
Daniel Weiser
84a002991f new field Tags in Customer struct 2020-08-04 13:09:42 +03:00
Alex Lushpai
d105cf9dee
pass parameter 'by' in payment edit method 2020-08-03 13:16:57 +03:00
Daniel Weiser
13ec136ef0 closes #38; OrderPaymentEdit test 2020-08-03 12:56:15 +03:00
Daniel Weiser
c6d33b527a pass parameter 'by' in payment edit method 2020-07-29 17:27:52 +03:00
Alex Lushpai
a975815296
Return order data with orderCreate 2020-07-14 12:29:16 +03:00
aa1745c13a Closes #34 return order data with orderCreate 2020-07-14 12:18:22 +03:00
Alex Lushpai
2245c40212
Settings method 2020-07-14 11:36:57 +03:00
12ae59c7b1 settings method 2020-07-14 10:22:36 +03:00
Alex Lushpai
38acbef244
Fixes OrdersStatuses method description 2020-06-29 17:16:50 +03:00
Akolzin Dmitry
e9bfb72faf fixes OrdersStatuses method description 2020-06-29 16:34:01 +03:00
36 changed files with 15079 additions and 8414 deletions

79
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,79 @@
name: ci
on:
push:
branches:
- '**'
tags-ignore:
- '*.*'
pull_request:
env:
RETAILCRM_URL: https://test.retailcrm.pro
RETAILCRM_KEY: key
jobs:
golangci:
name: lint
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Go 1.23
uses: actions/setup-go@v3
with:
go-version: '1.23'
- name: Get dependencies
run: |
go mod tidy
cp .env.dist .env
- name: Lint code with golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.62.2
only-new-issues: true
skip-pkg-cache: true
args: --build-tags=testutils
tests:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.19', '1.20', '1.21', '1.22', '1.23', 'stable']
include:
- go-version: '1.23'
coverage: 1
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go mod tidy
cp .env.dist .env
- name: Tests
env:
COVERAGE: ${{ matrix.coverage }}
if: env.COVERAGE != 1
run: |
go install gotest.tools/gotestsum@latest
gotestsum --format testdox ./... -tags=testutils -v -cpu 2 -timeout 60s -race
- name: Tests with coverage
env:
COVERAGE: ${{ matrix.coverage }}
if: env.COVERAGE == 1
run: |
go install gotest.tools/gotestsum@latest
gotestsum --format testdox ./... -tags=testutils -v -cpu 2 -timeout 60s -race -cover -coverprofile=coverage.txt -covermode=atomic "$d"
- name: Coverage
env:
COVERAGE: ${{ matrix.coverage }}
if: env.COVERAGE == 1
run: |
go install github.com/axw/gocov/gocov@latest
gocov convert ./coverage.txt | gocov report
bash <(curl -s https://codecov.io/bash)

1
.gitignore vendored
View file

@ -5,6 +5,7 @@
# Folders
_obj
vendor
# Architecture specific extensions/prefixes
*.[568vq]

213
.golangci.yml Normal file
View file

@ -0,0 +1,213 @@
run:
skip-dirs-use-default: true
allow-parallel-runners: true
issues:
exclude-files:
- testutils.go
exclude-rules:
- path: _test\.go
linters:
- lll
- errcheck
- misspell
- ineffassign
- whitespace
- makezero
- errcheck
- funlen
- goconst
- gocognit
- gocyclo
- godot
- unused
- errchkjson
- varnamelen
exclude-use-default: true
exclude-case-sensitive: false
max-issues-per-linter: 0
max-same-issues: 0
fix: true
output:
formats:
- format: colored-line-number
sort-results: true
# Linters below do not support go1.18 yet because of generics.
# See https://github.com/golangci/golangci-lint/issues/2649
# - bodyclose
# - sqlclosecheck
linters:
disable-all: true
enable:
- asciicheck
- asasalint
- varnamelen
- reassign
- nilnil
- nilerr
- nakedret
- goprintffuncname
- typecheck
- errchkjson
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- unparam
- dogsled
- dupl
- errorlint
- exhaustive
- exportloopref
- copyloopvar
- funlen
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- goimports
- revive
- gosec
- lll
- makezero
- misspell
- nestif
- prealloc
- predeclared
- unconvert
- whitespace
linters-settings:
govet:
check-shadowing: false
disable-all: true
enable:
- assign
- atomic
- atomicalign
- bools
- buildtag
- copylocks
- httpresponse
- loopclosure
- lostcancel
- printf
- shift
- stdmethods
- structtag
- tests
- unmarshal
- unreachable
- unsafeptr
settings:
printf:
funcs:
- (*log.Logger).Fatal
- (*log.Logger).Fatalf
- (*log.Logger).Fatalln
- (*log.Logger).Panic
- (*log.Logger).Panicf
- (*log.Logger).Panicln
- (*log.Logger).Print
- (*log.Logger).Printf
- (*log.Logger).Println
- (*testing.common).Error
- (*testing.common).Errorf
- (*testing.common).Fatal
- (*testing.common).Fatalf
- (*testing.common).Log
- (*testing.common).Logf
- (*testing.common).Skip
- (*testing.common).Skipf
- (testing.TB).Error
- (testing.TB).Errorf
- (testing.TB).Fatal
- (testing.TB).Fatalf
- (testing.TB).Log
- (testing.TB).Logf
- (testing.TB).Skip
- (testing.TB).Skipf
- fmt.Errorf
- fmt.Fprint
- fmt.Fprintf
- fmt.Fprintln
- fmt.Print
- fmt.Printf
- fmt.Println
- fmt.Sprint
- fmt.Sprintf
- fmt.Sprintln
- log.Fatal
- log.Fatalf
- log.Fatalln
- log.Panic
- log.Panicf
- log.Panicln
- log.Print
- log.Printf
- log.Println
- runtime/trace.Logf
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Fatalf
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Panicf
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Panicf
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Criticalf
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Errorf
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Warningf
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Noticef
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Infof
- (github.com/retailcrm/mg-transport-core/core.LoggerInterface).Debugf
unused:
check-exported: false
unparam:
check-exported: false
dogsled:
max-blank-identifiers: 3
dupl:
threshold: 200
errorlint:
errorf: true
asserts: false
comparison: false
exhaustive:
check-generated: false
default-signifies-exhaustive: false
funlen:
lines: 90
statements: 40
gocognit:
min-complexity: 25
gocyclo:
min-complexity: 25
goimports:
local-prefixes: github.com/retailcrm/api-client-go/v2
lll:
line-length: 160
misspell:
locale: US
nestif:
min-complexity: 4
whitespace:
multi-if: false
multi-func: false
varnamelen:
max-distance: 10
ignore-map-index-ok: true
ignore-type-assert-ok: true
ignore-chan-recv-ok: true
ignore-decls:
- t *testing.T
- e error
- i int
severity:
default-severity: error
case-sensitive: false
service:
golangci-lint-version: 1.62.x

View file

@ -1,15 +0,0 @@
language: go
go:
- '1.8'
- '1.9'
- '1.10'
- '1.11'
- '1.12'
- '1.13'
before_install:
- go get -v github.com/google/go-querystring/query
- go get -v github.com/h2non/gock
- cp .env.dist .env
script: ./go.test.sh
after_success:
- bash <(curl -s https://codecov.io/bash)

View file

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015-2018 RetailDriver LLC
Copyright (c) 2015-2020 RetailDriver LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

125
README.md
View file

@ -1,59 +1,60 @@
[![Build Status](https://img.shields.io/travis/retailcrm/api-client-go/master.svg?style=flat-square)](https://travis-ci.org/retailcrm/api-client-go)
[![Covarage](https://img.shields.io/codecov/c/gh/retailcrm/api-client-go/master.svg?style=flat-square)](https://codecov.io/gh/retailcrm/api-client-go)
[![GitHub release](https://img.shields.io/github/release/retailcrm/api-client-go.svg?style=flat-square)](https://github.com/retailcrm/api-client-go/releases)
[![GoLang version](https://img.shields.io/badge/go->=1.8-blue?style=flat-square)](https://golang.org/dl/)
[![Godoc reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/retailcrm/api-client-go)
[![Build Status](https://github.com/retailcrm/api-client-go/workflows/ci/badge.svg)](https://github.com/retailcrm/api-client-go/actions)
[![Covarage](https://img.shields.io/codecov/c/gh/retailcrm/api-client-go/master.svg?logo=codecov&logoColor=white)](https://codecov.io/gh/retailcrm/api-client-go)
[![GitHub release](https://img.shields.io/github/release/retailcrm/api-client-go.svg?logo=github&logoColor=white)](https://github.com/retailcrm/api-client-go/releases)
[![Go Report Card](https://goreportcard.com/badge/github.com/retailcrm/api-client-go)](https://goreportcard.com/report/github.com/retailcrm/api-client-go)
[![GoLang version](https://img.shields.io/badge/go->=1.8-blue)](https://golang.org/dl/)
[![pkg.go.dev](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/retailcrm/api-client-go)
# retailCRM API Go client
# RetailCRM API Go client
This is golang retailCRM API client.
This is golang RetailCRM API client.
## Install
## Installation
```bash
go get -x github.com/retailcrm/api-client-go
go get -u github.com/retailcrm/api-client-go/v2
```
## Usage
```golang
Example:
```go
package main
import (
"fmt"
"net/http"
"log"
"github.com/retailcrm/api-client-go/v5"
"github.com/retailcrm/api-client-go/v2"
)
func main() {
var client = v5.New("https://demo.retailcrm.pro", "09jIJ09j0JKhgyfvyuUIKhiugF")
var client = retailcrm.New("https://demo.retailcrm.pro", "09jIJ09j0JKhgyfvyuUIKhiugF")
data, status, err := client.Orders(v5.OrdersRequest{
Filter: v5.OrdersFilter{},
data, status, err := client.Orders(retailcrm.OrdersRequest{
Filter: retailcrm.OrdersFilter{},
Limit: 20,
Page: 1,
},)
})
if err != nil {
fmt.Printf("%v", err.Error())
}
if apiErr, ok := retailcrm.AsAPIError(err); ok {
log.Fatalf("http status: %d, %s", status, apiErr.String())
}
if status >= http.StatusBadRequest {
fmt.Printf("%v", err.ApiError())
log.Fatalf("http status: %d, error: %s", status, err)
}
for _, value := range data.Orders {
fmt.Printf("%v\n", value.Email)
log.Printf("%v\n", value.Email)
}
fmt.Println(data.Orders[1].FirstName)
log.Println(data.Orders[1].FirstName)
idata, status, err := c.InventoriesUpload(
[]InventoryUpload{
inventories, status, err := client.InventoriesUpload([]retailcrm.InventoryUpload{
{
XMLID: "pTKIKAeghYzX21HTdzFCe1",
Stores: []InventoryUploadStore{
Stores: []retailcrm.InventoryUploadStore{
{
Code: "test-store-v5",
Available: 10,
@ -73,7 +74,7 @@ func main() {
},
{
XMLID: "JQIvcrCtiSpOV3AAfMiQB3",
Stores: []InventoryUploadStore{
Stores: []retailcrm.InventoryUploadStore{
{
Code: "test-store-v5",
Available: 45,
@ -94,18 +95,70 @@ func main() {
},
)
if err != nil {
fmt.Printf("%v", err.Error())
if apiErr, ok := retailcrm.AsAPIError(err); ok {
log.Fatalf("http status: %d, %s", status, apiErr.String())
}
log.Fatalf("http status: %d, error: %s", status, err)
}
if status >= http.StatusBadRequest {
fmt.Printf("%v", err.ApiError())
}
fmt.Println(idata.processedOffersCount)
log.Println(inventories.ProcessedOffersCount)
}
```
## Documentation
You can use different error types and `retailcrm.AsAPIError` to process client errors. Example:
* [English](http://www.retailcrm.pro/docs/Developers/Index)
* [Russian](http://www.retailcrm.ru/docs/Developers/Index)
```go
package retailcrm
import (
"errors"
"log"
"os"
"strings"
"github.com/retailcrm/api-client-go/v2"
)
func main() {
var client = retailcrm.New("https://demo.retailcrm.pro", "09jIJ09j0JKhgyfvyuUIKhiugF")
resp, status, err := client.APICredentials()
if err != nil {
apiErr, ok := retailcrm.AsAPIError(err)
if !ok {
log.Fatalf("http status: %d, error: %s", status, err)
}
switch {
case errors.Is(apiErr, retailcrm.ErrMissingCredentials):
log.Fatalln("No API key provided.")
case errors.Is(apiErr, retailcrm.ErrInvalidCredentials):
log.Fatalln("Invalid API key.")
case errors.Is(apiErr, retailcrm.ErrAccessDenied):
log.Fatalln("Access denied. Please check that the provided key has access to the credentials info.")
case errors.Is(apiErr, retailcrm.ErrAccountDoesNotExist):
log.Fatalln("There is no RetailCRM at the provided URL.")
case errors.Is(apiErr, retailcrm.ErrMissingParameter):
// retailcrm.APIError in this case will always contain "Name" key in the errors list with the parameter name.
log.Fatalln("This parameter should be present:", apiErr.Errors()["Name"])
case errors.Is(apiErr, retailcrm.ErrValidation):
log.Println("Validation errors from the API:")
for name, value := range apiErr.Errors() {
log.Printf(" - %s: %s\n", name, value)
}
os.Exit(1)
case errors.Is(apiErr, retailcrm.ErrGeneric):
log.Fatalf("failure from the API. %s", apiErr.String())
}
}
log.Println("Available scopes:", strings.Join(resp.Scopes, ", "))
}
```
## Upgrading
Please check the [UPGRADING.md](UPGRADING.md) to learn how to upgrade to the new version.

70
UPGRADING.md Normal file
View file

@ -0,0 +1,70 @@
# Upgrading to the v2
### Install the new version
```bash
go get -u github.com/retailcrm/api-client-go/v2
```
### Update all imports
Before:
```go
package main
import v5 "github.com/retailcrm/api-client-go/v5"
```
After:
```go
package main
import "github.com/retailcrm/api-client-go/v2"
```
You can use package alias `v5` to skip the second step.
### Replace package name for all imported symbols
Before:
```go
package main
import v5 "github.com/retailcrm/api-client-go/v5"
func main() {
client := v5.New("https://test.retailcrm.pro", "key")
data, status, err := client.Orders(v5.OrdersRequest{
Filter: v5.OrdersFilter{
City: "Moscow",
},
Page: 1,
})
...
}
```
After:
```go
package main
import "github.com/retailcrm/api-client-go/v2"
func main() {
client := retailcrm.New("https://test.retailcrm.pro", "key")
data, status, err := client.Orders(retailcrm.OrdersRequest{
Filter: retailcrm.OrdersFilter{
City: "Moscow",
},
Page: 1,
})
...
}
```
### Upgrade client usages
This major release contains some breaking changes regarding field names and fully redesigned error handling. Use the second example from
the readme to learn how to process errors correctly.

7224
client.go Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

8
enum.go Normal file
View file

@ -0,0 +1,8 @@
package retailcrm
var UserGroupSuperadmins UserGroupType = "superadmins"
var (
NotificationTypeError NotificationType = "api.error"
NotificationTypeInfo NotificationType = "api.info"
)

194
error.go Normal file
View file

@ -0,0 +1,194 @@
package retailcrm
import (
"encoding/json"
"fmt"
"regexp"
"strings"
)
var missingParameterMatcher = regexp.MustCompile(`^Parameter \'([\w\]\[\_\-]+)\' is missing$`)
var (
// ErrRateLimited will be returned if request was rate limited.
ErrRateLimited = NewAPIError("rate limit exceeded")
// ErrMissingCredentials will be returned if no API key was provided to the API.
ErrMissingCredentials = NewAPIError(`apiKey is missing`)
// ErrInvalidCredentials will be returned if provided API key is invalid.
ErrInvalidCredentials = NewAPIError(`wrong "apiKey" value`)
// ErrAccessDenied will be returned in case of "Access denied" error.
ErrAccessDenied = NewAPIError("access denied")
// ErrAccountDoesNotExist will be returned if target system does not exist.
ErrAccountDoesNotExist = NewAPIError("account does not exist")
// ErrValidation will be returned in case of validation errors.
ErrValidation = NewAPIError("validation error")
// ErrMissingParameter will be returned if parameter is missing.
// Underlying error messages list will contain parameter name in the "Name" key.
ErrMissingParameter = NewAPIError("missing parameter")
// ErrGeneric will be returned if error cannot be classified as one of the errors above.
ErrGeneric = NewAPIError("API error")
)
// APIErrorsList struct.
type APIErrorsList map[string]string
// APIError returns when an API error was occurred.
type APIError interface {
error
fmt.Stringer
withWrapped(error) APIError
withErrors(APIErrorsList) APIError
Unwrap() error
Errors() APIErrorsList
}
type apiError struct {
ErrorMsg string `json:"errorMsg,omitempty"`
ErrorsList APIErrorsList `json:"errors,omitempty"`
wrapped error
}
// CreateAPIError from the provided response data. Different error types will be returned depending on the response,
// all of them can be matched using errors.Is. APi errors will always implement APIError interface.
func CreateAPIError(dataResponse []byte) error {
a := &apiError{}
if len(dataResponse) > 0 && dataResponse[0] == '<' {
return ErrAccountDoesNotExist
}
if err := json.Unmarshal(dataResponse, &a); err != nil {
return err
}
var found APIError
switch a.ErrorMsg {
case `"apiKey" is missing.`:
found = ErrMissingCredentials
case `Wrong "apiKey" value.`:
found = ErrInvalidCredentials
case "Access denied.":
found = ErrAccessDenied
case "Account does not exist.":
found = ErrAccountDoesNotExist
case "Errors in the entity format":
fallthrough
case "Validation error":
found = ErrValidation
default:
if param, ok := asMissingParameterErr(a.ErrorMsg); ok {
return a.withWrapped(ErrMissingParameter).withErrors(APIErrorsList{"Name": param})
}
found = ErrGeneric
}
result := NewAPIError(a.ErrorMsg).withWrapped(found)
if len(a.ErrorsList) > 0 {
return result.withErrors(a.ErrorsList)
}
return result
}
// CreateGenericAPIError for the situations when API response cannot be processed, but response was actually received.
func CreateGenericAPIError(message string) APIError {
return NewAPIError(message).withWrapped(ErrGeneric)
}
// NewAPIError returns API error with the provided message.
func NewAPIError(message string) APIError {
return &apiError{ErrorMsg: message}
}
// AsAPIError returns APIError and true if provided error is an APIError or contains wrapped APIError.
// Returns (nil, false) otherwise.
func AsAPIError(err error) (APIError, bool) {
apiErr := unwrapAPIError(err)
return apiErr, apiErr != nil
}
func unwrapAPIError(err error) APIError {
if err == nil {
return nil
}
if apiErr, ok := err.(APIError); ok { // nolint:errorlint
return apiErr
}
wrapper, ok := err.(interface { // nolint:errorlint
Unwrap() error
})
if ok {
return unwrapAPIError(wrapper.Unwrap())
}
return nil
}
// asMissingParameterErr returns true if "Parameter 'name' is missing" error message is provided.
func asMissingParameterErr(message string) (string, bool) {
matches := missingParameterMatcher.FindAllStringSubmatch(message, -1)
if len(matches) == 1 && len(matches[0]) == 2 {
return matches[0][1], true
}
return "", false
}
// Error returns errorMsg field from the response.
func (e *apiError) Error() string {
return e.ErrorMsg
}
// Unwrap returns wrapped error. It is usually one of the predefined types like ErrGeneric or ErrValidation.
// It can be used directly, but it's main purpose is to make errors matchable via errors.Is call.
func (e *apiError) Unwrap() error {
return e.wrapped
}
// Errors returns errors field from the response.
func (e *apiError) Errors() APIErrorsList {
return e.ErrorsList
}
// String returns string representation of an APIError.
func (e *apiError) String() string {
var sb strings.Builder
sb.Grow(256) // nolint:gomnd
sb.WriteString(fmt.Sprintf(`errorMsg: "%s"`, e.Error()))
if len(e.Errors()) > 0 {
i := 0
useIndex := true
errorList := make([]string, len(e.Errors()))
for index, errText := range e.Errors() {
if i == 0 && index == "0" {
useIndex = false
}
if useIndex {
errorList[i] = fmt.Sprintf(`%s: "%s"`, index, errText)
} else {
errorList[i] = errText
}
i++
}
sb.WriteString(", errors: [" + strings.Join(errorList, ", ") + "]")
}
return sb.String()
}
// withWrapped is a wrapped setter.
func (e *apiError) withWrapped(err error) APIError {
e.wrapped = err
return e
}
// withErrors is an ErrorsList setter.
func (e *apiError) withErrors(m APIErrorsList) APIError {
e.ErrorsList = m
return e
}

171
error_test.go Normal file
View file

@ -0,0 +1,171 @@
package retailcrm
import (
"testing"
"github.com/stretchr/testify/suite"
)
type ErrorTest struct {
suite.Suite
}
func TestError(t *testing.T) {
suite.Run(t, new(ErrorTest))
}
func (t *ErrorTest) TestFailure_ApiErrorsSlice() {
b := []byte(`{"success": false,
"errorMsg": "Failed to activate module",
"errors": [
"Your account has insufficient funds to activate integration module",
"Test error"
]}`)
expected := APIErrorsList{
"0": "Your account has insufficient funds to activate integration module",
"1": "Test error",
}
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().ErrorIs(e, ErrGeneric)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Assert().Equal(expected, apiErr.Errors())
}
func (t *ErrorTest) TestFailure_ApiErrorsMap() {
b := []byte(`{"success": false,
"errorMsg": "Failed to activate module",
"errors": {"id": "ID must be an integer", "test": "Test error"}}`,
)
expected := APIErrorsList{
"id": "ID must be an integer",
"test": "Test error",
}
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().ErrorIs(e, ErrGeneric)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Assert().Equal(expected, apiErr.Errors())
}
func (t *ErrorTest) TestFailure_APIKeyMissing() {
b := []byte(`{"success": false,
"errorMsg": "\"apiKey\" is missing."}`,
)
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Require().ErrorIs(e, ErrMissingCredentials)
}
func (t *ErrorTest) TestFailure_APIKeyWrong() {
b := []byte(`{"success": false,
"errorMsg": "Wrong \"apiKey\" value."}`,
)
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Require().ErrorIs(e, ErrInvalidCredentials)
}
func (t *ErrorTest) TestFailure_AccessDenied() {
b := []byte(`{"success": false,
"errorMsg": "Access denied."}`,
)
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Require().ErrorIs(e, ErrAccessDenied)
}
func (t *ErrorTest) TestFailure_AccountDoesNotExist() {
b := []byte(`{"success": false,
"errorMsg": "Account does not exist."}`,
)
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Require().ErrorIs(e, ErrAccountDoesNotExist)
}
func (t *ErrorTest) TestFailure_Validation() {
b := []byte(`{"success": false,
"errorMsg": "Errors in the entity format",
"errors": {"name": "name must be provided"}}`,
)
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Require().ErrorIs(e, ErrValidation)
t.Assert().Equal("name must be provided", apiErr.Errors()["name"])
}
func (t *ErrorTest) TestFailure_Validation2() {
b := []byte(`{"success": false,
"errorMsg": "Validation error",
"errors": {"name": "name must be provided"}}`,
)
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Require().ErrorIs(e, ErrValidation)
t.Assert().Equal("name must be provided", apiErr.Errors()["name"])
t.Assert().Equal("errorMsg: \"Validation error\", errors: [name: \"name must be provided\"]", apiErr.String())
}
func (t *ErrorTest) TestFailure_MissingParameter() {
b := []byte(`{"success": false,
"errorMsg": "Parameter 'item' is missing"}`,
)
e := CreateAPIError(b)
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Require().ErrorIs(e, ErrMissingParameter)
t.Assert().Equal("item", apiErr.Errors()["Name"])
}
func (t *ErrorTest) Test_CreateGenericAPIError() {
e := CreateGenericAPIError("generic error message")
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Assert().ErrorIs(apiErr, ErrGeneric)
t.Assert().Equal("generic error message", e.Error())
}
func (t *ErrorTest) TestFailure_HTML() {
e := CreateAPIError([]byte{'<'})
apiErr, ok := AsAPIError(e)
t.Require().NotNil(apiErr)
t.Require().True(ok)
t.Assert().ErrorIs(apiErr, ErrAccountDoesNotExist)
}

View file

@ -1,75 +0,0 @@
package errs
import (
"encoding/json"
"fmt"
"strconv"
)
// Error returns the string representation of the error and satisfies the error interface.
func (f *Failure) Error() string {
if f != nil && f.runtimeErr != nil {
return f.runtimeErr.Error()
}
return ""
}
// ApiError returns formatted string representation of the API error
func (f *Failure) ApiError() string {
if f != nil && f.apiErr != "" {
return fmt.Sprintf("%+v", f.apiErr)
}
return ""
}
// ApiErrors returns array of formatted strings that represents API errors
func (f *Failure) ApiErrors() map[string]string {
if len(f.apiErrs) > 0 {
return f.apiErrs
}
return nil
}
// SetRuntimeError set runtime error value
func (f *Failure) SetRuntimeError(e error) {
f.runtimeErr = e
}
// SetApiError set api error value
func (f *Failure) SetApiError(e string) {
f.apiErr = e
}
// SetApiErrors set api errors value
func (f *Failure) SetApiErrors(e map[string]string) {
f.apiErrs = e
}
// ErrorsHandler returns map
func ErrorsHandler(errs interface{}) map[string]string {
m := make(map[string]string)
switch errs.(type) {
case map[string]interface{}:
for idx, val := range errs.(map[string]interface{}) {
m[idx] = val.(string)
}
case []interface{}:
for idx, val := range errs.([]interface{}) {
m[strconv.Itoa(idx)] = val.(string)
}
}
return m
}
// ErrorResponse method
func ErrorResponse(data []byte) (FailureResponse, error) {
var resp FailureResponse
err := json.Unmarshal(data, &resp)
return resp, err
}

View file

@ -1,50 +0,0 @@
package errs
import (
"reflect"
"testing"
)
func TestFailure_ApiErrorsSlice(t *testing.T) {
var err = Failure{}
b := []byte(`{"success": false, "errorMsg": "Failed to activate module", "errors": ["Your account has insufficient funds to activate integration module"]}`)
expected := map[string]string{
"0": "Your account has insufficient funds to activate integration module",
}
resp, e := ErrorResponse(b)
err.SetRuntimeError(e)
err.SetApiError(resp.ErrorMsg)
if resp.Errors != nil {
err.SetApiErrors(ErrorsHandler(resp.Errors))
}
eq := reflect.DeepEqual(expected, err.ApiErrors())
if eq != true {
t.Errorf("%+v", eq)
}
}
func TestFailure_ApiErrorsMap(t *testing.T) {
var err = Failure{}
b := []byte(`{"success": false, "errorMsg": "Failed to activate module", "errors": {"id": "ID must be an integer"}}`)
expected := map[string]string{
"id": "ID must be an integer",
}
resp, e := ErrorResponse(b)
err.SetRuntimeError(e)
err.SetApiError(resp.ErrorMsg)
if resp.Errors != nil {
err.SetApiErrors(ErrorsHandler(resp.Errors))
}
eq := reflect.DeepEqual(expected, err.ApiErrors())
if eq != true {
t.Errorf("%+v", eq)
}
}

View file

@ -1,8 +0,0 @@
package errs
// Error implements generic error interface
type Error interface {
error
ApiError() string
ApiErrors() map[string]string
}

View file

@ -1,14 +0,0 @@
package errs
// Failure struct implode runtime & api errors
type Failure struct {
runtimeErr error
apiErr string
apiErrs map[string]string
}
// FailureResponse convert json error response into object
type FailureResponse struct {
ErrorMsg string `json:"errorMsg,omitempty"`
Errors interface{} `json:"errors,omitempty"`
}

View file

@ -1,6 +1,6 @@
package v5
package retailcrm
// CustomersFilter type
// CustomersFilter type.
type CustomersFilter struct {
Ids []string `url:"ids,omitempty,brackets"`
ExternalIds []string `url:"externalIds,omitempty,brackets"`
@ -57,7 +57,7 @@ type CustomersFilter struct {
CustomFields map[string]string `url:"customFields,omitempty,brackets"`
}
// CorporateCustomersFilter type
// CorporateCustomersFilter type.
type CorporateCustomersFilter struct {
ContragentName string `url:"contragentName,omitempty"`
ContragentInn string `url:"contragentInn,omitempty"`
@ -99,7 +99,7 @@ type CorporateCustomersFilter struct {
CustomFields map[string]string `url:"customFields,omitempty,brackets"`
}
// CorporateCustomersNotesFilter type
// CorporateCustomersNotesFilter type.
type CorporateCustomersNotesFilter struct {
Ids []string `url:"ids,omitempty,brackets"`
CustomerIds []string `url:"ids,omitempty,brackets"`
@ -110,7 +110,7 @@ type CorporateCustomersNotesFilter struct {
CreatedAtTo string `url:"createdAtTo,omitempty"`
}
// CorporateCustomerAddressesFilter type
// CorporateCustomerAddressesFilter type.
type CorporateCustomerAddressesFilter struct {
Ids []string `url:"ids,omitempty,brackets"`
Name string `url:"name,omitempty"`
@ -118,13 +118,13 @@ type CorporateCustomerAddressesFilter struct {
Region string `url:"region,omitempty"`
}
// IdentifiersPairFilter type
// IdentifiersPairFilter type.
type IdentifiersPairFilter struct {
Ids []string `url:"ids,omitempty,brackets"`
ExternalIds []string `url:"externalIds,omitempty,brackets"`
}
// CustomersHistoryFilter type
// CustomersHistoryFilter type.
type CustomersHistoryFilter struct {
CustomerID int `url:"customerId,omitempty"`
SinceID int `url:"sinceId,omitempty"`
@ -133,7 +133,7 @@ type CustomersHistoryFilter struct {
EndDate string `url:"endDate,omitempty"`
}
// CorporateCustomersHistoryFilter type
// CorporateCustomersHistoryFilter type.
type CorporateCustomersHistoryFilter struct {
CustomerID int `url:"customerId,omitempty"`
SinceID int `url:"sinceId,omitempty"`
@ -143,7 +143,7 @@ type CorporateCustomersHistoryFilter struct {
EndDate string `url:"endDate,omitempty"`
}
// OrdersFilter type
// OrdersFilter type.
type OrdersFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
ExternalIds []string `url:"externalIds,omitempty,brackets"`
@ -199,6 +199,7 @@ type OrdersFilter struct {
PaymentStatuses []string `url:"paymentStatuses,omitempty,brackets"`
PaymentTypes []string `url:"paymentTypes,omitempty,brackets"`
DeliveryTypes []string `url:"deliveryTypes,omitempty,brackets"`
DeliveryServices []string `url:"deliveryServices,omitempty,brackets"`
OrderMethods []string `url:"orderMethods,omitempty,brackets"`
ShipmentStores []string `url:"shipmentStores,omitempty,brackets"`
Couriers []string `url:"couriers,omitempty,brackets"`
@ -207,6 +208,8 @@ type OrdersFilter struct {
Sites []string `url:"sites,omitempty,brackets"`
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
CreatedAtTo string `url:"createdAtTo,omitempty"`
PaidAtFrom string `url:"paidAtFrom,omitempty"`
PaidAtTo string `url:"paidAtTo,omitempty"`
FullPaidAtFrom string `url:"fullPaidAtFrom,omitempty"`
FullPaidAtTo string `url:"fullPaidAtTo,omitempty"`
DeliveryDateFrom string `url:"deliveryDateFrom,omitempty"`
@ -234,7 +237,7 @@ type OrdersFilter struct {
CustomFields map[string]string `url:"customFields,omitempty,brackets"`
}
// OrdersHistoryFilter type
// OrdersHistoryFilter type.
type OrdersHistoryFilter struct {
OrderID int `url:"orderId,omitempty"`
SinceID int `url:"sinceId,omitempty"`
@ -243,7 +246,7 @@ type OrdersHistoryFilter struct {
EndDate string `url:"endDate,omitempty"`
}
// UsersFilter type
// UsersFilter type.
type UsersFilter struct {
Email string `url:"email,omitempty"`
Status string `url:"status,omitempty"`
@ -256,7 +259,7 @@ type UsersFilter struct {
Groups []string `url:"groups,omitempty,brackets"`
}
// TasksFilter type
// TasksFilter type.
type TasksFilter struct {
OrderNumber string `url:"orderNumber,omitempty"`
Status string `url:"status,omitempty"`
@ -268,7 +271,7 @@ type TasksFilter struct {
Performers []int `url:"performers,omitempty,brackets"`
}
// NotesFilter type
// NotesFilter type.
type NotesFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
CustomerIds []int `url:"customerIds,omitempty,brackets"`
@ -279,7 +282,7 @@ type NotesFilter struct {
CreatedAtTo string `url:"createdAtTo,omitempty"`
}
// SegmentsFilter type
// SegmentsFilter type.
type SegmentsFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
Active int `url:"active,omitempty"`
@ -291,7 +294,7 @@ type SegmentsFilter struct {
DateTo string `url:"dateTo,omitempty"`
}
// PacksFilter type
// PacksFilter type.
type PacksFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
Stores []string `url:"stores,omitempty"`
@ -306,7 +309,7 @@ type PacksFilter struct {
DeliveryNoteNumber string `url:"deliveryNoteNumber,omitempty"`
}
// InventoriesFilter type
// InventoriesFilter type.
type InventoriesFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
ProductExternalID string `url:"productExternalId,omitempty"`
@ -319,7 +322,7 @@ type InventoriesFilter struct {
Sites []string `url:"sites,omitempty,brackets"`
}
// ProductsGroupsFilter type
// ProductsGroupsFilter type.
type ProductsGroupsFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
Sites []string `url:"sites,omitempty,brackets"`
@ -327,7 +330,7 @@ type ProductsGroupsFilter struct {
ParentGroupID string `url:"parentGroupId,omitempty"`
}
// ProductsFilter type
// ProductsFilter type.
type ProductsFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
OfferIds []int `url:"offerIds,omitempty,brackets"`
@ -349,20 +352,21 @@ type ProductsFilter struct {
ExternalID string `url:"externalId,omitempty"`
Manufacturer string `url:"manufacturer,omitempty"`
URL string `url:"url,omitempty"`
URLLike string `url:"urlLike,omitempty"`
PriceType string `url:"priceType,omitempty"`
OfferExternalID string `url:"offerExternalId,omitempty"`
Sites []string `url:"sites,omitempty,brackets"`
Properties map[string]string `url:"properties,omitempty,brackets"`
}
// ProductsPropertiesFilter type
// ProductsPropertiesFilter type.
type ProductsPropertiesFilter struct {
Code string `url:"code,omitempty"`
Name string `url:"name,omitempty"`
Sites []string `url:"sites,omitempty,brackets"`
}
// ShipmentFilter type
// ShipmentFilter type.
type ShipmentFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
ExternalID string `url:"externalId,omitempty"`
@ -375,27 +379,27 @@ type ShipmentFilter struct {
Statuses []string `url:"statuses,omitempty,brackets"`
}
// CostsFilter type
// CostsFilter type.
type CostsFilter struct {
MinSumm string `url:"minSumm,omitempty"`
MaxSumm string `url:"maxSumm,omitempty"`
MinSumm int `url:"minSumm,omitempty"`
MaxSumm int `url:"maxSumm,omitempty"`
OrderNumber string `url:"orderNumber,omitempty"`
Comment string `url:"orderNumber,omitempty"`
Ids []string `url:"ids,omitempty,brackets"`
IDs []int `url:"ids,omitempty,brackets"`
Sites []string `url:"sites,omitempty,brackets"`
CreatedBy []string `url:"createdBy,omitempty,brackets"`
CreatedBy []int `url:"createdBy,omitempty,brackets"`
CostGroups []string `url:"costGroups,omitempty,brackets"`
CostItems []string `url:"costItems,omitempty,brackets"`
Users []string `url:"users,omitempty,brackets"`
Users []int `url:"users,omitempty,brackets"`
DateFrom string `url:"dateFrom,omitempty"`
DateTo string `url:"dateTo,omitempty"`
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
CreatedAtTo string `url:"createdAtTo,omitempty"`
OrderIds []string `url:"orderIds,omitempty,brackets"`
OrderExternalIds []string `url:"orderIds,omitempty,brackets"`
OrderIDs []int `url:"orderIds,omitempty,brackets"`
OrderExternalIDs []string `url:"orderExternalIds,omitempty,brackets"`
}
// FilesFilter type
// FilesFilter type.
type FilesFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
OrderIds []int `url:"orderIds,omitempty,brackets"`
@ -412,7 +416,7 @@ type FilesFilter struct {
Sites []string `url:"sites,omitempty,brackets"`
}
// CustomFieldsFilter type
// CustomFieldsFilter type.
type CustomFieldsFilter struct {
Name string `url:"name,omitempty"`
Code string `url:"code,omitempty"`
@ -422,8 +426,72 @@ type CustomFieldsFilter struct {
DisplayArea string `url:"displayArea,omitempty"`
}
// CustomDictionariesFilter type
// CustomDictionariesFilter type.
type CustomDictionariesFilter struct {
Name string `url:"name,omitempty"`
Code string `url:"code,omitempty"`
}
// BonusOperationsFilter type.
type BonusOperationsFilter struct {
Loyalties []int `url:"loyalties,omitempty,brackets"`
}
type AccountBonusOperationsFilter struct {
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
CreatedAtTo string `url:"createdAtTo,omitempty"`
}
type LoyaltyBonusAPIFilterType struct {
Date string `url:"date,omitempty"`
}
type LoyaltyAccountAPIFilter struct {
ID string `url:"id,omitempty"`
Status string `url:"status,,omitempty"`
Customer string `url:"customer,omitempty"`
MinOrderSum string `url:"minOrdersSum,omitempty"`
MaxOrderSum string `url:"maxOrdersSum,omitempty"`
MinAmount string `url:"minAmount,omitempty"`
MaxAmount string `url:"maxAmount,omitempty"`
PhoneNumber string `url:"phoneNumber,omitempty"`
CardNumber string `url:"cardNumber,omitempty"`
Ids []int `url:"ids,omitempty,brackets"`
Loyalties []int `url:"loyalties,omitempty,brackets"`
Sites []string `url:"sites,omitempty,brackets"`
Level int `url:"level,omitempty"`
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
CreatedAtTo string `url:"createdAtTo,omitempty"`
BurnDateFrom string `url:"burnDateFrom,omitempty"`
BurnDateTo string `url:"burnDateTo,omitempty"`
CustomFields []string `url:"customFields,omitempty,brackets"`
CustomerID string `url:"customerId,omitempty"`
CustomerExternalID string `url:"customerExternalId,omitempty"`
}
type LoyaltyAPIFilter struct {
Active *int `url:"active,omitempty"`
Blocked *int `url:"blocked,omitempty"`
Ids []int `url:"ids,omitempty,brackets"`
Sites []string `url:"sites,omitempty,brackets"`
}
type OffersFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
Active *int `url:"active,omitempty"`
}
type SiteFilter struct {
// SiteBy contains information about what is betrayed site id or site code.
// id|code, default is code.
SiteBy string `url:"siteBy,omitempty"`
}
type GetCartFilter struct {
// SiteBy contains information about what is betrayed site id or site code.
// id|code, default is code.
SiteBy string `url:"siteBy,omitempty"`
// By contains information about what is betrayed: customer id or customer externalId.
// id|externalId, default is externalId.
By string `url:"by,omitempty"`
}

10
go.mod Normal file
View file

@ -0,0 +1,10 @@
module github.com/retailcrm/api-client-go/v2
go 1.13
require (
github.com/google/go-querystring v1.1.0
github.com/joho/godotenv v1.3.0
github.com/stretchr/testify v1.7.0
gopkg.in/h2non/gock.v1 v1.1.2
)

25
go.sum Normal file
View file

@ -0,0 +1,25 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -1,15 +0,0 @@
#!/usr/bin/env bash
set -e
export DEVELOPER_NODE=1
export RETAILCRM_URL=https://test.retailcrm.pro
export RETAILCRM_KEY=key
echo "" > coverage.txt
for d in $(go list ./... | grep -v vendor); do
go test -race -coverprofile=profile.out -covermode=atomic "$d"
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
rm profile.out
fi
done

25
log.go Normal file
View file

@ -0,0 +1,25 @@
package retailcrm
// BasicLogger provides basic functionality for logging.
type BasicLogger interface {
Printf(string, ...interface{})
}
// DebugLogger can be used to easily wrap any logger with Debugf method into the BasicLogger instance.
type DebugLogger interface {
Debugf(string, ...interface{})
}
type debugLoggerAdapter struct {
logger DebugLogger
}
// DebugLoggerAdapter returns BasicLogger that calls underlying DebugLogger.Debugf.
func DebugLoggerAdapter(logger DebugLogger) BasicLogger {
return &debugLoggerAdapter{logger: logger}
}
// Printf data in the log using DebugLogger.Debugf.
func (l *debugLoggerAdapter) Printf(format string, v ...interface{}) {
l.logger.Debugf(format, v...)
}

24
log_test.go Normal file
View file

@ -0,0 +1,24 @@
package retailcrm
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
type wrappedLogger struct {
lastMessage string
}
func (w *wrappedLogger) Debugf(msg string, v ...interface{}) {
w.lastMessage = fmt.Sprintf(msg, v...)
}
func TestDebugLoggerAdapter_Printf(t *testing.T) {
wrapped := &wrappedLogger{}
logger := DebugLoggerAdapter(wrapped)
logger.Printf("Test message #%d", 1)
assert.Equal(t, "Test message #1", wrapped.lastMessage)
}

160
marshaling.go Normal file
View file

@ -0,0 +1,160 @@
package retailcrm
import (
"encoding/json"
"fmt"
"strconv"
)
func (t Tag) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Name)
}
func (a *APIErrorsList) UnmarshalJSON(data []byte) error {
var i interface{}
var m APIErrorsList
if err := json.Unmarshal(data, &i); err != nil {
return err
}
switch e := i.(type) {
case map[string]interface{}:
m = make(APIErrorsList, len(e))
for idx, val := range e {
m[idx] = fmt.Sprint(val)
}
case []interface{}:
m = make(APIErrorsList, len(e))
for idx, val := range e {
m[strconv.Itoa(idx)] = fmt.Sprint(val)
}
}
*a = m
return nil
}
func (l *StringMap) UnmarshalJSON(data []byte) error {
var i interface{}
var m StringMap
if err := json.Unmarshal(data, &i); err != nil {
return err
}
switch e := i.(type) {
case map[string]interface{}:
m = make(StringMap, len(e))
for idx, val := range e {
m[idx] = fmt.Sprint(val)
}
case []interface{}:
m = make(StringMap, len(e))
for idx, val := range e {
m[strconv.Itoa(idx)] = fmt.Sprint(val)
}
}
*l = m
return nil
}
func (l *CustomFieldMap) UnmarshalJSON(data []byte) error {
var i interface{}
var items CustomFieldMap
if err := json.Unmarshal(data, &i); err != nil {
return err
}
switch e := i.(type) {
case map[string]interface{}:
items = make(CustomFieldMap, len(e))
for idx, val := range e {
items[idx] = val
}
case []interface{}:
items = make(CustomFieldMap, len(e))
for idx, val := range e {
items[strconv.Itoa(idx)] = val
}
}
*l = items
return nil
}
func (p *OrderPayments) UnmarshalJSON(data []byte) error {
var i interface{}
var m OrderPayments
if err := json.Unmarshal(data, &i); err != nil {
return err
}
switch e := i.(type) {
case map[string]interface{}:
m = make(OrderPayments, len(e))
for idx, val := range e {
var res OrderPayment
err := unmarshalMap(val.(map[string]interface{}), &res)
if err != nil {
return err
}
m[idx] = res
}
case []interface{}:
m = make(OrderPayments, len(e))
for idx, val := range e {
var res OrderPayment
err := unmarshalMap(val.(map[string]interface{}), &res)
if err != nil {
return err
}
m[strconv.Itoa(idx)] = res
}
}
*p = m
return nil
}
func (p *Properties) UnmarshalJSON(data []byte) error {
var i interface{}
var m Properties
if err := json.Unmarshal(data, &i); err != nil {
return err
}
switch e := i.(type) {
case map[string]interface{}:
m = make(Properties, len(e))
for idx, val := range e {
var res Property
err := unmarshalMap(val.(map[string]interface{}), &res)
if err != nil {
return err
}
m[idx] = res
}
case []interface{}:
m = make(Properties, len(e))
for idx, val := range e {
var res Property
err := unmarshalMap(val.(map[string]interface{}), &res)
if err != nil {
return err
}
m[strconv.Itoa(idx)] = res
}
}
*p = m
return nil
}
func unmarshalMap(m map[string]interface{}, v interface{}) (err error) {
var data []byte
data, err = json.Marshal(m)
if err != nil {
return err
}
return json.Unmarshal(data, v)
}

102
marshaling_test.go Normal file
View file

@ -0,0 +1,102 @@
package retailcrm
import (
"encoding/json"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTag_MarshalJSON(t *testing.T) {
tags := []Tag{
{"first", "#3e89b6", false},
{"second", "#ffa654", false},
}
names := []byte(`["first","second"]`)
str, err := json.Marshal(tags)
if err != nil {
t.Errorf("%v", err.Error())
}
if !reflect.DeepEqual(str, names) {
t.Errorf("Marshaled: %#v\nExpected: %#v\n", str, names)
}
}
func TestAPIErrorsList_UnmarshalJSON(t *testing.T) {
var list APIErrorsList
require.NoError(t, json.Unmarshal([]byte(`["first", "second"]`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["0"], "first")
assert.Equal(t, list["1"], "second")
require.NoError(t, json.Unmarshal([]byte(`{"a": "first", "b": "second"}`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["a"], "first")
assert.Equal(t, list["b"], "second")
require.NoError(t, json.Unmarshal([]byte(`[]`), &list))
assert.Len(t, list, 0)
}
func TestCustomFieldsList_UnmarshalJSON(t *testing.T) {
var list CustomFieldMap
require.NoError(t, json.Unmarshal([]byte(`["first", "second"]`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["0"], "first")
assert.Equal(t, list["1"], "second")
require.NoError(t, json.Unmarshal([]byte(`{"a": "first", "b": "second"}`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["a"], "first")
assert.Equal(t, list["b"], "second")
require.NoError(t, json.Unmarshal([]byte(`{"a": ["first", "second"], "b": "second"}`), &list))
assert.Len(t, list, 2)
assert.Len(t, list["a"].([]interface{}), 2)
assert.Equal(t, list["a"].([]interface{})[0], "first")
assert.Equal(t, list["a"].([]interface{})[1], "second")
assert.Equal(t, list["b"], "second")
require.NoError(t, json.Unmarshal([]byte(`[]`), &list))
assert.Len(t, list, 0)
}
func TestOrderPayments_UnmarshalJSON(t *testing.T) {
var list OrderPayments
require.NoError(t, json.Unmarshal([]byte(`[{"id": 1}, {"id": 2}]`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["0"], OrderPayment{ID: 1})
assert.Equal(t, list["1"], OrderPayment{ID: 2})
require.NoError(t, json.Unmarshal([]byte(`{"a": {"id": 1}, "b": {"id": 2}}`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["a"], OrderPayment{ID: 1})
assert.Equal(t, list["b"], OrderPayment{ID: 2})
require.NoError(t, json.Unmarshal([]byte(`[]`), &list))
assert.Len(t, list, 0)
}
func TestProperties_UnmarshalJSON(t *testing.T) {
var list Properties
require.NoError(t, json.Unmarshal([]byte(`[{"code": "first"}, {"code": "second"}]`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["0"], Property{Code: "first"})
assert.Equal(t, list["1"], Property{Code: "second"})
require.NoError(t, json.Unmarshal([]byte(`{"a": {"code": "first"}, "b": {"code": "second"}}`), &list))
assert.Len(t, list, 2)
assert.Equal(t, list["a"], Property{Code: "first"})
assert.Equal(t, list["b"], Property{Code: "second"})
require.NoError(t, json.Unmarshal([]byte(`[]`), &list))
assert.Len(t, list, 0)
}

View file

@ -1,33 +1,39 @@
package v5
package retailcrm
// CustomerRequest type
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
// CustomerRequest type.
type CustomerRequest struct {
By string `url:"by,omitempty"`
Site string `url:"site,omitempty"`
}
// CustomersRequest type
// CustomersRequest type.
type CustomersRequest struct {
Filter CustomersFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// CorporateCustomersRequest type
// CorporateCustomersRequest type.
type CorporateCustomersRequest struct {
Filter CorporateCustomersFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// CorporateCustomersNotesRequest type
// CorporateCustomersNotesRequest type.
type CorporateCustomersNotesRequest struct {
Filter CorporateCustomersNotesFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// CorporateCustomerAddressesRequest type
// CorporateCustomerAddressesRequest type.
type CorporateCustomerAddressesRequest struct {
Filter CorporateCustomerAddressesFilter `url:"filter,omitempty"`
By string `url:"by,omitempty"`
@ -36,7 +42,7 @@ type CorporateCustomerAddressesRequest struct {
Page int `url:"page,omitempty"`
}
// IdentifiersPairRequest type
// IdentifiersPairRequest type.
type IdentifiersPairRequest struct {
Filter IdentifiersPairFilter `url:"filter,omitempty"`
By string `url:"by,omitempty"`
@ -45,173 +51,282 @@ type IdentifiersPairRequest struct {
Page int `url:"page,omitempty"`
}
// CustomersUploadRequest type
// CustomersUploadRequest type.
type CustomersUploadRequest struct {
Customers []Customer `url:"customers,omitempty,brackets"`
Site string `url:"site,omitempty"`
}
// CustomersHistoryRequest type
// CustomersHistoryRequest type.
type CustomersHistoryRequest struct {
Filter CustomersHistoryFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// CorporateCustomersHistoryRequest type
// CorporateCustomersHistoryRequest type.
type CorporateCustomersHistoryRequest struct {
Filter CorporateCustomersHistoryFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// OrderRequest type
// OrderRequest type.
type OrderRequest struct {
By string `url:"by,omitempty"`
Site string `url:"site,omitempty"`
}
// OrdersRequest type
// OrdersRequest type.
type OrdersRequest struct {
Filter OrdersFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// OrdersStatusesRequest type
// OrdersStatusesRequest type.
type OrdersStatusesRequest struct {
IDs []int `url:"ids,omitempty,brackets"`
ExternalIDs []string `url:"externalIds,omitempty,brackets"`
}
// OrdersUploadRequest type
// OrdersUploadRequest type.
type OrdersUploadRequest struct {
Orders []Order `url:"orders,omitempty,brackets"`
Site string `url:"site,omitempty"`
}
// OrdersHistoryRequest type
// OrdersHistoryRequest type.
type OrdersHistoryRequest struct {
Filter OrdersHistoryFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// PacksRequest type
// PacksRequest type.
type PacksRequest struct {
Filter PacksFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// PacksHistoryRequest type
// PacksHistoryRequest type.
type PacksHistoryRequest struct {
Filter OrdersHistoryFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// UsersRequest type
// UsersRequest type.
type UsersRequest struct {
Filter UsersFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// UserGroupsRequest type
// UserGroupsRequest type.
type UserGroupsRequest struct {
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// TasksRequest type
// TasksRequest type.
type TasksRequest struct {
Filter TasksFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// NotesRequest type
// NotesRequest type.
type NotesRequest struct {
Filter NotesFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// SegmentsRequest type
// SegmentsRequest type.
type SegmentsRequest struct {
Filter SegmentsFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// InventoriesRequest type
// InventoriesRequest type.
type InventoriesRequest struct {
Filter InventoriesFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// ProductsGroupsRequest type
// ProductsGroupsRequest type.
type ProductsGroupsRequest struct {
Filter ProductsGroupsFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// ProductsRequest type
// ProductsRequest type.
type ProductsRequest struct {
Filter ProductsFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// ProductsPropertiesRequest type
// ProductsPropertiesRequest type.
type ProductsPropertiesRequest struct {
Filter ProductsPropertiesFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// DeliveryTrackingRequest type
// DeliveryTrackingRequest type.
type DeliveryTrackingRequest struct {
DeliveryID string `url:"deliveryId,omitempty"`
TrackNumber string `url:"trackNumber,omitempty"`
History []DeliveryHistoryRecord `url:"history,omitempty,brackets"`
ExtraData map[string]string `url:"extraData,omitempty,brackets"`
DeliveryID string `json:"deliveryId,omitempty"`
TrackNumber string `json:"trackNumber,omitempty"`
History []DeliveryHistoryRecord `json:"history,omitempty"`
ExtraData map[string]string `json:"extraData,omitempty"`
}
// DeliveryShipmentsRequest type
// DeliveryShipmentsRequest type.
type DeliveryShipmentsRequest struct {
Filter ShipmentFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// CostsRequest type
// ClearCartRequest type.
type ClearCartRequest struct {
ClearedAt string `url:"clearedAt,omitempty"`
Customer CartCustomer `url:"customer,omitempty"`
Order ClearCartOrder `url:"order,omitempty"`
}
// SetCartRequest type.
type SetCartRequest struct {
ExternalID string `url:"externalId,omitempty"`
DroppedAt string `url:"droppedAt,omitempty"`
Link string `url:"link,omitempty"`
Customer CartCustomer `url:"customer,omitempty"`
Items []SetCartItem `url:"items,omitempty"`
}
// CostsRequest type.
type CostsRequest struct {
Filter CostsFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// FilesRequest type
// FilesRequest type.
type FilesRequest struct {
Filter FilesFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// CustomFieldsRequest type
// CustomFieldsRequest type.
type CustomFieldsRequest struct {
Filter CustomFieldsFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// CustomDictionariesRequest type
// CustomDictionariesRequest type.
type CustomDictionariesRequest struct {
Filter CustomDictionariesFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
// ConnectRequest contains information about the system connection that is requested to be created.
type ConnectRequest struct {
// Token is used to verify the request. Do not use directly; use Verify instead.
Token string `json:"token"`
// APIKey that was generated for the module.
APIKey string `json:"apiKey"`
// URL of the system. Do not use directly; use SystemURL instead.
URL string `json:"systemUrl"`
}
// BonusOperationsRequest type.
type BonusOperationsRequest struct {
Filter BonusOperationsFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Cursor string `url:"cursor,omitempty"`
}
// AccountBonusOperationsRequest type.
type AccountBonusOperationsRequest struct {
Filter AccountBonusOperationsFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
type LoyaltyBonusCreditRequest struct {
Amount float64 `url:"amount"`
ActivationDate string `url:"activationDate,omitempty"`
ExpiredDate string `url:"expireDate,omitempty"`
Comment string `url:"comment,omitempty"`
}
type LoyaltyBonusStatusDetailsRequest struct {
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
Filter LoyaltyBonusAPIFilterType `url:"filter,omitempty"`
}
type LoyaltyAccountsRequest struct {
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
Filter LoyaltyAccountAPIFilter `url:"filter,omitempty"`
}
type LoyaltyCalculateRequest struct {
Site string
Order Order
Bonuses float32
}
type LoyaltiesRequest struct {
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
Filter LoyaltyAPIFilter `url:"filter,omitempty"`
}
type NotificationsSendRequest struct {
UserGroups []UserGroupType `json:"userGroups,omitempty"`
Type NotificationType `json:"type"`
Message string `json:"message"`
UserIDs []string `json:"userIds,omitempty"`
}
type EditMGChannelTemplateRequest struct {
Templates []MGChannelTemplate `json:"templates"`
Removed []int `json:"removed"`
}
// SystemURL returns system URL from the connection request without trailing slash.
func (r ConnectRequest) SystemURL() string {
if r.URL == "" {
return ""
}
if r.URL[len(r.URL)-1:] == "/" {
return r.URL[:len(r.URL)-1]
}
return r.URL
}
// Verify returns true if connection request is legitimate. Application secret should be provided to this method.
func (r ConnectRequest) Verify(secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
if _, err := mac.Write([]byte(r.APIKey)); err != nil {
panic(err)
}
return hmac.Equal([]byte(r.Token), []byte(hex.EncodeToString(mac.Sum(nil))))
}
type OffersRequest struct {
OffersFilter `url:"filter,omitempty"`
}

43
request_test.go Normal file
View file

@ -0,0 +1,43 @@
package retailcrm
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"testing"
"github.com/stretchr/testify/suite"
)
type ConnectRequestTest struct {
suite.Suite
}
func TestConnectRequest(t *testing.T) {
suite.Run(t, new(ConnectRequestTest))
}
func (t *ConnectRequestTest) Test_SystemURL() {
t.Assert().Equal("", ConnectRequest{}.SystemURL())
t.Assert().Equal("https://test.retailcrm.pro", ConnectRequest{URL: "https://test.retailcrm.pro"}.SystemURL())
t.Assert().Equal("https://test.retailcrm.pro", ConnectRequest{URL: "https://test.retailcrm.pro/"}.SystemURL())
}
func (t *ConnectRequestTest) Test_Verify() {
t.Assert().True(ConnectRequest{
APIKey: "key",
Token: createConnectToken("key", "secret"),
}.Verify("secret"))
t.Assert().False(ConnectRequest{
APIKey: "key",
Token: createConnectToken("key", "secret2"),
}.Verify("secret"))
}
func createConnectToken(apiKey, secret string) string {
mac := hmac.New(sha256.New, []byte(secret))
if _, err := mac.Write([]byte(apiKey)); err != nil {
panic(err)
}
return hex.EncodeToString(mac.Sum(nil))
}

699
response.go Normal file
View file

@ -0,0 +1,699 @@
package retailcrm
// SuccessfulResponse type.
type SuccessfulResponse struct {
Success bool `json:"success"`
}
type CreateLoyaltyAccountResponse struct {
SuccessfulResponse
LoyaltyAccount LoyaltyAccount `json:"loyaltyAccount,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
type EditLoyaltyAccountResponse struct {
SuccessfulResponse
LoyaltyAccount LoyaltyAccount `json:"loyaltyAccount,omitempty"`
}
// CreateResponse type.
type CreateResponse struct {
Success bool `json:"success"`
ID int `json:"id,omitempty"`
}
// OrderCreateResponse type.
type OrderCreateResponse struct {
CreateResponse
Order Order `json:"order,omitempty"`
}
// OperationResponse type.
type OperationResponse struct {
Success bool `json:"success"`
Errors map[string]string `json:"ErrorsList,omitempty"`
}
// VersionResponse return available API versions.
type VersionResponse struct {
Success bool `json:"success,omitempty"`
Versions []string `json:"versions,omitempty"`
}
// CredentialResponse return available API methods.
type CredentialResponse struct {
Success bool `json:"success,omitempty"`
// deprecated
Credentials []string `json:"credentials,omitempty"`
Scopes []string `json:"scopes,omitempty"`
SiteAccess string `json:"siteAccess,omitempty"`
SitesAvailable []string `json:"sitesAvailable,omitempty"`
}
// SystemInfoResponse return system info.
type SystemInfoResponse struct {
Success bool `json:"success,omitempty"`
SystemVersion string `json:"systemVersion,omitempty"`
PublicURL string `json:"publicUrl,omitempty"`
TechnicalURL string `json:"technicalUrl,omitempty"`
}
// CustomerResponse type.
type CustomerResponse struct {
Success bool `json:"success"`
Customer *Customer `json:"customer,omitempty"`
}
// CorporateCustomerResponse type.
type CorporateCustomerResponse struct {
Success bool `json:"success"`
CorporateCustomer *CorporateCustomer `json:"customerCorporate,omitempty"`
}
// CustomersResponse type.
type CustomersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Customers []Customer `json:"customers,omitempty"`
}
// CorporateCustomersResponse type.
type CorporateCustomersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
CustomersCorporate []CorporateCustomer `json:"customersCorporate,omitempty"`
}
// CorporateCustomersNotesResponse type.
type CorporateCustomersNotesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Notes []Note `json:"notes,omitempty"`
}
// CorporateCustomersAddressesResponse type.
type CorporateCustomersAddressesResponse struct {
Success bool `json:"success"`
Addresses []CorporateCustomerAddress `json:"addresses"`
}
// CorporateCustomerCompaniesResponse type.
type CorporateCustomerCompaniesResponse struct {
Success bool `json:"success"`
Companies []Company `json:"companies"`
}
// CorporateCustomerContactsResponse type.
type CorporateCustomerContactsResponse struct {
Success bool `json:"success"`
Contacts []CorporateCustomerContact `json:"contacts"`
}
// CustomerChangeResponse type.
type CustomerChangeResponse struct {
Success bool `json:"success"`
ID int `json:"id,omitempty"`
State string `json:"state,omitempty"`
}
// CorporateCustomerChangeResponse type.
type CorporateCustomerChangeResponse CustomerChangeResponse
// CustomersUploadResponse type.
type CustomersUploadResponse struct {
Success bool `json:"success"`
UploadedCustomers []IdentifiersPair `json:"uploadedCustomers,omitempty"`
FailedCustomers []ExternalID `json:"failedCustomers,omitempty"`
}
// CorporateCustomersUploadResponse type.
type CorporateCustomersUploadResponse CustomersUploadResponse
// CustomersHistoryResponse type.
type CustomersHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []CustomerHistoryRecord `json:"history,omitempty"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// CorporateCustomersHistoryResponse type.
type CorporateCustomersHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []CorporateCustomerHistoryRecord `json:"history,omitempty"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// OrderResponse type.
type OrderResponse struct {
Success bool `json:"success"`
Order *Order `json:"order,omitempty"`
}
// OrdersResponse type.
type OrdersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Orders []Order `json:"orders,omitempty"`
}
// OrdersStatusesResponse type.
type OrdersStatusesResponse struct {
Success bool `json:"success"`
Orders []OrdersStatus `json:"orders"`
}
// OrdersUploadResponse type.
type OrdersUploadResponse struct {
Success bool `json:"success"`
UploadedOrders []IdentifiersPair `json:"uploadedOrders,omitempty"`
FailedOrders []ExternalID `json:"failedOrders,omitempty"`
}
// OrdersHistoryResponse type.
type OrdersHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []OrdersHistoryRecord `json:"history,omitempty"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// PackResponse type.
type PackResponse struct {
Success bool `json:"success"`
Pack *Pack `json:"pack,omitempty"`
}
// PacksResponse type.
type PacksResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Packs []Pack `json:"packs,omitempty"`
}
// PacksHistoryResponse type.
type PacksHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []PacksHistoryRecord `json:"history,omitempty"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// UserResponse type.
type UserResponse struct {
Success bool `json:"success"`
User *User `json:"user,omitempty"`
}
// UsersResponse type.
type UsersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Users []User `json:"users,omitempty"`
}
// UserGroupsResponse type.
type UserGroupsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Groups []UserGroup `json:"groups,omitempty"`
}
// TaskResponse type.
type TaskResponse struct {
Success bool `json:"success"`
Task *Task `json:"task,omitempty"`
}
// TasksResponse type.
type TasksResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Tasks []Task `json:"tasks,omitempty"`
}
// NotesResponse type.
type NotesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Notes []Note `json:"notes,omitempty"`
}
// SegmentsResponse type.
type SegmentsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Segments []Segment `json:"segments,omitempty"`
}
// SettingsResponse type.
type SettingsResponse struct {
Success bool `json:"success"`
Settings Settings `json:"settings,omitempty"`
}
// CountriesResponse type.
type CountriesResponse struct {
Success bool `json:"success"`
CountriesIso []string `json:"countriesIso,omitempty"`
}
// CostGroupsResponse type.
type CostGroupsResponse struct {
Success bool `json:"success"`
CostGroups []CostGroup `json:"costGroups,omitempty"`
}
// CostItemsResponse type.
type CostItemsResponse struct {
Success bool `json:"success"`
CostItems []CostItem `json:"costItems,omitempty"`
}
// CouriersResponse type.
type CouriersResponse struct {
Success bool `json:"success"`
Couriers []Courier `json:"couriers,omitempty"`
}
// DeliveryServiceResponse type.
type DeliveryServiceResponse struct {
Success bool `json:"success"`
DeliveryServices map[string]DeliveryService `json:"deliveryServices,omitempty"`
}
// DeliveryTypesResponse type.
type DeliveryTypesResponse struct {
Success bool `json:"success"`
DeliveryTypes map[string]DeliveryType `json:"deliveryTypes,omitempty"`
}
// LegalEntitiesResponse type.
type LegalEntitiesResponse struct {
Success bool `json:"success"`
LegalEntities []LegalEntity `json:"legalEntities,omitempty"`
}
// OrderMethodsResponse type.
type OrderMethodsResponse struct {
Success bool `json:"success"`
OrderMethods map[string]OrderMethod `json:"orderMethods,omitempty"`
}
// OrderTypesResponse type.
type OrderTypesResponse struct {
Success bool `json:"success"`
OrderTypes map[string]OrderType `json:"orderTypes,omitempty"`
}
// PaymentStatusesResponse type.
type PaymentStatusesResponse struct {
Success bool `json:"success"`
PaymentStatuses map[string]PaymentStatus `json:"paymentStatuses,omitempty"`
}
// PaymentTypesResponse type.
type PaymentTypesResponse struct {
Success bool `json:"success"`
PaymentTypes map[string]PaymentType `json:"paymentTypes,omitempty"`
}
// PriceTypesResponse type.
type PriceTypesResponse struct {
Success bool `json:"success"`
PriceTypes []PriceType `json:"priceTypes,omitempty"`
}
// ProductStatusesResponse type.
type ProductStatusesResponse struct {
Success bool `json:"success"`
ProductStatuses map[string]ProductStatus `json:"productStatuses,omitempty"`
}
// StatusesResponse type.
type StatusesResponse struct {
Success bool `json:"success"`
Statuses map[string]Status `json:"statuses,omitempty"`
}
// StatusGroupsResponse type.
type StatusGroupsResponse struct {
Success bool `json:"success"`
StatusGroups map[string]StatusGroup `json:"statusGroups,omitempty"`
}
// SitesResponse type.
type SitesResponse struct {
Success bool `json:"success"`
Sites map[string]Site `json:"sites,omitempty"`
}
// StoresResponse type.
type StoresResponse struct {
Success bool `json:"success"`
Stores []Store `json:"stores,omitempty"`
}
// InventoriesResponse type.
type InventoriesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Offers []Offer `json:"offers,omitempty"`
}
// StoreUploadResponse type.
type StoreUploadResponse struct {
Success bool `json:"success"`
ProcessedOffersCount int `json:"processedOffersCount,omitempty"`
NotFoundOffers []Offer `json:"notFoundOffers,omitempty"`
}
// ProductsGroupsResponse type.
type ProductsGroupsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
ProductGroup []ProductGroup `json:"productGroup,omitempty"`
}
// ProductsResponse type.
type ProductsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Products []Product `json:"products,omitempty"`
}
type ProductEditNotFoundResponse struct {
ID string `json:"id"`
ExternalID string `json:"externalId,omitempty"`
}
type ProductsBatchEditResponse struct {
SuccessfulResponse
ProcessedProductsCount int `json:"processedProductsCount,omitempty"`
NotFoundProducts []ProductEditNotFoundResponse `json:"notFoundProducts,omitempty"`
}
type ProductsBatchCreateResponse struct {
SuccessfulResponse
ProcessedProductsCount int `json:"processedProductsCount,omitempty"`
AddedProducts []int `json:"addedProducts,omitempty"`
}
// ProductsPropertiesResponse type.
type ProductsPropertiesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Properties []Property `json:"properties,omitempty"`
}
// CartResponse type.
type CartResponse struct {
SuccessfulResponse
Cart Cart `json:"cart"`
}
// DeliveryShipmentsResponse type.
type DeliveryShipmentsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
DeliveryShipments []DeliveryShipment `json:"deliveryShipments,omitempty"`
}
// DeliveryShipmentResponse type.
type DeliveryShipmentResponse struct {
Success bool `json:"success"`
DeliveryShipment *DeliveryShipment `json:"deliveryShipment,omitempty"`
}
// DeliveryShipmentUpdateResponse type.
type DeliveryShipmentUpdateResponse struct {
Success bool `json:"success"`
ID int `json:"id,omitempty"`
Status string `json:"status,omitempty"`
}
// IntegrationModuleResponse type.
type IntegrationModuleResponse struct {
Success bool `json:"success"`
IntegrationModule *IntegrationModule `json:"integrationModule,omitempty"`
}
// UpdateScopesResponse update scopes response.
type UpdateScopesResponse struct {
ErrorResponse
APIKey string `json:"apiKey"`
}
// IntegrationModuleEditResponse type.
type IntegrationModuleEditResponse struct {
Success bool `json:"success"`
Info ResponseInfo `json:"info,omitempty"`
}
// ResponseInfo type.
type ResponseInfo struct {
MgTransportInfo MgInfo `json:"mgTransport,omitempty"`
MgBotInfo MgInfo `json:"mgBot,omitempty"`
BillingInfo *BillingInfo `json:"billingInfo,omitempty"`
DeliveryTypeInfo DeliveryTypeInfo `json:"deliveryType,omitempty"`
}
type BillingInfo struct {
Price float64 `json:"price,omitempty"`
PriceWithDiscount float64 `json:"priceWithDiscount,omitempty"`
BillingType string `json:"billingType,omitempty"`
Currency *BillingInfoCurrency `json:"currency,omitempty"`
}
type BillingInfoCurrency struct {
Name string `json:"name,omitempty"`
ShortName string `json:"shortName,omitempty"`
Code string `json:"code,omitempty"`
}
// MgInfo type.
type MgInfo struct {
EndpointURL string `json:"endpointUrl"`
Token string `json:"token"`
}
// CostsResponse type.
type CostsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Costs []Cost `json:"costs,omitempty"`
}
// CostsUploadResponse type.
type CostsUploadResponse struct {
Success bool `json:"success"`
UploadedCosts []int `json:"uploadedCosts,omitempty"`
}
// CostsDeleteResponse type.
type CostsDeleteResponse struct {
Success bool `json:"success"`
Count int `json:"count,omitempty"`
NotRemovedIds []int `json:"notRemovedIds,omitempty"`
}
// CostResponse type.
type CostResponse struct {
Success bool `json:"success"`
Cost *Cost `json:"cost,omitempty"`
}
// FilesResponse type.
type FilesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Files []File `json:"files,omitempty"`
}
// FileUpload response.
type FileUploadResponse struct {
Success bool `json:"success"`
File *File `json:"file,omitempty"`
}
// FileResponse type.
type FileResponse struct {
Success bool `json:"success"`
File *File `json:"file,omitempty"`
}
// CustomFieldsResponse type.
type CustomFieldsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
CustomFields []CustomFields `json:"customFields,omitempty"`
}
// CustomDictionariesResponse type.
type CustomDictionariesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
CustomDictionaries *[]CustomDictionary `json:"customDictionaries,omitempty"`
}
// CustomResponse type.
type CustomResponse struct {
Success bool `json:"success"`
Code string `json:"code,omitempty"`
}
// CustomDictionaryResponse type.
type CustomDictionaryResponse struct {
Success bool `json:"success"`
CustomDictionary *CustomDictionary `json:"CustomDictionary,omitempty"`
}
// CustomFieldResponse type.
type CustomFieldResponse struct {
Success bool `json:"success"`
CustomField CustomFields `json:"customField,omitempty"`
}
// UnitsResponse type.
type UnitsResponse struct {
Success bool `json:"success"`
Units *[]Unit `json:"units,omitempty"`
}
// ErrorResponse should be returned to the one-step connection request in case of failure.
type ErrorResponse struct {
SuccessfulResponse
ErrorMessage string `json:"errorMsg"`
}
// ConnectResponse should be returned to the one-step connection request in case of successful connection.
type ConnectResponse struct {
SuccessfulResponse
AccountURL string `json:"accountUrl"`
}
// ConnectionConfigResponse contains connection configuration for one-step connection.
type ConnectionConfigResponse struct {
SuccessfulResponse
Scopes []string `json:"scopes"`
RegisterURL string `json:"registerUrl"`
}
// NewConnectResponse returns ConnectResponse with the provided account URL.
func NewConnectResponse(accountURL string) ConnectResponse {
return ConnectResponse{
SuccessfulResponse: SuccessfulResponse{Success: true},
AccountURL: accountURL,
}
}
// BonusOperationsResponse type.
type BonusOperationsResponse struct {
Success bool `json:"success"`
Pagination *CursorPagination `json:"pagination,omitempty"`
BonusOperations []BonusOperation `json:"bonusOperations,omitempty"`
}
// AccountBonusOperationsResponse type.
type AccountBonusOperationsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
BonusOperations []BonusOperation `json:"bonusOperations,omitempty"`
}
// ClientIDResponse type.
type ClientIDResponse struct {
ErrorMsg string `json:"errorMsg,omitempty"`
Errors map[string]string `json:"errors,omitempty"`
FailedClientIds []ClientID `json:"failed_client_ids,omitempty"`
Success bool `json:"success"`
}
// SourcesResponse type.
type SourcesResponse struct {
ErrorMsg string `json:"errorMsg,omitempty"`
Errors map[string]string `json:"errors,omitempty"`
FailedSources []Source `json:"failed_sources,omitempty"`
Success bool `json:"success"`
}
// CurrencyResponse type.
type CurrencyResponse struct {
Currencies []Currency `json:"currencies,omitempty"`
Success bool `json:"success"`
}
// CurrencyCreateResponse type.
type CurrencyCreateResponse struct {
Success bool `json:"success"`
ID int `json:"id,omitempty"`
}
type LoyaltyAccountResponse struct {
SuccessfulResponse
LoyaltyAccount `json:"loyaltyAccount"`
}
type LoyaltyAccountActivateResponse struct {
SuccessfulResponse
LoyaltyAccount `json:"loyaltyAccount"`
Verification SmsVerification `json:"verification,omitempty"`
}
type LoyaltyBonusCreditResponse struct {
SuccessfulResponse
LoyaltyBonus LoyaltyBonus `json:"loyaltyBonus"`
}
type LoyaltyBonusDetailsResponse struct {
SuccessfulResponse
Pagination `json:"pagination"`
Statistic LoyaltyBonusStatisticResponse `json:"statistic"`
Bonuses []BonusDetail `json:"bonuses,omitempty"`
}
type LoyaltyBonusStatisticResponse struct {
TotalAmount float64 `json:"totalAmount"`
}
type LoyaltyAccountsResponse struct {
SuccessfulResponse
Pagination *Pagination `json:"pagination"`
LoyaltyAccounts []LoyaltyAccount `json:"loyaltyAccounts,omitempty"`
}
type LoyaltyCalculateResponse struct {
SuccessfulResponse
Order SerializedLoyaltyOrder `json:"order,omitempty"`
Calculations []LoyaltyCalculation `json:"calculations,omitempty"`
Loyalty SerializedLoyalty `json:"loyalty,omitempty"`
}
type LoyaltiesResponse struct {
SuccessfulResponse
Pagination *Pagination `json:"pagination"`
Loyalties []Loyalty `json:"loyalties,omitempty"`
}
type LoyaltyResponse struct {
SuccessfulResponse
Loyalty Loyalty `json:"loyalty"`
}
type ActionProductsGroupResponse struct {
SuccessfulResponse
ID int `json:"id"`
}
type MGChannelTemplatesResponse struct {
Pagination *Pagination `json:"pagination"`
Templates []MGChannelTemplate `json:"templates"`
SuccessfulResponse
}
type StoreOffersResponse struct {
Pagination *Pagination `json:"pagination"`
SuccessfulResponse
Offers []Offer `json:"offers,omitempty"`
}

16
response_test.go Normal file
View file

@ -0,0 +1,16 @@
package retailcrm
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewConnectResponse(t *testing.T) {
assert.Equal(t, ConnectResponse{
SuccessfulResponse: SuccessfulResponse{
Success: true,
},
AccountURL: "https://example.com",
}, NewConnectResponse("https://example.com"))
}

30
system_time.go Normal file
View file

@ -0,0 +1,30 @@
package retailcrm
import (
"fmt"
"strings"
"time"
)
type SystemTime time.Time
const systemTimeLayout = "2006-01-02 15:04:05"
// UnmarshalJSON parses time.Time from system format.
func (st *SystemTime) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), `"`)
nt, err := time.Parse(systemTimeLayout, s)
*st = SystemTime(nt)
return
}
// MarshalJSON will marshal time.Time to system format.
func (st SystemTime) MarshalJSON() ([]byte, error) {
return []byte(st.String()), nil
}
// String returns the time in the custom format.
func (st *SystemTime) String() string {
t := time.Time(*st)
return fmt.Sprintf("%q", t.Format(systemTimeLayout))
}

122
template.go Normal file
View file

@ -0,0 +1,122 @@
package retailcrm
import (
"encoding/json"
"errors"
"fmt"
)
const (
// TemplateItemTypeText is a type for text chunk in template.
TemplateItemTypeText uint8 = iota
// TemplateItemTypeVar is a type for variable in template.
TemplateItemTypeVar
QuickReply ButtonType = "QUICK_REPLY"
PhoneNumber ButtonType = "PHONE_NUMBER"
URL ButtonType = "URL"
)
const (
// TemplateVarCustom is a custom variable type.
TemplateVarCustom = "custom"
// TemplateVarName is a name variable type.
TemplateVarName = "name"
// TemplateVarFirstName is a first name variable type.
TemplateVarFirstName = "first_name"
// TemplateVarLastName is a last name variable type.
TemplateVarLastName = "last_name"
)
// templateVarAssoc for checking variable validity, only for internal use.
var templateVarAssoc = map[string]interface{}{
TemplateVarCustom: nil,
TemplateVarName: nil,
TemplateVarFirstName: nil,
TemplateVarLastName: nil,
}
type Text struct {
Parts []TextTemplateItem `json:"parts"`
Example []string `json:"example,omitempty"`
}
type Media struct {
Example string `json:"example,omitempty"`
}
type Header struct {
Text *Text `json:"text,omitempty"`
Document *Media `json:"document,omitempty"`
Image *Media `json:"image,omitempty"`
Video *Media `json:"video,omitempty"`
}
type TemplateItemList []TextTemplateItem
// TextTemplateItem is a part of template.
type TextTemplateItem struct {
Text string
VarType string
Type uint8
}
// MarshalJSON controls how TextTemplateItem will be marshaled into JSON.
func (t TextTemplateItem) MarshalJSON() ([]byte, error) {
switch t.Type {
case TemplateItemTypeText:
return json.Marshal(t.Text)
case TemplateItemTypeVar:
return json.Marshal(map[string]interface{}{
"var": t.VarType,
})
}
return nil, errors.New("unknown TextTemplateItem type")
}
// UnmarshalJSON will correctly unmarshal TextTemplateItem.
func (t *TextTemplateItem) UnmarshalJSON(b []byte) error {
var obj interface{}
err := json.Unmarshal(b, &obj)
if err != nil {
return err
}
switch bodyPart := obj.(type) {
case string:
t.Type = TemplateItemTypeText
t.Text = bodyPart
case map[string]interface{}:
// {} case
if len(bodyPart) == 0 {
t.Type = TemplateItemTypeText
t.Text = "{}"
return nil
}
if varTypeCurr, ok := bodyPart["var"].(string); ok {
if _, ok := templateVarAssoc[varTypeCurr]; !ok {
return fmt.Errorf("invalid placeholder var '%s'", varTypeCurr)
}
t.Type = TemplateItemTypeVar
t.VarType = varTypeCurr
} else {
return errors.New("invalid TextTemplateItem")
}
default:
return errors.New("invalid TextTemplateItem")
}
return nil
}
type ButtonType string
type Button struct {
Type ButtonType `json:"type"`
URL string `json:"url,omitempty"`
Text string `json:"text,omitempty"`
PhoneNumber string `json:"phoneNumber,omitempty"`
Example []string `json:"example,omitempty"`
}

565
testutils.go Normal file
View file

@ -0,0 +1,565 @@
//go:build testutils
// +build testutils
package retailcrm
func getProductsCreate() []ProductCreate {
products := []ProductCreate{
{
CatalogID: 3,
BaseProduct: BaseProduct{
Name: "Product 1",
URL: "https://example.com/p/1",
Article: "p1",
ExternalID: "ext1",
Manufacturer: "man1",
Description: "Description 1",
Popular: true,
Stock: true,
Novelty: true,
Recommended: true,
Active: true,
Markable: true,
},
Groups: []ProductEditGroupInput{{ID: 19}},
},
{
CatalogID: 3,
BaseProduct: BaseProduct{
Name: "Product 2",
URL: "https://example.com/p/2",
Article: "p2",
ExternalID: "ext2",
Manufacturer: "man2",
Description: "Description 2",
Popular: true,
Stock: true,
Novelty: true,
Recommended: true,
Active: true,
Markable: true,
},
Groups: []ProductEditGroupInput{{ID: 19}},
},
}
return products
}
func getProductsCreateResponse() ProductsBatchCreateResponse {
return ProductsBatchCreateResponse{
SuccessfulResponse: SuccessfulResponse{Success: true},
ProcessedProductsCount: 2,
AddedProducts: []int{1, 2},
}
}
func getProductsEdit() []ProductEdit {
products := []ProductEdit{
{
BaseProduct: getProductsCreate()[0].BaseProduct,
ID: 194,
CatalogID: 3,
Site: "second",
},
{
BaseProduct: getProductsCreate()[1].BaseProduct,
ID: 195,
CatalogID: 3,
Site: "second",
},
}
return products
}
func getProductsEditResponse() ProductsBatchEditResponse {
return ProductsBatchEditResponse{
SuccessfulResponse: SuccessfulResponse{Success: true},
ProcessedProductsCount: 2,
NotFoundProducts: nil,
}
}
func getLoyaltyAccountCreate() SerializedCreateLoyaltyAccount {
return SerializedCreateLoyaltyAccount{
SerializedBaseLoyaltyAccount: SerializedBaseLoyaltyAccount{
PhoneNumber: "89151005004",
CustomFields: []interface{}{"dog"},
},
Customer: SerializedEntityCustomer{
ID: 123,
},
}
}
func getLoyaltyAccountCreateResponse() CreateLoyaltyAccountResponse {
return CreateLoyaltyAccountResponse{
SuccessfulResponse: SuccessfulResponse{Success: true},
LoyaltyAccount: LoyaltyAccount{
Active: true,
ID: 13,
PhoneNumber: "89151005004",
LoyaltyLevel: LoyaltyLevel{},
CreatedAt: "2022-11-24 12:39:37",
ActivatedAt: "2022-11-24 12:39:37",
CustomFields: map[string]interface{}{
"animal": "dog",
},
},
}
}
func getLoyaltyAccountEditResponse() EditLoyaltyAccountResponse {
return EditLoyaltyAccountResponse{
SuccessfulResponse: SuccessfulResponse{Success: true},
LoyaltyAccount: LoyaltyAccount{
Active: true,
ID: 13,
PhoneNumber: "89142221020",
LoyaltyLevel: LoyaltyLevel{},
CreatedAt: "2022-11-24 12:39:37",
ActivatedAt: "2022-11-24 12:39:37",
CustomFields: map[string]interface{}{
"animal": "dog",
},
},
}
}
func getLoyaltyAccountResponse() string {
return `{
"success": true,
"loyaltyAccount": {
"active": true,
"id": 13,
"loyalty": {
"id": 2
},
"customer": {
"id": 123,
"customFields": {},
"firstName": "Руслан1",
"lastName": "Ефанов",
"patronymic": ""
},
"phoneNumber": "89142221020",
"amount": 0,
"ordersSum": 0,
"nextLevelSum": 10000,
"level": {
"type": "bonus_percent",
"id": 5,
"name": "Новичок",
"sum": 0,
"privilegeSize": 5,
"privilegeSizePromo": 3
},
"createdAt": "2022-11-24 12:39:37",
"activatedAt": "2022-11-24 12:39:37",
"status": "activated",
"customFields": {
"custom_multiselect": ["test1", "test3"],
"custom_select": "test2",
"custom_integer": 456,
"custom_float": 8.43
}
}
}`
}
func getBonusDetailsResponse() string {
return `{
"success": true,
"pagination": {
"limit": 20,
"totalCount": 41,
"currentPage": 3,
"totalPageCount": 3
},
"statistic": {
"totalAmount": 240
},
"bonuses": [
{
"date": "2022-12-08",
"amount": 240
}
]
}`
}
func getLoyaltyAccountsResponse() string {
return `{
"success": true,
"pagination": {
"limit": 20,
"totalCount": 1,
"currentPage": 1,
"totalPageCount": 1
},
"loyaltyAccounts": [
{
"active": true,
"id": 14,
"loyalty": {
"id": 2
},
"customer": {
"id": 109,
"firstName": "Казимир",
"lastName": "Эльбрусов"
},
"phoneNumber": "89185556363",
"amount": 0,
"ordersSum": 0,
"nextLevelSum": 10000,
"level": {
"type": "bonus_percent",
"id": 5,
"name": "Новичок",
"sum": 0,
"privilegeSize": 5,
"privilegeSizePromo": 3
},
"createdAt": "2022-12-07 15:27:04",
"activatedAt": "2022-12-07 15:27:04",
"status": "activated"
}
]
}`
}
func getLoyaltyCalculateReq() LoyaltyCalculateRequest {
return LoyaltyCalculateRequest{
Site: "main",
Order: Order{
PrivilegeType: "loyalty_level",
Customer: &Customer{
ID: 123,
},
Items: []OrderItem{
{
InitialPrice: 10000,
Quantity: 1,
Offer: Offer{ID: 214},
PriceType: &PriceType{Code: "base"},
},
},
},
Bonuses: 10,
}
}
func getLoyaltyCalculateResponse() string {
return `{
"success": true,
"order": {
"bonusesCreditTotal": 999,
"bonusesChargeTotal": 10,
"privilegeType": "loyalty_level",
"totalSumm": 9990,
"loyaltyAccount": {
"id": 13,
"amount": 240
},
"loyaltyLevel": {
"id": 6,
"name": "Любитель"
},
"customer": {
"id": 123,
"personalDiscount": 0
},
"delivery": {
"cost": 0
},
"site": "main",
"items": [
{
"bonusesChargeTotal": 10,
"bonusesCreditTotal": 999,
"priceType": {
"code": "base"
},
"initialPrice": 10000,
"discounts": [
{
"type": "bonus_charge",
"amount": 10
}
],
"discountTotal": 10,
"prices": [
{
"price": 9990,
"quantity": 1
}
],
"quantity": 1,
"offer": {
"xmlId": "696999ed-bc8d-4d0f-9627-527acf7b1d57"
}
}
]
},
"calculations": [
{
"privilegeType": "loyalty_level",
"discount": 10,
"creditBonuses": 999,
"maxChargeBonuses": 240,
"maximum": true
},
{
"privilegeType": "none",
"discount": 10,
"creditBonuses": 0,
"maxChargeBonuses": 240,
"maximum": false
}
],
"loyalty": {
"name": "Бонусная программа",
"chargeRate": 1
}
}`
}
func getLoyaltiesResponse() string {
return `{
"success": true,
"pagination": {
"limit": 20,
"totalCount": 1,
"currentPage": 1,
"totalPageCount": 1
},
"loyalties": [
{
"levels": [
{
"type": "bonus_percent",
"id": 5,
"name": "Новичок",
"sum": 0,
"privilegeSize": 5,
"privilegeSizePromo": 3
},
{
"type": "bonus_percent",
"id": 6,
"name": "Любитель",
"sum": 10000,
"privilegeSize": 10,
"privilegeSizePromo": 5
},
{
"type": "bonus_percent",
"id": 7,
"name": "Продвинутый покупатель",
"sum": 25000,
"privilegeSize": 15,
"privilegeSizePromo": 7
},
{
"type": "bonus_percent",
"id": 8,
"name": "Мастер шоппинга",
"sum": 50000,
"privilegeSize": 20,
"privilegeSizePromo": 10
}
],
"active": true,
"blocked": false,
"id": 2,
"name": "Бонусная программа",
"confirmSmsCharge": false,
"confirmSmsRegistration": false,
"createdAt": "2022-01-18 15:40:22",
"activatedAt": "2022-12-08 12:05:45"
}
]
}`
}
func getLoyaltyResponse() string {
return `{
"success": true,
"loyalty": {
"levels": [
{
"type": "bonus_percent",
"id": 5,
"name": "Новичок",
"sum": 0,
"privilegeSize": 5,
"privilegeSizePromo": 3
},
{
"type": "bonus_percent",
"id": 6,
"name": "Любитель",
"sum": 10000,
"privilegeSize": 10,
"privilegeSizePromo": 5
},
{
"type": "bonus_percent",
"id": 7,
"name": "Продвинутый покупатель",
"sum": 25000,
"privilegeSize": 15,
"privilegeSizePromo": 7
},
{
"type": "bonus_percent",
"id": 8,
"name": "Мастер шоппинга",
"sum": 50000,
"privilegeSize": 20,
"privilegeSizePromo": 10
}
],
"active": true,
"blocked": false,
"id": 2,
"name": "Бонусная программа",
"confirmSmsCharge": false,
"confirmSmsRegistration": false,
"createdAt": "2022-01-18 15:40:22",
"activatedAt": "2022-12-08 12:05:45"
}
}`
}
func getMGTemplatesResponse() string {
return `{
"success": true,
"pagination": {
"limit": 10,
"totalCount": 100,
"currentPage": 5,
"totalPageCount": 10
},
"templates": [
{
"id": 1,
"externalId": 0,
"channel": {
"allowedSendByPhone": false,
"id": 1,
"externalId": 1,
"type": "fbmessenger",
"active": true,
"name": "fbmessenger"
},
"code": "namespace#NAMEAAA#ru",
"name": "NAMEAAA",
"active": true,
"template": [
"Text_0",
{
"var": "custom"
}
],
"templateExample": ["Text_1"],
"namespace": "namespace_0",
"lang": "en",
"category": "test_0",
"header": {
"text": {
"parts": [
"JABAAA",
{
"var": "custom"
}
],
"example": [
"AAAAAA"
]
},
"image": {
"example": "https://example.com/file/123.png"
},
"document": {
"example": "https://example.com/file/123.pdf"
},
"video": {
"example": "https://example.com/file/123.mp4"
}
},
"footer": "footer_0",
"buttons": [
{
"type": "PHONE_NUMBER",
"text": "your-phone-button-text",
"phoneNumber": "+79895553535"
},
{
"type": "QUICK_REPLY",
"text": "Yes"
},
{
"type": "URL",
"url": "https://example.com/file/{{1}}",
"text": "button",
"example": [
"https://www.website.com/dynamic-url-example"
]
}
],
"verificationStatus": "APPROVED"
}
]
}`
}
func getMGTemplatesForEdit() string {
return `[{"header":{"text":{"parts":["Hello,",{"var":"custom"}],"example":["Henry"]},"document":{"example":"https://example.com/file/123.pdf"},"image":{"example":"https://example.com/file/123.png"},"video":{"example":"https://example.com/file/123.mp4"}},"lang":"en","category":"test_0","code":"namespace#name_0#ru","name":"name_0","namespace":"namespace","footer":"footer_0","verificationStatus":"REJECTED","template":["Text_0",{"var":"custom"}],"buttons":[{"type":"PHONE_NUMBER","text":"your-phone-button-text","phoneNumber":"+79895553535"},{"type":"QUICK_REPLY","text":"Yes"},{"type":"URL","url":"https://example.com/file/{{1}}","text":"button","example":["https://www.website.com/dynamic-url-example"]}],"templateExample":["WIU"],"id":1,"externalId":10,"mgChannelId":110,"active":true}]`
}
func getStoreOfferResponse() string {
return `{
"success": true,
"pagination": {
"limit": 20,
"totalCount": 1,
"currentPage": 1,
"totalPageCount": 1
},
"offers": [
{
"images": [
"https://s3-s1.retailcrm.tech/ru-central1/retailcrm/dev-vega-d32aea7f9a5bc26eba6ad986077cea03/product/65a92fa0bb737-test.jpeg"
],
"id": 76,
"site": "main",
"name": "Название\nПеревод строки",
"article": "Артикул",
"product": {
"type": "product",
"catalogId": 2,
"id": 222
},
"prices": [
{
"priceType": "base",
"price": 10000,
"ordering": 991,
"currency": "RUB"
}
],
"purchasePrice": 10,
"quantity": 5,
"active": true,
"unit": {
"code": "pc",
"name": "Штука",
"sym": "шт."
}
}
]
}`
}

1719
types.go Normal file

File diff suppressed because it is too large Load diff

55
types_test.go Normal file
View file

@ -0,0 +1,55 @@
package retailcrm
import (
"encoding/json"
"reflect"
"testing"
)
func TestClient_OrderDeliveryData(t *testing.T) {
d := OrderDeliveryData{
OrderDeliveryDataBasic: OrderDeliveryDataBasic{
"track",
"status",
"address",
"type",
},
}
data, _ := json.Marshal(d)
expectedStr := `{"payerType":"type","pickuppointAddress":"address","status":"status","trackNumber":"track"}`
if string(data) != expectedStr {
t.Errorf("Marshaled: %s\nExpected: %s\n", data, expectedStr)
}
d.AdditionalFields = map[string]interface{}{
"customFirst": "one",
"customSecond": "two",
}
data, _ = json.Marshal(d)
expectedStr = `{"customFirst":"one","customSecond":"two","payerType":"type","pickuppointAddress":"address","status":"status","trackNumber":"track"}`
if string(data) != expectedStr {
t.Errorf("Marshaled: %s\nExpected: %s\n", data, expectedStr)
}
d = OrderDeliveryData{}
json.Unmarshal(data, &d)
expected := OrderDeliveryData{
OrderDeliveryDataBasic: OrderDeliveryDataBasic{
"track",
"status",
"address",
"type",
},
AdditionalFields: map[string]interface{}{
"customFirst": "one",
"customSecond": "two",
},
}
eq := reflect.DeepEqual(expected, d)
if eq != true {
t.Errorf("Unmarshaled: %#v\nExpected: %#v\n", d, expected)
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,483 +0,0 @@
package v5
// SuccessfulResponse type
type SuccessfulResponse struct {
Success bool `json:"success,omitempty"`
}
// CreateResponse type
type CreateResponse struct {
Success bool `json:"success"`
ID int `json:"id,omitempty"`
}
// OperationResponse type
type OperationResponse struct {
Success bool `json:"success"`
Errors map[string]string `json:"errors,omitempty,brackets"`
}
// VersionResponse return available API versions
type VersionResponse struct {
Success bool `json:"success,omitempty"`
Versions []string `json:"versions,brackets,omitempty"`
}
// CredentialResponse return available API methods
type CredentialResponse struct {
Success bool `json:"success,omitempty"`
Credentials []string `json:"credentials,brackets,omitempty"`
SiteAccess string `json:"siteAccess,omitempty"`
SitesAvailable []string `json:"sitesAvailable,brackets,omitempty"`
}
// CustomerResponse type
type CustomerResponse struct {
Success bool `json:"success"`
Customer *Customer `json:"customer,omitempty,brackets"`
}
// CorporateCustomerResponse type
type CorporateCustomerResponse struct {
Success bool `json:"success"`
CorporateCustomer *CorporateCustomer `json:"customerCorporate,omitempty,brackets"`
}
// CustomersResponse type
type CustomersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Customers []Customer `json:"customers,omitempty,brackets"`
}
// CorporateCustomersResponse type
type CorporateCustomersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
CustomersCorporate []CorporateCustomer `json:"customersCorporate,omitempty,brackets"`
}
// CorporateCustomersNotesResponse type
type CorporateCustomersNotesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Notes []Note `json:"notes,omitempty,brackets"`
}
// CorporateCustomersAddressesResponse type
type CorporateCustomersAddressesResponse struct {
Success bool `json:"success"`
Addresses []CorporateCustomerAddress `json:"addresses"`
}
// CorporateCustomerCompaniesResponse type
type CorporateCustomerCompaniesResponse struct {
Success bool `json:"success"`
Companies []Company `json:"companies"`
}
// CorporateCustomerContactsResponse type
type CorporateCustomerContactsResponse struct {
Success bool `json:"success"`
Contacts []CorporateCustomerContact `json:"contacts"`
}
// CustomerChangeResponse type
type CustomerChangeResponse struct {
Success bool `json:"success"`
ID int `json:"id,omitempty"`
State string `json:"state,omitempty"`
}
// CorporateCustomerChangeResponse type
type CorporateCustomerChangeResponse CustomerChangeResponse
// CustomersUploadResponse type
type CustomersUploadResponse struct {
Success bool `json:"success"`
UploadedCustomers []IdentifiersPair `json:"uploadedCustomers,omitempty,brackets"`
}
// CorporateCustomersUploadResponse type
type CorporateCustomersUploadResponse CustomersUploadResponse
// CustomersHistoryResponse type
type CustomersHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []CustomerHistoryRecord `json:"history,omitempty,brackets"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// CorporateCustomersHistoryResponse type
type CorporateCustomersHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []CorporateCustomerHistoryRecord `json:"history,omitempty,brackets"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// OrderResponse type
type OrderResponse struct {
Success bool `json:"success"`
Order *Order `json:"order,omitempty,brackets"`
}
// OrdersResponse type
type OrdersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Orders []Order `json:"orders,omitempty,brackets"`
}
// OrdersStatusesResponse type
type OrdersStatusesResponse struct {
Success bool `json:"success"`
Orders []OrdersStatus `json:"orders"`
}
// OrdersUploadResponse type
type OrdersUploadResponse struct {
Success bool `json:"success"`
UploadedOrders []IdentifiersPair `json:"uploadedOrders,omitempty,brackets"`
}
// OrdersHistoryResponse type
type OrdersHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []OrdersHistoryRecord `json:"history,omitempty,brackets"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// PackResponse type
type PackResponse struct {
Success bool `json:"success"`
Pack *Pack `json:"pack,omitempty,brackets"`
}
// PacksResponse type
type PacksResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Packs []Pack `json:"packs,omitempty,brackets"`
}
// PacksHistoryResponse type
type PacksHistoryResponse struct {
Success bool `json:"success,omitempty"`
GeneratedAt string `json:"generatedAt,omitempty"`
History []PacksHistoryRecord `json:"history,omitempty,brackets"`
Pagination *Pagination `json:"pagination,omitempty"`
}
// UserResponse type
type UserResponse struct {
Success bool `json:"success"`
User *User `json:"user,omitempty,brackets"`
}
// UsersResponse type
type UsersResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Users []User `json:"users,omitempty,brackets"`
}
// UserGroupsResponse type
type UserGroupsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Groups []UserGroup `json:"groups,omitempty,brackets"`
}
// TaskResponse type
type TaskResponse struct {
Success bool `json:"success"`
Task *Task `json:"task,omitempty,brackets"`
}
// TasksResponse type
type TasksResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Tasks []Task `json:"tasks,omitempty,brackets"`
}
// NotesResponse type
type NotesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Notes []Note `json:"notes,omitempty,brackets"`
}
// SegmentsResponse type
type SegmentsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Segments []Segment `json:"segments,omitempty,brackets"`
}
// CountriesResponse type
type CountriesResponse struct {
Success bool `json:"success"`
CountriesIso []string `json:"countriesIso,omitempty,brackets"`
}
// CostGroupsResponse type
type CostGroupsResponse struct {
Success bool `json:"success"`
CostGroups []CostGroup `json:"costGroups,omitempty,brackets"`
}
// CostItemsResponse type
type CostItemsResponse struct {
Success bool `json:"success"`
CostItems []CostItem `json:"costItems,omitempty,brackets"`
}
// CouriersResponse type
type CouriersResponse struct {
Success bool `json:"success"`
Couriers []Courier `json:"couriers,omitempty,brackets"`
}
// DeliveryServiceResponse type
type DeliveryServiceResponse struct {
Success bool `json:"success"`
DeliveryServices map[string]DeliveryService `json:"deliveryServices,omitempty,brackets"`
}
// DeliveryTypesResponse type
type DeliveryTypesResponse struct {
Success bool `json:"success"`
DeliveryTypes map[string]DeliveryType `json:"deliveryTypes,omitempty,brackets"`
}
// LegalEntitiesResponse type
type LegalEntitiesResponse struct {
Success bool `json:"success"`
LegalEntities []LegalEntity `json:"legalEntities,omitempty,brackets"`
}
// OrderMethodsResponse type
type OrderMethodsResponse struct {
Success bool `json:"success"`
OrderMethods map[string]OrderMethod `json:"orderMethods,omitempty,brackets"`
}
// OrderTypesResponse type
type OrderTypesResponse struct {
Success bool `json:"success"`
OrderTypes map[string]OrderType `json:"orderTypes,omitempty,brackets"`
}
// PaymentStatusesResponse type
type PaymentStatusesResponse struct {
Success bool `json:"success"`
PaymentStatuses map[string]PaymentStatus `json:"paymentStatuses,omitempty,brackets"`
}
// PaymentTypesResponse type
type PaymentTypesResponse struct {
Success bool `json:"success"`
PaymentTypes map[string]PaymentType `json:"paymentTypes,omitempty,brackets"`
}
// PriceTypesResponse type
type PriceTypesResponse struct {
Success bool `json:"success"`
PriceTypes []PriceType `json:"priceTypes,omitempty,brackets"`
}
// ProductStatusesResponse type
type ProductStatusesResponse struct {
Success bool `json:"success"`
ProductStatuses map[string]ProductStatus `json:"productStatuses,omitempty,brackets"`
}
// StatusesResponse type
type StatusesResponse struct {
Success bool `json:"success"`
Statuses map[string]Status `json:"statuses,omitempty,brackets"`
}
// StatusGroupsResponse type
type StatusGroupsResponse struct {
Success bool `json:"success"`
StatusGroups map[string]StatusGroup `json:"statusGroups,omitempty,brackets"`
}
// SitesResponse type
type SitesResponse struct {
Success bool `json:"success"`
Sites map[string]Site `json:"sites,omitempty,brackets"`
}
// StoresResponse type
type StoresResponse struct {
Success bool `json:"success"`
Stores []Store `json:"stores,omitempty,brackets"`
}
// InventoriesResponse type
type InventoriesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Offers []Offer `json:"offers,omitempty"`
}
// StoreUploadResponse type
type StoreUploadResponse struct {
Success bool `json:"success"`
ProcessedOffersCount int `json:"processedOffersCount,omitempty"`
NotFoundOffers []Offer `json:"notFoundOffers,omitempty"`
}
// ProductsGroupsResponse type
type ProductsGroupsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
ProductGroup []ProductGroup `json:"productGroup,omitempty,brackets"`
}
// ProductsResponse type
type ProductsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Products []Product `json:"products,omitempty,brackets"`
}
// ProductsPropertiesResponse type
type ProductsPropertiesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Properties []Property `json:"properties,omitempty,brackets"`
}
// DeliveryShipmentsResponse type
type DeliveryShipmentsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
DeliveryShipments []DeliveryShipment `json:"deliveryShipments,omitempty,brackets"`
}
// DeliveryShipmentResponse type
type DeliveryShipmentResponse struct {
Success bool `json:"success"`
DeliveryShipment *DeliveryShipment `json:"deliveryShipment,omitempty,brackets"`
}
// DeliveryShipmentUpdateResponse type
type DeliveryShipmentUpdateResponse struct {
Success bool `json:"success"`
ID int `json:"id,omitempty"`
Status string `json:"status,omitempty"`
}
// IntegrationModuleResponse type
type IntegrationModuleResponse struct {
Success bool `json:"success"`
IntegrationModule *IntegrationModule `json:"integrationModule,omitempty"`
}
// IntegrationModuleEditResponse type
type IntegrationModuleEditResponse struct {
Success bool `json:"success"`
Info ResponseInfo `json:"info,omitempty,brackets"`
}
// ResponseInfo type
type ResponseInfo struct {
MgTransportInfo MgInfo `json:"mgTransport,omitempty,brackets"`
MgBotInfo MgInfo `json:"mgBot,omitempty,brackets"`
}
// MgInfo type
type MgInfo struct {
EndpointUrl string `json:"endpointUrl"`
Token string `json:"token"`
}
// CostsResponse type
type CostsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Costs []Cost `json:"costs,omitempty,brackets"`
}
// CostsUploadResponse type
type CostsUploadResponse struct {
Success bool `json:"success"`
UploadedCosts []int `json:"uploadedCosts,omitempty,brackets"`
}
// CostsDeleteResponse type
type CostsDeleteResponse struct {
Success bool `json:"success"`
Count int `json:"count,omitempty,brackets"`
NotRemovedIds []int `json:"notRemovedIds,omitempty,brackets"`
}
// CostResponse type
type CostResponse struct {
Success bool `json:"success"`
Cost *Cost `json:"cost,omitempty,brackets"`
}
// FilesResponse type
type FilesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
Files []File `json:"files,omitempty"`
}
// FileUpload response
type FileUploadResponse struct {
Success bool `json:"success"`
File *File `json:"file,omitempty"`
}
// FileResponse type
type FileResponse struct {
Success bool `json:"success"`
File *File `json:"file,omitempty"`
}
// CustomFieldsResponse type
type CustomFieldsResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
CustomFields []CustomFields `json:"customFields,omitempty,brackets"`
}
// CustomDictionariesResponse type
type CustomDictionariesResponse struct {
Success bool `json:"success"`
Pagination *Pagination `json:"pagination,omitempty"`
CustomDictionaries *[]CustomDictionary `json:"customDictionaries,omitempty,brackets"`
}
// CustomResponse type
type CustomResponse struct {
Success bool `json:"success"`
Code string `json:"code,omitempty"`
}
// CustomDictionaryResponse type
type CustomDictionaryResponse struct {
Success bool `json:"success"`
CustomDictionary *CustomDictionary `json:"CustomDictionary,omitempty,brackets"`
}
// CustomFieldResponse type
type CustomFieldResponse struct {
Success bool `json:"success"`
CustomField CustomFields `json:"customField,omitempty,brackets"`
}
// UnitsResponse type
type UnitsResponse struct {
Success bool `json:"success"`
Units *[]Unit `json:"units,omitempty,brackets"`
}

File diff suppressed because it is too large Load diff