bx-rest 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.en.md +25 -22
- package/README.md +25 -22
- package/esm2022/functions/errors.mjs +64 -8
- package/esm2022/services/navvy/extends/navvy-pag-base.mjs +12 -3
- package/fesm2022/bx-rest.mjs +72 -8
- package/fesm2022/bx-rest.mjs.map +1 -1
- package/functions/errors.d.ts +46 -6
- package/package.json +1 -1
- package/services/navvy/extends/navvy-pag-base.d.ts +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@
|
|
|
7
7
|
История до версии 1.0.0 здесь не восстанавливалась — смотрите `git log` для
|
|
8
8
|
более ранних изменений.
|
|
9
9
|
|
|
10
|
+
## [1.2.0] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `.res()` терял код и описание ошибки Bitrix, если портал отвечал со
|
|
15
|
+
статусом не 2xx — а так Bitrix отдаёт большинство ошибок (`expired_token`
|
|
16
|
+
— 401, `QUERY_LIMIT_EXCEEDED` — 503, `Not found` у `crm.*.get` — 400).
|
|
17
|
+
Вместо `Error("AxiosError: Request failed with status code 400")` теперь
|
|
18
|
+
приходит `BXRestApiError` с `error` и `error_description` — с
|
|
19
|
+
`throwOnApiError` и без него. Поддерживается и формат ошибок REST v3
|
|
20
|
+
(`{ error: { code, message } }`). Сетевые ошибки и ответы не от Bitrix
|
|
21
|
+
(например, HTML-страница прокси) по-прежнему приходят обычным `Error`.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- У `BXRestApiError` появились поля `status` (HTTP-статус; `undefined`, если
|
|
26
|
+
ошибка пришла в теле ответа 2xx), `method` (REST-метод) и `cause` (исходная
|
|
27
|
+
ошибка axios с полным телом ответа в `cause.response.data`). Конструктор
|
|
28
|
+
принимает их третьим необязательным аргументом — существующие вызовы
|
|
29
|
+
`new BXRestApiError(error, error_description)` работают как раньше.
|
|
30
|
+
|
|
10
31
|
## [1.1.0] - 2026-10-09
|
|
11
32
|
|
|
12
33
|
### Fixed
|
package/README.en.md
CHANGED
|
@@ -156,16 +156,27 @@ BXRestSettings.update({
|
|
|
156
156
|
|
|
157
157
|
## Error handling
|
|
158
158
|
|
|
159
|
-
`
|
|
159
|
+
`.res()` can fail in three ways — which one depends on how the portal answered.
|
|
160
160
|
|
|
161
|
-
| Failure |
|
|
162
|
-
| --- | --- |
|
|
163
|
-
|
|
|
164
|
-
| Bitrix API
|
|
161
|
+
| Failure | How it surfaces on `.res()` |
|
|
162
|
+
| --- | --- |
|
|
163
|
+
| Bitrix API error with a non-2xx HTTP status — this is how Bitrix sends most errors: `expired_token` (401), `ERROR_METHOD_NOT_FOUND` (404), `QUERY_LIMIT_EXCEEDED` (503), `Not found` from `crm.*.get` (400) | The returned `Observable` errors with a `BXRestApiError`, with or without `throwOnApiError`. |
|
|
164
|
+
| Bitrix API error in the body of a 2xx response | By default, `.res()` resolves normally with `undefined` — **it does not throw**. Pass `{ throwOnApiError: true }` to make it throw a `BXRestApiError` instead. |
|
|
165
|
+
| Transport / configuration errors — network failure, missing auth, missing base URL, a response that is not from Bitrix (e.g. a proxy's HTML error page) | The returned `Observable` errors with a plain `Error`. |
|
|
166
|
+
|
|
167
|
+
`BXRestApiError` fields:
|
|
168
|
+
|
|
169
|
+
| Field | Value |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| `error` | Bitrix error code, e.g. `expired_token`. Can be an empty string: `crm.*.get` answers a missing ID with an empty code and `Not found` in the description. |
|
|
172
|
+
| `error_description` | Bitrix error description; also used as `message`. |
|
|
173
|
+
| `status` | HTTP status, e.g. `401`; `undefined` when the error came in the body of a 2xx response. |
|
|
174
|
+
| `method` | REST method, e.g. `crm.deal.get`. |
|
|
175
|
+
| `cause` | The original axios error when the status was non-2xx; the full response body is in `cause.response.data` (for REST v3 — including `validation`). |
|
|
165
176
|
|
|
166
|
-
|
|
177
|
+
For REST v3 methods, `error` holds the error `code` and `error_description` its `message`.
|
|
167
178
|
|
|
168
|
-
**
|
|
179
|
+
**Recommended — `.res({ throwOnApiError: true })`.** Every Bitrix error then arrives as a `BXRestApiError` whatever the HTTP status, and can be handled alongside transport errors in a single `catchError`/`error` callback:
|
|
169
180
|
|
|
170
181
|
```typescript
|
|
171
182
|
import { BXRestNavvy, BXRestApiError } from 'bx-rest'
|
|
@@ -180,7 +191,10 @@ bxRest.crm.deal.list({
|
|
|
180
191
|
next: deals => console.log(deals),
|
|
181
192
|
error: err => {
|
|
182
193
|
if (err instanceof BXRestApiError) {
|
|
183
|
-
console.error('Bitrix API error:', err.error, err.error_description)
|
|
194
|
+
console.error('Bitrix API error:', err.status, err.error, err.error_description)
|
|
195
|
+
if (err.error === 'expired_token') {
|
|
196
|
+
// refresh the token and retry the request
|
|
197
|
+
}
|
|
184
198
|
} else {
|
|
185
199
|
console.error('Request failed:', err.message)
|
|
186
200
|
}
|
|
@@ -188,7 +202,9 @@ bxRest.crm.deal.list({
|
|
|
188
202
|
})
|
|
189
203
|
```
|
|
190
204
|
|
|
191
|
-
|
|
205
|
+
Without `throwOnApiError`, plain `.res()` can't tell "no records" apart from "an error in a 2xx response" — both resolve as `undefined`.
|
|
206
|
+
|
|
207
|
+
**Alternative — check the raw response.** `.resVanilla()` returns the raw Bitrix body for 2xx responses; check it with the exported `isBXRestAnswerSuccess()` type guard. Note that `.resVanilla()`, `.mapForVanilla()` and `.resAll()` don't convert errors: on a Bitrix error with a non-2xx status they fail with the original axios error, with the response body in `err.response.data`.
|
|
192
208
|
|
|
193
209
|
```typescript
|
|
194
210
|
import { firstValueFrom } from 'rxjs'
|
|
@@ -211,19 +227,6 @@ if (!answer || !isBXRestAnswerSuccess(answer)) {
|
|
|
211
227
|
}
|
|
212
228
|
```
|
|
213
229
|
|
|
214
|
-
Regardless of `throwOnApiError`, transport-level failures always propagate as `Observable` errors and can be caught the usual RxJS way:
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
bxRest.crm.deal.list({
|
|
218
|
-
select: ['ID', 'TITLE'],
|
|
219
|
-
filter: {},
|
|
220
|
-
start: 0
|
|
221
|
-
}).res().subscribe({
|
|
222
|
-
next: deals => console.log(deals),
|
|
223
|
-
error: err => console.error('Request failed:', err.message)
|
|
224
|
-
})
|
|
225
|
-
```
|
|
226
|
-
|
|
227
230
|
## Usage in Angular
|
|
228
231
|
|
|
229
232
|
Register the public clients and `Navvy` when you want to inject them into Angular services or components:
|
package/README.md
CHANGED
|
@@ -166,16 +166,27 @@ BXRestSettings.update({
|
|
|
166
166
|
|
|
167
167
|
## Обработка ошибок
|
|
168
168
|
|
|
169
|
-
`
|
|
169
|
+
`.res()` может завершиться неудачей тремя способами — какой именно, зависит от того, как ответил портал.
|
|
170
170
|
|
|
171
|
-
| Сбой |
|
|
172
|
-
| --- | --- |
|
|
173
|
-
|
|
|
174
|
-
|
|
|
171
|
+
| Сбой | Как проявляется в `.res()` |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| Ошибка API Bitrix с HTTP-статусом не 2xx — так Bitrix отдаёт большинство ошибок: `expired_token` (401), `ERROR_METHOD_NOT_FOUND` (404), `QUERY_LIMIT_EXCEEDED` (503), `Not found` у `crm.*.get` (400) | Возвращённый `Observable` завершается ошибкой `BXRestApiError` — с `throwOnApiError` и без него. |
|
|
174
|
+
| Ошибка API Bitrix в теле ответа со статусом 2xx | По умолчанию `.res()` резолвится обычным образом со значением `undefined` — **исключение не выбрасывается**. Передайте `{ throwOnApiError: true }`, чтобы вместо этого выбрасывалось `BXRestApiError`. |
|
|
175
|
+
| Ошибки транспорта / конфигурации — сбой сети, отсутствие авторизации, отсутствие базового URL, ответ не от Bitrix (например, HTML-страница ошибки прокси) | Возвращённый `Observable` завершается обычной ошибкой `Error`. |
|
|
176
|
+
|
|
177
|
+
Поля `BXRestApiError`:
|
|
178
|
+
|
|
179
|
+
| Поле | Значение |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| `error` | Код ошибки Bitrix, например `expired_token`. Может быть пустой строкой: на несуществующий ID `crm.*.get` отвечает пустым кодом и `Not found` в описании. |
|
|
182
|
+
| `error_description` | Описание ошибки Bitrix; оно же используется как `message`. |
|
|
183
|
+
| `status` | HTTP-статус, например `401`; `undefined`, если ошибка пришла в теле ответа со статусом 2xx. |
|
|
184
|
+
| `method` | REST-метод, например `crm.deal.get`. |
|
|
185
|
+
| `cause` | Исходная ошибка axios, если статус был не 2xx; полное тело ответа — в `cause.response.data` (для REST v3 — в том числе `validation`). |
|
|
175
186
|
|
|
176
|
-
|
|
187
|
+
Для методов REST v3 в `error` попадает `code` ошибки, а в `error_description` — её `message`.
|
|
177
188
|
|
|
178
|
-
|
|
189
|
+
**Рекомендуемый вариант — `.res({ throwOnApiError: true })`.** Тогда любая ошибка Bitrix приходит как `BXRestApiError` независимо от HTTP-статуса, и её можно обработать вместе с транспортными ошибками в одном колбэке `catchError`/`error`:
|
|
179
190
|
|
|
180
191
|
```typescript
|
|
181
192
|
import { BXRestNavvy, BXRestApiError } from 'bx-rest'
|
|
@@ -190,7 +201,10 @@ bxRest.crm.deal.list({
|
|
|
190
201
|
next: deals => console.log(deals),
|
|
191
202
|
error: err => {
|
|
192
203
|
if (err instanceof BXRestApiError) {
|
|
193
|
-
console.error('Bitrix API error:', err.error, err.error_description)
|
|
204
|
+
console.error('Bitrix API error:', err.status, err.error, err.error_description)
|
|
205
|
+
if (err.error === 'expired_token') {
|
|
206
|
+
// обновить токен и повторить запрос
|
|
207
|
+
}
|
|
194
208
|
} else {
|
|
195
209
|
console.error('Request failed:', err.message)
|
|
196
210
|
}
|
|
@@ -198,7 +212,9 @@ bxRest.crm.deal.list({
|
|
|
198
212
|
})
|
|
199
213
|
```
|
|
200
214
|
|
|
201
|
-
|
|
215
|
+
Без `throwOnApiError` по голому `.res()` нельзя отличить «нет записей» от «ошибки в ответе 2xx» — оба случая резолвятся как `undefined`.
|
|
216
|
+
|
|
217
|
+
**Альтернатива — проверять сырой ответ.** `.resVanilla()` возвращает сырое тело ответа Bitrix для ответов 2xx; проверяйте его экспортируемым type guard `isBXRestAnswerSuccess()`. Учтите, что `.resVanilla()`, `.mapForVanilla()` и `.resAll()` ошибки не преобразуют: на ошибку Bitrix со статусом не 2xx они завершаются исходной ошибкой axios, тело ответа — в `err.response.data`.
|
|
202
218
|
|
|
203
219
|
```typescript
|
|
204
220
|
import { firstValueFrom } from 'rxjs'
|
|
@@ -221,19 +237,6 @@ if (!answer || !isBXRestAnswerSuccess(answer)) {
|
|
|
221
237
|
}
|
|
222
238
|
```
|
|
223
239
|
|
|
224
|
-
Независимо от `throwOnApiError`, ошибки транспортного уровня всегда всплывают как ошибки `Observable` и могут быть перехвачены обычным способом RxJS:
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
bxRest.crm.deal.list({
|
|
228
|
-
select: ['ID', 'TITLE'],
|
|
229
|
-
filter: {},
|
|
230
|
-
start: 0
|
|
231
|
-
}).res().subscribe({
|
|
232
|
-
next: deals => console.log(deals),
|
|
233
|
-
error: err => console.error('Request failed:', err.message)
|
|
234
|
-
})
|
|
235
|
-
```
|
|
236
|
-
|
|
237
240
|
## Использование в Angular
|
|
238
241
|
|
|
239
242
|
Зарегистрируйте публичные клиенты и `Navvy`, когда нужно внедрять их в сервисы или компоненты Angular:
|
|
@@ -1,17 +1,73 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Bitrix REST call resolves as a Bitrix-level API error, e.g.
|
|
4
|
-
* `INSUFFICIENT_SCOPE` or `expired_token`.
|
|
3
|
+
* A Bitrix-level API error, e.g. `INSUFFICIENT_SCOPE` or `expired_token`.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* `.res()` fails with it whenever the portal answers with a Bitrix error and
|
|
6
|
+
* a non-2xx HTTP status — which is how Bitrix sends most of its errors. When
|
|
7
|
+
* the error comes in the body of a 2xx response, `.res()` resolves as
|
|
8
|
+
* `undefined` by default and fails with it only with
|
|
9
|
+
* `{ throwOnApiError: true }` — see the "Error handling" section of the README.
|
|
8
10
|
*/
|
|
9
11
|
export class BXRestApiError extends Error {
|
|
10
|
-
constructor(error, error_description) {
|
|
11
|
-
super(error_description || error || 'Bitrix API error');
|
|
12
|
+
constructor(error, error_description, details) {
|
|
13
|
+
super(error_description || error || 'Bitrix API error', details?.cause !== undefined ? { cause: details.cause } : undefined);
|
|
12
14
|
this.error = error;
|
|
13
15
|
this.error_description = error_description;
|
|
14
16
|
this.name = 'BXRestApiError';
|
|
17
|
+
this.status = details?.status;
|
|
18
|
+
this.method = details?.method;
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Recognises a Bitrix error body: REST v2 `{ error, error_description }` and
|
|
23
|
+
* REST v3 `{ error: { code, message } }`. Returns `undefined` for anything
|
|
24
|
+
* else — a success payload, an HTML page from a proxy, an empty body.
|
|
25
|
+
*
|
|
26
|
+
* An empty `error` still counts: Bitrix answers e.g. `crm.deal.get` for a
|
|
27
|
+
* missing ID with `{ error: '', error_description: 'Not found' }`.
|
|
28
|
+
*/
|
|
29
|
+
export function parseBXRestErrorBody(body) {
|
|
30
|
+
if (!body || typeof body !== 'object' || !('error' in body)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const { error, error_description } = body;
|
|
34
|
+
if (typeof error === 'string') {
|
|
35
|
+
return {
|
|
36
|
+
error,
|
|
37
|
+
error_description: typeof error_description === 'string' ? error_description : undefined
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
41
|
+
const { code, message } = error;
|
|
42
|
+
return {
|
|
43
|
+
error: String(code),
|
|
44
|
+
error_description: typeof message === 'string' ? message : undefined
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Turns an HTTP failure into a `BXRestApiError` when the portal answered with
|
|
51
|
+
* a Bitrix error body. Bitrix sends most errors with a non-2xx status
|
|
52
|
+
* (401 `expired_token`, 404 `ERROR_METHOD_NOT_FOUND`, 503
|
|
53
|
+
* `QUERY_LIMIT_EXCEEDED`, 400 "Not found" from `crm.*.get`, ...), and axios
|
|
54
|
+
* rejects on those.
|
|
55
|
+
*
|
|
56
|
+
* Returns `undefined` for network errors and for responses that are not a
|
|
57
|
+
* Bitrix error body, so the caller can keep its own handling for them.
|
|
58
|
+
*/
|
|
59
|
+
export function bxRestApiErrorFromHttp(err, method) {
|
|
60
|
+
if (!axios.isAxiosError(err) || !err.response) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
const parsed = parseBXRestErrorBody(err.response.data);
|
|
64
|
+
if (!parsed) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
return new BXRestApiError(parsed.error, parsed.error_description, {
|
|
68
|
+
status: err.response.status,
|
|
69
|
+
method,
|
|
70
|
+
cause: err
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvYngtcmVzdC9zcmMvZnVuY3Rpb25zL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxPQUFPLENBQUE7QUFpQnpCOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxPQUFPLGNBQWUsU0FBUSxLQUFLO0lBSXZDLFlBQ1MsS0FBeUIsRUFDekIsaUJBQXFDLEVBQzVDLE9BQStCO1FBRS9CLEtBQUssQ0FDSCxpQkFBaUIsSUFBSSxLQUFLLElBQUksa0JBQWtCLEVBQ2hELE9BQU8sRUFBRSxLQUFLLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FDcEUsQ0FBQTtRQVBNLFVBQUssR0FBTCxLQUFLLENBQW9CO1FBQ3pCLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBb0I7UUFPNUMsSUFBSSxDQUFDLElBQUksR0FBRyxnQkFBZ0IsQ0FBQTtRQUM1QixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sRUFBRSxNQUFNLENBQUE7UUFDN0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLEVBQUUsTUFBTSxDQUFBO0lBQy9CLENBQUM7Q0FDRjtBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQUMsSUFBYTtJQUVoRCxJQUFJLENBQUMsSUFBSSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxDQUFDLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDNUQsT0FBTyxTQUFTLENBQUE7SUFDbEIsQ0FBQztJQUNELE1BQU0sRUFBRSxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsR0FBRyxJQUF1RCxDQUFBO0lBRTVGLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDOUIsT0FBTztZQUNMLEtBQUs7WUFDTCxpQkFBaUIsRUFBRSxPQUFPLGlCQUFpQixLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLFNBQVM7U0FDekYsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLEtBQUssSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFELE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsS0FBNkMsQ0FBQTtRQUN2RSxPQUFPO1lBQ0wsS0FBSyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUM7WUFDbkIsaUJBQWlCLEVBQUUsT0FBTyxPQUFPLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVM7U0FDckUsQ0FBQTtJQUNILENBQUM7SUFFRCxPQUFPLFNBQVMsQ0FBQTtBQUNsQixDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQixDQUFDLEdBQVksRUFBRSxNQUFjO0lBQ2pFLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQzlDLE9BQU8sU0FBUyxDQUFBO0lBQ2xCLENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFBO0lBQ3RELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE9BQU8sU0FBUyxDQUFBO0lBQ2xCLENBQUM7SUFDRCxPQUFPLElBQUksY0FBYyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLGlCQUFpQixFQUFFO1FBQ2hFLE1BQU0sRUFBRSxHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU07UUFDM0IsTUFBTTtRQUNOLEtBQUssRUFBRSxHQUFHO0tBQ1gsQ0FBQyxDQUFBO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBheGlvcyBmcm9tICdheGlvcydcblxuZXhwb3J0IGludGVyZmFjZSBCWFJlc3RBcGlFcnJvckRldGFpbHMge1xuICAvKipcbiAgICogSFRUUCBzdGF0dXMgb2YgdGhlIHBvcnRhbCByZXNwb25zZS4gYHVuZGVmaW5lZGAgd2hlbiB0aGUgZXJyb3IgY2FtZSBpblxuICAgKiB0aGUgYm9keSBvZiBhIHN1Y2Nlc3NmdWwgKDJ4eCkgcmVzcG9uc2UuXG4gICAqL1xuICBzdGF0dXM/OiBudW1iZXJcbiAgLyoqIFJFU1QgbWV0aG9kIHRoYXQgZmFpbGVkLCBlLmcuIGBjcm0uZGVhbC5nZXRgLiAqL1xuICBtZXRob2Q/OiBzdHJpbmdcbiAgLyoqXG4gICAqIFRoZSBvcmlnaW5hbCBlcnJvciwgZS5nLiB0aGUgYEF4aW9zRXJyb3JgIOKAlCBpdHMgYHJlc3BvbnNlLmRhdGFgIGhvbGRzXG4gICAqIHRoZSBmdWxsIHJlc3BvbnNlIGJvZHkgKGluY2x1ZGluZyBSRVNUIHYzIGB2YWxpZGF0aW9uYCkuXG4gICAqL1xuICBjYXVzZT86IHVua25vd25cbn1cblxuLyoqXG4gKiBBIEJpdHJpeC1sZXZlbCBBUEkgZXJyb3IsIGUuZy4gYElOU1VGRklDSUVOVF9TQ09QRWAgb3IgYGV4cGlyZWRfdG9rZW5gLlxuICpcbiAqIGAucmVzKClgIGZhaWxzIHdpdGggaXQgd2hlbmV2ZXIgdGhlIHBvcnRhbCBhbnN3ZXJzIHdpdGggYSBCaXRyaXggZXJyb3IgYW5kXG4gKiBhIG5vbi0yeHggSFRUUCBzdGF0dXMg4oCUIHdoaWNoIGlzIGhvdyBCaXRyaXggc2VuZHMgbW9zdCBvZiBpdHMgZXJyb3JzLiBXaGVuXG4gKiB0aGUgZXJyb3IgY29tZXMgaW4gdGhlIGJvZHkgb2YgYSAyeHggcmVzcG9uc2UsIGAucmVzKClgIHJlc29sdmVzIGFzXG4gKiBgdW5kZWZpbmVkYCBieSBkZWZhdWx0IGFuZCBmYWlscyB3aXRoIGl0IG9ubHkgd2l0aFxuICogYHsgdGhyb3dPbkFwaUVycm9yOiB0cnVlIH1gIOKAlCBzZWUgdGhlIFwiRXJyb3IgaGFuZGxpbmdcIiBzZWN0aW9uIG9mIHRoZSBSRUFETUUuXG4gKi9cbmV4cG9ydCBjbGFzcyBCWFJlc3RBcGlFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgcmVhZG9ubHkgc3RhdHVzOiBudW1iZXIgfCB1bmRlZmluZWRcbiAgcmVhZG9ubHkgbWV0aG9kOiBzdHJpbmcgfCB1bmRlZmluZWRcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwdWJsaWMgZXJyb3I6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBwdWJsaWMgZXJyb3JfZGVzY3JpcHRpb246IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBkZXRhaWxzPzogQlhSZXN0QXBpRXJyb3JEZXRhaWxzXG4gICkge1xuICAgIHN1cGVyKFxuICAgICAgZXJyb3JfZGVzY3JpcHRpb24gfHwgZXJyb3IgfHwgJ0JpdHJpeCBBUEkgZXJyb3InLFxuICAgICAgZGV0YWlscz8uY2F1c2UgIT09IHVuZGVmaW5lZCA/IHsgY2F1c2U6IGRldGFpbHMuY2F1c2UgfSA6IHVuZGVmaW5lZFxuICAgIClcbiAgICB0aGlzLm5hbWUgPSAnQlhSZXN0QXBpRXJyb3InXG4gICAgdGhpcy5zdGF0dXMgPSBkZXRhaWxzPy5zdGF0dXNcbiAgICB0aGlzLm1ldGhvZCA9IGRldGFpbHM/Lm1ldGhvZFxuICB9XG59XG5cbi8qKlxuICogUmVjb2duaXNlcyBhIEJpdHJpeCBlcnJvciBib2R5OiBSRVNUIHYyIGB7IGVycm9yLCBlcnJvcl9kZXNjcmlwdGlvbiB9YCBhbmRcbiAqIFJFU1QgdjMgYHsgZXJyb3I6IHsgY29kZSwgbWVzc2FnZSB9IH1gLiBSZXR1cm5zIGB1bmRlZmluZWRgIGZvciBhbnl0aGluZ1xuICogZWxzZSDigJQgYSBzdWNjZXNzIHBheWxvYWQsIGFuIEhUTUwgcGFnZSBmcm9tIGEgcHJveHksIGFuIGVtcHR5IGJvZHkuXG4gKlxuICogQW4gZW1wdHkgYGVycm9yYCBzdGlsbCBjb3VudHM6IEJpdHJpeCBhbnN3ZXJzIGUuZy4gYGNybS5kZWFsLmdldGAgZm9yIGFcbiAqIG1pc3NpbmcgSUQgd2l0aCBgeyBlcnJvcjogJycsIGVycm9yX2Rlc2NyaXB0aW9uOiAnTm90IGZvdW5kJyB9YC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlQlhSZXN0RXJyb3JCb2R5KGJvZHk6IHVua25vd24pXG4gIDogeyBlcnJvcjogc3RyaW5nLCBlcnJvcl9kZXNjcmlwdGlvbjogc3RyaW5nIHwgdW5kZWZpbmVkIH0gfCB1bmRlZmluZWQge1xuICBpZiAoIWJvZHkgfHwgdHlwZW9mIGJvZHkgIT09ICdvYmplY3QnIHx8ICEoJ2Vycm9yJyBpbiBib2R5KSkge1xuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxuICBjb25zdCB7IGVycm9yLCBlcnJvcl9kZXNjcmlwdGlvbiB9ID0gYm9keSBhcyB7IGVycm9yOiB1bmtub3duLCBlcnJvcl9kZXNjcmlwdGlvbj86IHVua25vd24gfVxuXG4gIGlmICh0eXBlb2YgZXJyb3IgPT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGVycm9yLFxuICAgICAgZXJyb3JfZGVzY3JpcHRpb246IHR5cGVvZiBlcnJvcl9kZXNjcmlwdGlvbiA9PT0gJ3N0cmluZycgPyBlcnJvcl9kZXNjcmlwdGlvbiA6IHVuZGVmaW5lZFxuICAgIH1cbiAgfVxuXG4gIGlmIChlcnJvciAmJiB0eXBlb2YgZXJyb3IgPT09ICdvYmplY3QnICYmICdjb2RlJyBpbiBlcnJvcikge1xuICAgIGNvbnN0IHsgY29kZSwgbWVzc2FnZSB9ID0gZXJyb3IgYXMgeyBjb2RlOiB1bmtub3duLCBtZXNzYWdlPzogdW5rbm93biB9XG4gICAgcmV0dXJuIHtcbiAgICAgIGVycm9yOiBTdHJpbmcoY29kZSksXG4gICAgICBlcnJvcl9kZXNjcmlwdGlvbjogdHlwZW9mIG1lc3NhZ2UgPT09ICdzdHJpbmcnID8gbWVzc2FnZSA6IHVuZGVmaW5lZFxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB1bmRlZmluZWRcbn1cblxuLyoqXG4gKiBUdXJucyBhbiBIVFRQIGZhaWx1cmUgaW50byBhIGBCWFJlc3RBcGlFcnJvcmAgd2hlbiB0aGUgcG9ydGFsIGFuc3dlcmVkIHdpdGhcbiAqIGEgQml0cml4IGVycm9yIGJvZHkuIEJpdHJpeCBzZW5kcyBtb3N0IGVycm9ycyB3aXRoIGEgbm9uLTJ4eCBzdGF0dXNcbiAqICg0MDEgYGV4cGlyZWRfdG9rZW5gLCA0MDQgYEVSUk9SX01FVEhPRF9OT1RfRk9VTkRgLCA1MDNcbiAqIGBRVUVSWV9MSU1JVF9FWENFRURFRGAsIDQwMCBcIk5vdCBmb3VuZFwiIGZyb20gYGNybS4qLmdldGAsIC4uLiksIGFuZCBheGlvc1xuICogcmVqZWN0cyBvbiB0aG9zZS5cbiAqXG4gKiBSZXR1cm5zIGB1bmRlZmluZWRgIGZvciBuZXR3b3JrIGVycm9ycyBhbmQgZm9yIHJlc3BvbnNlcyB0aGF0IGFyZSBub3QgYVxuICogQml0cml4IGVycm9yIGJvZHksIHNvIHRoZSBjYWxsZXIgY2FuIGtlZXAgaXRzIG93biBoYW5kbGluZyBmb3IgdGhlbS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ4UmVzdEFwaUVycm9yRnJvbUh0dHAoZXJyOiB1bmtub3duLCBtZXRob2Q6IHN0cmluZyk6IEJYUmVzdEFwaUVycm9yIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCFheGlvcy5pc0F4aW9zRXJyb3IoZXJyKSB8fCAhZXJyLnJlc3BvbnNlKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZFxuICB9XG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlQlhSZXN0RXJyb3JCb2R5KGVyci5yZXNwb25zZS5kYXRhKVxuICBpZiAoIXBhcnNlZCkge1xuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxuICByZXR1cm4gbmV3IEJYUmVzdEFwaUVycm9yKHBhcnNlZC5lcnJvciwgcGFyc2VkLmVycm9yX2Rlc2NyaXB0aW9uLCB7XG4gICAgc3RhdHVzOiBlcnIucmVzcG9uc2Uuc3RhdHVzLFxuICAgIG1ldGhvZCxcbiAgICBjYXVzZTogZXJyXG4gIH0pXG59XG4iXX0=
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { catchError, map } from 'rxjs/operators';
|
|
2
2
|
import { BXRestMapResult, instanceOfiBXRestAnswerSuccess } from '../../../functions/mapResult';
|
|
3
3
|
import { HttpBXServices } from '../../http/HttpBX';
|
|
4
|
-
import { BXRestApiError } from '../../../functions/errors';
|
|
4
|
+
import { BXRestApiError, bxRestApiErrorFromHttp, parseBXRestErrorBody } from '../../../functions/errors';
|
|
5
5
|
export class NavvyPagBase {
|
|
6
6
|
constructor(url, param, map = undefined, restApi = false) {
|
|
7
7
|
this.url = url;
|
|
@@ -31,9 +31,11 @@ export class NavvyPagBase {
|
|
|
31
31
|
: v));
|
|
32
32
|
}
|
|
33
33
|
mapAndError(request, options) {
|
|
34
|
+
const method = this.http.getNameMethod(this.url);
|
|
34
35
|
return request.pipe(map(v => {
|
|
35
36
|
if (options?.throwOnApiError && v && !instanceOfiBXRestAnswerSuccess(v)) {
|
|
36
|
-
|
|
37
|
+
const parsed = parseBXRestErrorBody(v);
|
|
38
|
+
throw new BXRestApiError(parsed ? parsed.error : v.error, parsed ? parsed.error_description : v.error_description, { method });
|
|
37
39
|
}
|
|
38
40
|
return BXRestMapResult(v);
|
|
39
41
|
}), catchError((err) => {
|
|
@@ -41,8 +43,15 @@ export class NavvyPagBase {
|
|
|
41
43
|
if (err instanceof BXRestApiError) {
|
|
42
44
|
throw err;
|
|
43
45
|
}
|
|
46
|
+
// Большинство ошибок Bitrix приходит со статусом не 2xx, и axios
|
|
47
|
+
// отклоняет такой ответ — достаём из него код и описание ошибки,
|
|
48
|
+
// а не отдаём безликое "AxiosError: Request failed with status code 400".
|
|
49
|
+
const apiError = bxRestApiErrorFromHttp(err, method);
|
|
50
|
+
if (apiError) {
|
|
51
|
+
throw apiError;
|
|
52
|
+
}
|
|
44
53
|
throw new Error((err.error) ? err.error : err);
|
|
45
54
|
}));
|
|
46
55
|
}
|
|
47
56
|
}
|
|
48
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
57
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmF2dnktcGFnLWJhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9ieC1yZXN0L3NyYy9zZXJ2aWNlcy9uYXZ2eS9leHRlbmRzL25hdnZ5LXBhZy1iYXNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDaEQsT0FBTyxFQUFFLGVBQWUsRUFBRSw4QkFBOEIsRUFBRSxNQUFNLDhCQUE4QixDQUFBO0FBQzlGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUVsRCxPQUFPLEVBQUUsY0FBYyxFQUFFLHNCQUFzQixFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUE7QUFpQnhHLE1BQU0sT0FBZ0IsWUFBWTtJQUtoQyxZQUNZLEdBQWEsRUFDYixLQUFRLEVBQ1IsTUFBNEQsU0FBUyxFQUNyRSxVQUFVLEtBQUs7UUFIZixRQUFHLEdBQUgsR0FBRyxDQUFVO1FBQ2IsVUFBSyxHQUFMLEtBQUssQ0FBRztRQUNSLFFBQUcsR0FBSCxHQUFHLENBQWtFO1FBQ3JFLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFOM0IsYUFBUSxHQUFHLEVBQUUsQ0FBQSxDQUFDLDhCQUE4QjtRQVExQyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksY0FBYyxFQUFFLENBQUE7SUFDbEMsQ0FBQztJQUVELFVBQVU7UUFDUixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDOUQsQ0FBQztJQUVELGFBQWE7UUFDWCxPQUFPLElBQUksQ0FBQyxVQUFVLEVBQUU7YUFDckIsSUFBSSxDQUNILEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRTtZQUNOLElBQUksQ0FBQyxJQUFJLDhCQUE4QixDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDdkQsb0RBQW9EO2dCQUNwRCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxNQUFXLENBQTBCLENBQUE7Z0JBQy9ELE9BQU8sRUFBRSxHQUFHLENBQUMsRUFBRSxHQUFHLEVBQUMsTUFBTSxFQUFFLE1BQU0sRUFBQyxFQUEwQyxDQUFBO1lBQzlFLENBQUM7WUFDRCxPQUFPLENBQXFELENBQUE7UUFDOUQsQ0FBQyxDQUFDLENBQ0gsQ0FBQTtJQUNMLENBQUM7SUFFRCxHQUFHLENBQUMsT0FBMEI7UUFDNUIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQ3RELEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUM7WUFDdEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ2IsQ0FBQyxDQUFDLENBQUMsQ0FDSixDQUMrQyxDQUFBO0lBQ3BELENBQUM7SUFFRCxXQUFXLENBQ1QsT0FBaUQsRUFDakQsT0FBMEI7UUFFMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQ2hELE9BQU8sT0FBTyxDQUFDLElBQUksQ0FDakIsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ04sSUFBSSxPQUFPLEVBQUUsZUFBZSxJQUFJLENBQUMsSUFBSSxDQUFDLDhCQUE4QixDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ3hFLE1BQU0sTUFBTSxHQUFHLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFBO2dCQUN0QyxNQUFNLElBQUksY0FBYyxDQUN0QixNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQy9CLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsaUJBQWlCLEVBQ3ZELEVBQUUsTUFBTSxFQUFFLENBQ1gsQ0FBQTtZQUNILENBQUM7WUFDRCxPQUFPLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUMzQixDQUFDLENBQUMsRUFDRixVQUFVLENBQUMsQ0FBQyxHQUFRLEVBQUUsRUFBRTtZQUN0QixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBQ2xCLElBQUksR0FBRyxZQUFZLGNBQWMsRUFBRSxDQUFDO2dCQUNsQyxNQUFNLEdBQUcsQ0FBQTtZQUNYLENBQUM7WUFDRCxpRUFBaUU7WUFDakUsaUVBQWlFO1lBQ2pFLDBFQUEwRTtZQUMxRSxNQUFNLFFBQVEsR0FBRyxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsTUFBTSxDQUFDLENBQUE7WUFDcEQsSUFBSSxRQUFRLEVBQUUsQ0FBQztnQkFDYixNQUFNLFFBQVEsQ0FBQTtZQUNoQixDQUFDO1lBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDaEQsQ0FBQyxDQUFDLENBQ0gsQ0FBQTtJQUNILENBQUM7Q0FFRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE9ic2VydmFibGUgfSBmcm9tICdyeGpzJ1xyXG5pbXBvcnQgeyBpQlhSZXN0QW5zd2VyIH0gZnJvbSAnLi4vLi4vLi4vdHlwaWZpY2F0aW9uL3Jlc3QvYmFzZS9hbnN3ZXInXHJcbmltcG9ydCB7IGNhdGNoRXJyb3IsIG1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJ1xyXG5pbXBvcnQgeyBCWFJlc3RNYXBSZXN1bHQsIGluc3RhbmNlT2ZpQlhSZXN0QW5zd2VyU3VjY2VzcyB9IGZyb20gJy4uLy4uLy4uL2Z1bmN0aW9ucy9tYXBSZXN1bHQnXHJcbmltcG9ydCB7IEh0dHBCWFNlcnZpY2VzIH0gZnJvbSAnLi4vLi4vaHR0cC9IdHRwQlgnXHJcbmltcG9ydCB7IFJldHVyblR5cGVOYXZ2eSB9IGZyb20gJy4uL25hdnZ5LXN1cHBvcnQnXHJcbmltcG9ydCB7IEJYUmVzdEFwaUVycm9yLCBieFJlc3RBcGlFcnJvckZyb21IdHRwLCBwYXJzZUJYUmVzdEVycm9yQm9keSB9IGZyb20gJy4uLy4uLy4uL2Z1bmN0aW9ucy9lcnJvcnMnXHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEJYUmVzdFJlc09wdGlvbnMge1xyXG4gIC8qKlxyXG4gICAqIFdoZW4gYHRydWVgLCBhIEJpdHJpeC1sZXZlbCBBUEkgZXJyb3IgdGhhdCBjb21lcyBpbiB0aGUgYm9keSBvZiBhIDJ4eFxyXG4gICAqIHJlc3BvbnNlIG1ha2VzIHRoZSBgT2JzZXJ2YWJsZWAgcmV0dXJuZWQgYnkgYC5yZXMoKWAgZXJyb3Igb3V0IHdpdGggYVxyXG4gICAqIGBCWFJlc3RBcGlFcnJvcmAgaW5zdGVhZCBvZiByZXNvbHZpbmcgd2l0aCBgdW5kZWZpbmVkYC5cclxuICAgKlxyXG4gICAqIEVycm9ycyB0aGF0IEJpdHJpeCBzZW5kcyB3aXRoIGEgbm9uLTJ4eCBIVFRQIHN0YXR1cyAobW9zdCBvZiB0aGVtLCBlLmcuXHJcbiAgICogYGV4cGlyZWRfdG9rZW5gLCBgSU5TVUZGSUNJRU5UX1NDT1BFYCkgZmFpbCB3aXRoIGBCWFJlc3RBcGlFcnJvcmBcclxuICAgKiByZWdhcmRsZXNzIG9mIHRoaXMgb3B0aW9uLlxyXG4gICAqXHJcbiAgICogRGVmYXVsdDogYGZhbHNlYCAocHJlc2VydmVzIHRoZSBoaXN0b3JpY2FsIGAucmVzKClgIGJlaGF2aW91cikuXHJcbiAgICovXHJcbiAgdGhyb3dPbkFwaUVycm9yPzogYm9vbGVhblxyXG59XHJcblxyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTmF2dnlQYWdCYXNlPFQsIFIsIFA+IHtcclxuXHJcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGh0dHA6IEh0dHBCWFNlcnZpY2VzXHJcbiAgcGFnZVNpemUgPSA1MCAvLyDRgdGC0LDQvdC00LDRgNGC0L3Ri9C5INGA0LDQt9C80LXRgCDRgdGC0YDQsNC90LjRhtGLXHJcblxyXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihcclxuICAgIHByb3RlY3RlZCB1cmw6IHN0cmluZ1tdLFxyXG4gICAgcHJvdGVjdGVkIHBhcmFtOiBQLFxyXG4gICAgcHJvdGVjdGVkIG1hcDogKChwYXJhbTogVHwgdW5kZWZpbmVkKSA9PiBSIHwgdW5kZWZpbmVkKSB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZCxcclxuICAgIHByb3RlY3RlZCByZXN0QXBpID0gZmFsc2VcclxuICApIHtcclxuICAgIHRoaXMuaHR0cCA9IG5ldyBIdHRwQlhTZXJ2aWNlcygpXHJcbiAgfVxyXG5cclxuICByZXNWYW5pbGxhKCkge1xyXG4gICAgcmV0dXJuIHRoaXMuaHR0cC5wb3N0PFQ+KHRoaXMudXJsLCB0aGlzLnBhcmFtLCB0aGlzLnJlc3RBcGkpXHJcbiAgfVxyXG5cclxuICBtYXBGb3JWYW5pbGxhKCk6IE9ic2VydmFibGU8aUJYUmVzdEFuc3dlcjxSZXR1cm5UeXBlTmF2dnk8VCwgUj4+IHwgdW5kZWZpbmVkPiB7XHJcbiAgICByZXR1cm4gdGhpcy5yZXNWYW5pbGxhKClcclxuICAgICAgLnBpcGUoXHJcbiAgICAgICAgbWFwKHYgPT4ge1xyXG4gICAgICAgICAgaWYgKHYgJiYgaW5zdGFuY2VPZmlCWFJlc3RBbnN3ZXJTdWNjZXNzKHYpICYmIHRoaXMubWFwKSB7XHJcbiAgICAgICAgICAgIC8vINCf0YDQuNC80LXQvdGP0LXQvCB0aGlzLm1hcCDQuCDRj9Cy0L3QviDQv9GA0LjQstC+0LTQuNC8INGC0LjQvyDRgNC10LfRg9C70YzRgtCw0YLQsFxyXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLm1hcCh2LnJlc3VsdCBhcyBUKSBhcyBSZXR1cm5UeXBlTmF2dnk8VCwgUj5cclxuICAgICAgICAgICAgcmV0dXJuIHsgLi4udiwgLi4ue3Jlc3VsdDogcmVzdWx0fSB9IGFzIGlCWFJlc3RBbnN3ZXI8UmV0dXJuVHlwZU5hdnZ5PFQsIFI+PlxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIHYgYXMgaUJYUmVzdEFuc3dlcjxSZXR1cm5UeXBlTmF2dnk8VCwgUj4+IHwgdW5kZWZpbmVkXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gIH1cclxuXHJcbiAgcmVzKG9wdGlvbnM/OiBCWFJlc3RSZXNPcHRpb25zKSB7XHJcbiAgICByZXR1cm4gdGhpcy5tYXBBbmRFcnJvcih0aGlzLnJlc1ZhbmlsbGEoKSwgb3B0aW9ucykucGlwZShcclxuICAgICAgbWFwKHYgPT4gKHYgJiYgdGhpcy5tYXApXHJcbiAgICAgICAgPyB0aGlzLm1hcCh2KVxyXG4gICAgICAgIDogdlxyXG4gICAgICApXHJcbiAgICApIGFzIE9ic2VydmFibGU8UmV0dXJuVHlwZU5hdnZ5PFQsIFI+IHwgdW5kZWZpbmVkPlxyXG4gIH1cclxuXHJcbiAgbWFwQW5kRXJyb3I8VD4oXHJcbiAgICByZXF1ZXN0OiBPYnNlcnZhYmxlPGlCWFJlc3RBbnN3ZXI8VD4gfCB1bmRlZmluZWQ+LFxyXG4gICAgb3B0aW9ucz86IEJYUmVzdFJlc09wdGlvbnNcclxuICApIHtcclxuICAgIGNvbnN0IG1ldGhvZCA9IHRoaXMuaHR0cC5nZXROYW1lTWV0aG9kKHRoaXMudXJsKVxyXG4gICAgcmV0dXJuIHJlcXVlc3QucGlwZShcclxuICAgICAgbWFwKHYgPT4ge1xyXG4gICAgICAgIGlmIChvcHRpb25zPy50aHJvd09uQXBpRXJyb3IgJiYgdiAmJiAhaW5zdGFuY2VPZmlCWFJlc3RBbnN3ZXJTdWNjZXNzKHYpKSB7XHJcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUJYUmVzdEVycm9yQm9keSh2KVxyXG4gICAgICAgICAgdGhyb3cgbmV3IEJYUmVzdEFwaUVycm9yKFxyXG4gICAgICAgICAgICBwYXJzZWQgPyBwYXJzZWQuZXJyb3IgOiB2LmVycm9yLFxyXG4gICAgICAgICAgICBwYXJzZWQgPyBwYXJzZWQuZXJyb3JfZGVzY3JpcHRpb24gOiB2LmVycm9yX2Rlc2NyaXB0aW9uLFxyXG4gICAgICAgICAgICB7IG1ldGhvZCB9XHJcbiAgICAgICAgICApXHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBCWFJlc3RNYXBSZXN1bHQodilcclxuICAgICAgfSksXHJcbiAgICAgIGNhdGNoRXJyb3IoKGVycjogYW55KSA9PiB7XHJcbiAgICAgICAgY29uc29sZS5lcnJvcihlcnIpXHJcbiAgICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIEJYUmVzdEFwaUVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJcclxuICAgICAgICB9XHJcbiAgICAgICAgLy8g0JHQvtC70YzRiNC40L3RgdGC0LLQviDQvtGI0LjQsdC+0LogQml0cml4INC/0YDQuNGF0L7QtNC40YIg0YHQviDRgdGC0LDRgtGD0YHQvtC8INC90LUgMnh4LCDQuCBheGlvc1xyXG4gICAgICAgIC8vINC+0YLQutC70L7QvdGP0LXRgiDRgtCw0LrQvtC5INC+0YLQstC10YIg4oCUINC00L7RgdGC0LDRkdC8INC40Lcg0L3QtdCz0L4g0LrQvtC0INC4INC+0L/QuNGB0LDQvdC40LUg0L7RiNC40LHQutC4LFxyXG4gICAgICAgIC8vINCwINC90LUg0L7RgtC00LDRkdC8INCx0LXQt9C70LjQutC+0LUgXCJBeGlvc0Vycm9yOiBSZXF1ZXN0IGZhaWxlZCB3aXRoIHN0YXR1cyBjb2RlIDQwMFwiLlxyXG4gICAgICAgIGNvbnN0IGFwaUVycm9yID0gYnhSZXN0QXBpRXJyb3JGcm9tSHR0cChlcnIsIG1ldGhvZClcclxuICAgICAgICBpZiAoYXBpRXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IGFwaUVycm9yXHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigoZXJyLmVycm9yKSA/IGVyci5lcnJvciA6IGVycilcclxuICAgICAgfSlcclxuICAgIClcclxuICB9XHJcblxyXG59XHJcbiJdfQ==
|
package/fesm2022/bx-rest.mjs
CHANGED
|
@@ -1059,21 +1059,76 @@ class HttpBXServices extends HttpServices {
|
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
1061
|
/**
|
|
1062
|
-
*
|
|
1063
|
-
* Bitrix REST call resolves as a Bitrix-level API error, e.g.
|
|
1064
|
-
* `INSUFFICIENT_SCOPE` or `expired_token`.
|
|
1062
|
+
* A Bitrix-level API error, e.g. `INSUFFICIENT_SCOPE` or `expired_token`.
|
|
1065
1063
|
*
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1064
|
+
* `.res()` fails with it whenever the portal answers with a Bitrix error and
|
|
1065
|
+
* a non-2xx HTTP status — which is how Bitrix sends most of its errors. When
|
|
1066
|
+
* the error comes in the body of a 2xx response, `.res()` resolves as
|
|
1067
|
+
* `undefined` by default and fails with it only with
|
|
1068
|
+
* `{ throwOnApiError: true }` — see the "Error handling" section of the README.
|
|
1068
1069
|
*/
|
|
1069
1070
|
class BXRestApiError extends Error {
|
|
1070
|
-
constructor(error, error_description) {
|
|
1071
|
-
super(error_description || error || 'Bitrix API error');
|
|
1071
|
+
constructor(error, error_description, details) {
|
|
1072
|
+
super(error_description || error || 'Bitrix API error', details?.cause !== undefined ? { cause: details.cause } : undefined);
|
|
1072
1073
|
this.error = error;
|
|
1073
1074
|
this.error_description = error_description;
|
|
1074
1075
|
this.name = 'BXRestApiError';
|
|
1076
|
+
this.status = details?.status;
|
|
1077
|
+
this.method = details?.method;
|
|
1075
1078
|
}
|
|
1076
1079
|
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Recognises a Bitrix error body: REST v2 `{ error, error_description }` and
|
|
1082
|
+
* REST v3 `{ error: { code, message } }`. Returns `undefined` for anything
|
|
1083
|
+
* else — a success payload, an HTML page from a proxy, an empty body.
|
|
1084
|
+
*
|
|
1085
|
+
* An empty `error` still counts: Bitrix answers e.g. `crm.deal.get` for a
|
|
1086
|
+
* missing ID with `{ error: '', error_description: 'Not found' }`.
|
|
1087
|
+
*/
|
|
1088
|
+
function parseBXRestErrorBody(body) {
|
|
1089
|
+
if (!body || typeof body !== 'object' || !('error' in body)) {
|
|
1090
|
+
return undefined;
|
|
1091
|
+
}
|
|
1092
|
+
const { error, error_description } = body;
|
|
1093
|
+
if (typeof error === 'string') {
|
|
1094
|
+
return {
|
|
1095
|
+
error,
|
|
1096
|
+
error_description: typeof error_description === 'string' ? error_description : undefined
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
if (error && typeof error === 'object' && 'code' in error) {
|
|
1100
|
+
const { code, message } = error;
|
|
1101
|
+
return {
|
|
1102
|
+
error: String(code),
|
|
1103
|
+
error_description: typeof message === 'string' ? message : undefined
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
return undefined;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Turns an HTTP failure into a `BXRestApiError` when the portal answered with
|
|
1110
|
+
* a Bitrix error body. Bitrix sends most errors with a non-2xx status
|
|
1111
|
+
* (401 `expired_token`, 404 `ERROR_METHOD_NOT_FOUND`, 503
|
|
1112
|
+
* `QUERY_LIMIT_EXCEEDED`, 400 "Not found" from `crm.*.get`, ...), and axios
|
|
1113
|
+
* rejects on those.
|
|
1114
|
+
*
|
|
1115
|
+
* Returns `undefined` for network errors and for responses that are not a
|
|
1116
|
+
* Bitrix error body, so the caller can keep its own handling for them.
|
|
1117
|
+
*/
|
|
1118
|
+
function bxRestApiErrorFromHttp(err, method) {
|
|
1119
|
+
if (!axios.isAxiosError(err) || !err.response) {
|
|
1120
|
+
return undefined;
|
|
1121
|
+
}
|
|
1122
|
+
const parsed = parseBXRestErrorBody(err.response.data);
|
|
1123
|
+
if (!parsed) {
|
|
1124
|
+
return undefined;
|
|
1125
|
+
}
|
|
1126
|
+
return new BXRestApiError(parsed.error, parsed.error_description, {
|
|
1127
|
+
status: err.response.status,
|
|
1128
|
+
method,
|
|
1129
|
+
cause: err
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1077
1132
|
|
|
1078
1133
|
class NavvyPagBase {
|
|
1079
1134
|
constructor(url, param, map = undefined, restApi = false) {
|
|
@@ -1104,9 +1159,11 @@ class NavvyPagBase {
|
|
|
1104
1159
|
: v));
|
|
1105
1160
|
}
|
|
1106
1161
|
mapAndError(request, options) {
|
|
1162
|
+
const method = this.http.getNameMethod(this.url);
|
|
1107
1163
|
return request.pipe(map(v => {
|
|
1108
1164
|
if (options?.throwOnApiError && v && !instanceOfiBXRestAnswerSuccess(v)) {
|
|
1109
|
-
|
|
1165
|
+
const parsed = parseBXRestErrorBody(v);
|
|
1166
|
+
throw new BXRestApiError(parsed ? parsed.error : v.error, parsed ? parsed.error_description : v.error_description, { method });
|
|
1110
1167
|
}
|
|
1111
1168
|
return BXRestMapResult(v);
|
|
1112
1169
|
}), catchError((err) => {
|
|
@@ -1114,6 +1171,13 @@ class NavvyPagBase {
|
|
|
1114
1171
|
if (err instanceof BXRestApiError) {
|
|
1115
1172
|
throw err;
|
|
1116
1173
|
}
|
|
1174
|
+
// Большинство ошибок Bitrix приходит со статусом не 2xx, и axios
|
|
1175
|
+
// отклоняет такой ответ — достаём из него код и описание ошибки,
|
|
1176
|
+
// а не отдаём безликое "AxiosError: Request failed with status code 400".
|
|
1177
|
+
const apiError = bxRestApiErrorFromHttp(err, method);
|
|
1178
|
+
if (apiError) {
|
|
1179
|
+
throw apiError;
|
|
1180
|
+
}
|
|
1117
1181
|
throw new Error((err.error) ? err.error : err);
|
|
1118
1182
|
}));
|
|
1119
1183
|
}
|