moysklad
moysklad copied to clipboard
Библиотека для работы с API сервиса МойСклад
moysklad
Библиотека для взаимодействия с JSON API сервиса МойСклад для node.js и браузера.
ВНИМАНИЕ! Библиотека находится в стадии разработки и становления. API может меняться. Перед обновлением минорной версии смотрите историю изменений.
Содержание
- Содержание
- Особенности
- Установка
- Использование
- Параметры инициализации
- Аутентификация
-
Статические методы
- getTimeString
- parseTimeString
- parseUrl статический метод
- buildFilter
- buildQuery
-
Методы экземпляра
- GET
- POST
- PUT
- DELETE
- getOptions
- buildUrl
- parseUrl
- fetchUrl
-
Основные аргументы
- path
-
query
- querystring
- filter
- order
- expand и limit
- options параметры запроса
- События
-
Работа с ошибками
- MoyskladError
- MoyskladRequestError
- MoyskladApiError
- MoyskladCollectionError
- MoyskladUnexpectedRedirectError
- История изменений
- Планы развития
- TODO
Особенности
Библиотека представляет максимально простой и прозрачный интерфейс к существующим методам API МойСклад, не абстрагирует разработчика от API и не выполняет никаких внутренних преобразований отправляемых и получаемых данных.
Основная задача библиотеки - упростить ряд рутинных задач:
- формирование строки запроса (передача параметров, заголовков и фильтрация)
- обработка ошибок
- явное преобразование даты в формат МойСклад и обратно в
Date
- базовые TypeScript тайпинги для подсказок по API библиотеки
Важно отметить, что библиотека не поможет вам разобраться с API МойСклад, но лишь упростит работу с ним.
Установка
Поддерживаются версии Node.js >=14
$ npm install moysklad
Для Node.js до 18 версии, дополнительно нужно установить библиотеку для Fetch API и явно указать модуль с соответствующим интерфейсом при создании экземпляра библиотеки
$ npm install undici
const { fetch } = require('undici')
const Moysklad = require('moysklad')
const moysklad = Moysklad({ fetch })
Для работы с библиотекой в браузере или для Node.js c 18 версии, установка дополнительного модуля не требуется.
Использование
const Moysklad = require('moysklad')
// Для инициализации экземпляра библиотеки указывать ключевое слово new не нужно
const ms = Moysklad({ login, password })
ms.GET('entity/customerorder', {
filter: {
applicable: true,
state: {
name: 'Отгружен'
},
sum: { $gt: 1000000, $lt: 2000000 }
},
limit: 10,
order: 'moment,desc',
expand: 'agent'
}).then(({ meta, rows }) => {
console.log(
`Последние ${meta.limit} из ${meta.size} проведенных заказов ` +
`в статусе "Отгружен" на сумму от 10000 до 20000 руб`
)
// Выводим имя заказа, имя контрагента и сумму заказа для всех позиций
rows.forEach(row => {
console.log(`${row.name} ${row.agent.name} ${row.sum / 100}`)
})
})
С другими примерами использования можно ознакомиться в папке examples
Параметры инициализации
Все параметры опциональные (имеют значения по умолчанию)
Параметр | Значение по умолчанию | Описание |
---|---|---|
fetch |
глобальный fetch | Функция с интерфейсом Fetch API. Если глобальный fetch не найден, то будет выброшена ошибка при попытке осуществить http запрос. Начиная с Node.js 18 fetch является частью стандратной библиотеки. |
endpoint |
"https://api.moysklad.ru/api" |
Точка досупа к API (хост точки доступа можно указать через переменную окружения MOYSKLAD_HOST , по умолчанию api.moysklad.ru ) |
api |
"remap" |
Раздел API (можно задать через переменную окружения MOYSKLAD_API ) |
apiVersion |
"1.2" |
Версия API (можно задать через переменную окружения MOYSKLAD_{NAME}_API_VERSION , где {NAME} - название API в верхнем регистре, напр. MOYSKLAD_REMAP_API_VERSION ) |
token |
undefined |
Токен доступа к API (см. Аутентификация) |
login |
undefined |
Логин для доступа к API (см. Аутентификация) |
password |
undefined |
Пароль для доступа к API (см. Аутентификация) |
emitter |
undefined |
экземляр EventEmitter для передачи событий библиотеки |
userAgent |
moysklad/{ver} (+https://github.com/wmakeev/moysklad) , где {ver} - текущая версия библиотеки |
Содержимое заголовка "User-Agent" при выполнении запроса. Удобно использовать для контроля изменений через API на вкладке "Аудит". Можно задать через переменную окружения MOYSKLAD_USER_AGENT . |
Явное задание параметра переопределяет значение заданное в соотв. переменной окружения.
Пример использования:
const Moysklad = require('moysklad')
// Явное указание используемой версии API
const moysklad = Moysklad({ apiVersion: '1.2' })
Аутентификация
Есть несколько способов передачи параметров аутентификации:
-
Напрямую при инициализации экземпляра
// Аутентификация по логину и паролю const moysklad = Moysklad({ login, password })
// Аутентификация по токену const moysklad = Moysklad({ token })
-
Через глобальные переменные или переменные окружения
Если параметры аутентификации не указаны при инициализации клиента,
const moysklad = Moysklad()
то будет проведен поиск параметров в следующем порядке:
- Переменная окружения
process.env.MOYSKLAD_TOKEN
- Переменные окружения
process.env.MOYSKLAD_LOGIN
иprocess.env.MOYSKLAD_PASSWORD
- Глобальная переменная
window.MOYSKLAD_TOKEN
- Глобальные переменные
window.MOYSKLAD_LOGIN
иwindow.MOYSKLAD_PASSWORD
- Глобальная переменная
global.MOYSKLAD_TOKEN
- Глобальные переменные
global.MOYSKLAD_LOGIN
иglobal.MOYSKLAD_PASSWORD
- Переменная окружения
Статические методы
getTimeString
Преобразует локальную дату в строку в формате API МойСклад в часовом поясе Москвы
Moysklad.getTimeString(date: Date, includeMs?: boolean): string
Параметры:
date
- дата
includeMs
- если true
, то в дату будут включены миллисекунды
Пример использования:
const date = new Date('2017-02-01T07:10:11.123Z')
const timeString = Moysklad.getTimeString(date, true)
assert.equal(timeString, '2017-02-01 10:10:11.123')
parseTimeString
Преобразует строку с датой в формате API МойСклад в объект даты (с учетом локального часового пояса и часового пояса API МойСклад)
Moysklad.parseTimeString(date: string) : Date
Параметры:
date
- дата в формате МойСклад (напр. 2017-04-08 13:33:00.123
)
Пример использования:
const parsedDate = Moysklad.parseTimeString('2017-04-08 13:33:00.123')
assert.equal(parsedDate.toISOString(), '2017-04-08T10:33:00.123Z')
parseUrl (статический метод)
Разбор url на составные компоненты
Аналогичен parseUrl методу экземпляра, за тем исключением, что на вход принимает только строку в формате href МойСклад.
buildFilter
Возвращает строку фильтра по объекту
QueryFilter
(см. filter)
Moysklad.buildFilter({ name: { $st: 'foo' } })
// 'code=123;name~=foo'
buildQuery
Формирует строку с параметрами запроса по объекту
Query
(см. query)
Moysklad.buildQuery({
filter: { name: 'foo' },
limit: 100,
foo: 'bar'
})
// 'filter=name%3Dfoo&limit=100&foo=bar'
Методы экземпляра
GET
GET запрос
ms.GET(path: string, query?: object, options?: object): Promise
Параметры:
path
- url ресурса
query
- параметры запроса
options
- опции запроса
Пример использования:
const productsCollection = await ms.GET('entity/product', { limit: 50 })
const order = await ms.GET(`entity/customerorder/${orderId}`, {
expand: 'positions'
})
POST
POST запрос
ms.POST(
path: string,
payload?: object | Array<object>,
query?: object,
options?: object
): Promise
Параметры:
path
- url ресурса
payload
- объект или коллекция объектов (будет преобразовано в строку методом JSON.stringify
)
query
- параметры запроса
options
- опции запроса
Пример использования:
const newProduct = await ms.POST('entity/product', { name: 'Новый товар' })
По умолчанию, при массовом обновлении сущностей, если хотябы один из элементов в ответе содержит ошибку, то метод выбросит ошибку MoyskladCollectionError .
Если такое поведение не является предпочтительным, то можно обрабатывать ошибки при массовом обновлении/создании объектов вручную (см. muteCollectionErrors
в параметрах запроса):
const updated = await ms.POST('entity/supply', supplyList, null, {
muteCollectionErrors: true
})
const errors = updated
.filter(item => item.errors)
.map(item => item.errors[0].error)
if (errors.length) {
console.log('Есть ошибки:', errors.join(', '))
}
const supplyHrefs = updated
.filter(item => !item.errors)
.map(item => item.meta.href)
PUT
PUT запрос
ms.PUT(
path: string | string[],
payload?: object,
query?: object,
options?: object
) : Promise
Параметры:
path
- url ресурса
payload
- обнвляемый объект (будет преобразован в строку методом JSON.stringify
)
query
- параметры запроса
options
- опции запроса
Пример использования:
const updatedProduct = await ms.PUT(`entity/product/${id}`, product)
DELETE
DELETE запрос
ms.DELETE(path: string, options?: object): Promise
Параметры:
path
- url ресурса
options
- опции запроса
Метод DELETE
возвращает undefined
при успешном запросе.
Пример использования:
await ms.DELETE(`entity/product/${product.id}`)
getOptions
Возвращает опции переданные в момент инициализации экземпляра библиотеки
Пример использования:
const options = {
login: 'login',
password: 'password'
}
const ms = Moysklad(options)
const msOptions = ms.getOptions()
assert.ok(msOptions !== options)
assert.equal(msOptions.login, 'login')
assert.equal(msOptions.password, 'password')
buildUrl
Формирует url запроса
ms.buildUrl(url: string, query?: object): string
Параметры:
url
- полный url (должен соответствовать настройкам)
path
- url ресурса
query
- параметры запроса
Пример использования:
const url = ms.buildUrl(
'https://api.moysklad.ru/api/remap/1.2/entity/customerorder?expand=positions',
{ limit: 100 }
)
assert.equal(
url,
'https://api.moysklad.ru/api/remap/1.2/entity/customerorder?expand=positions&limit=100'
)
const url = ms.buildUrl('entity/customerorder', { expand: 'positions' })
assert.equal(
url,
'https://api.moysklad.ru/api/remap/1.2/entity/customerorder?expand=positions'
)
Можно безопасно дублировать символы /
, лишние знаки будут исключены из
результирующего url
const positionUrl = `/positions/${posId}/`
const url = ms.buildUrl(`entity/customerorder/` + positionUrl)
assert.equal(url, `https://api.moysklad.ru/api/remap/1.2/entity/customerorder/positions/${posId}`)
parseUrl
Разбор url на составные компоненты
ms.parseUrl(url: string): {
endpoint: string
api: string
apiVersion: string
path: Array<string>
query: object
}
Параметры:
url
- url ресурса
Пример использования:
const parsedUri = ms.parseUrl('https://api.moysklad.ru/api/remap/1.2/entity/customerorder?expand=positions')
assert.deepEqual(parsedUri, {
endpoint: 'https://api.moysklad.ru/api',
api: 'remap'
apiVersion: '1.2',
path: ['entity', 'customerorder'],
query: {
expand: 'positions'
}
})
fetchUrl
Выполнить запрос по указанному url
ms.fetchUrl(url: string, options?: object): Promise
Параметры:
url
- url ресурса
options
- опции запроса
Пример использования:
const url = `https://api.moysklad.ru/api/remap/1.2/entity/customerorder/eb7bcc22-ae8d-11e3-9e32-002590a28eca`
const patch = { applicable: false }
const updatedOrder = await ms.fetchUrl(url, {
method: 'PUT',
body: JSON.stringify(patch)
})
Основные аргументы
path
Строка.
Примеры:
Url запроса можно указать полностью
ms.GET(
`https://api.moysklad.ru/api/remap/1.2/entity/customerorder/${ORDER_ID}/positions/${POSITION_ID}?expand=assortment`
)
Но гораздо удобнее указывать путь только после версии API и выносить параметры запроса в параметры метода. Полный url будет сгенерирован автоматически, согласно настройкам экземпляра.
Ниже пример аналогичного запроса:
ms.GET(`entity/customerorder/${ORDER_ID}/positions/${POSITION_ID}`, {
expand: 'assortment'
})
Можно безопасно дублировать символы /
, лишние знаки будут сключены из
результирующего url
const positionUrl = `/positions/${posId}`
ms.GET(`entity/customerorder/` + positionUrl)
query
querystring
Все поля объекта запроса преобразуются в соответствующую строку запроса url. Некоторые поля могут подвергаться преобразованию (напр. поля filter
и order
).
Поле объекта запроса должно иметь тип: string
, number
, boolean
, null
или undefined
, любое другое значение вызовет ошибку.
const query = {
str: 'some string',
num: 1,
bool: true,
nil: null, // будет добавлено в строку запроса с пустым значением
nothing: undefined, // поле будет пропущено
arr: ['str', 1, true, null, undefined]
}
// https://api.moysklad.ru/api/remap/1.2/entity/demand?str=some%20string&num=1&bool=true&nil=&arr=str&arr=1&arr=true&arr=
ms.GET('entity/demand', query)
filter
Если поле filter
объект, то вложенные поля filter
преобразуются в параметры фильтра в строке запроса в соответствии со следующими правилами:
-
string
,number
,boolean
не проходят дополнительных преобразований (key=value
) -
null
преобразуется в пустую строку (key=
) -
Date
преобразуется в строку методомgetTimeString
(key=YYYY-MM-DD HH:mm:ss
) -
object
интерпретируется как набор селекторов или вложенных полей (см. пример ниже)
Пример фильтра:
const query = {
filter: {
name: '00001',
code: [1, 2, '03'],
foo: new Date(2000, 0, 1),
state: {
name: 'Оформлен'
},
moment: {
$gt: new Date(2000, 0, 1),
$lte: new Date(2001, 0, 2, 10, 0, 15, 123)
},
bar: {
baz: 1,
$exists: true
}
}
}
соответствует следующему значению поля filter
в запросе (даты переданы в часовом поясе +5):
bar!=;bar.baz=1;code=03;code=1;code=2;foo=1999-12-31 22:00:00;moment<=2001-01-02 08:00:15.123;moment>1999-12-31 22:00:00;name=00001;state.name=Оформлен
Для построения фильтра можно использовать селекторы в стиле Mongo (как в примере выше).
Подробное описание всех возможных селекторов:
Селектор | Фильтр МойСклад | Описание |
---|---|---|
key: { $eq: value } |
key=value |
равно |
key: { $ne: value } |
key!=value |
не равно |
key: { $gt: value } |
key>value |
больше |
key: { $gte: value } |
key>=value |
больше или равно |
key: { $lt: value } |
key<value |
меньше |
key: { $lte: value } |
key<=value |
меньше или равно |
key: { $st: value } |
key~=value |
начинается со строки |
key: { $et: value } |
key=~value |
заканчивается строкой |
key: { $contains: value } |
key~value |
содержит строку |
key: { $in: [..] } или key: [..] |
key=value1;key=value2;... |
входит в |
key: { $nin: [..] } |
key!=value1;key!=value2;... |
не входит в |
key: { $exists: true } |
key!= |
наличие значения (не null) |
key: { $exists: false } |
key= |
пустое значение (null) |
key: { $all: [{..}, ..] } |
объединение условий | |
key: { $not: {..} } |
отрицание условия |
На один ключ можно использовать несколько селекторов.
Подробнее с правилами фильтрации можно ознакомится в документации МойСклад:
order
Если поле order
массив, то произойдет преобразование записи из формы массива в строку.
Примеры:
-
['name']
→'name'
-
[['code','desc']]
→'code,desc'
-
['name', ['code','desc']]
→'name;code,desc'
-
['name,desc', ['code','asc'], ['moment']]
→'name,desc;code,asc;moment'
expand и limit
Если указано значение expand, но не указан limit, то в поле limit по умолчанию будет подставлено значение 100
. Это важно, т.к. в версии API remap 1.2 expand не работает, если не указан limit.
options
(параметры запроса)
Все опции переданные в объекте options
(за исключением описанных ниже) передаются напрямую в опции метода fetch
(Fetch API) при осуществлении запроса.
С опциями fetch API можно ознакомиться по этой ссылке
Опции специфичные для библиотеки moysklad (не передаются в fetch
):
Поле | Тип | Описание |
---|---|---|
rawResponse |
boolean |
Если true , то метод вернет результат в виде объекта Response. Код и содержимое ответа не проверяется на ошибки. |
rawRedirect |
boolean |
Если true и код запроса 3xx (редирект), то метод вернет Response. В обратном случае, будет выброшена ошибка MoyskladRequestError . Опция нужна для явного указания того, что вы ожидаете получить редирект при опции запроса redirect не равной follow . |
muteApiErrors |
boolean |
Если true , то все ошибки API будут проигнорированы (метод не будет генерировать ошибку если код ответа сервера не в диапазоне 200-299 и тело ответа содержит описание ошибки МойСклад). Опция не затрагивает прочие ошибки, которые не возвращают JSON ответ. |
muteCollectionErrors |
boolean |
Если true , то все ошибки внутри коллекций при массовом обновлении сущностей будут проигнорированы. |
precision |
boolean |
Если true , то в запрос будет включен заголовок X-Lognex-Precision со значением true (отключение округления цен и себестоимости до копеек). |
webHookDisable |
boolean |
Если true , то в запрос будет включен заголовок X-Lognex-WebHook-Disable со значением true (отключить уведомления вебхуков в контексте данного запроса). |
downloadExpirationSeconds |
number |
Устанавливает значение для заголовока X-Lognex-Download-Expiration-Seconds (подробнее см. Ссылки на файлы) |
Примеры
-
Формирование заполненного шаблона печатной формы и получение ссылки для загрузки (examples/download-print-form.js):
const path = require('node:path') const { writeFile } = require('node:fs/promises') const { fetch } = require('undici') /** @type {import('..')} */ const Moysklad = require('moysklad') const TEMPLATE_ID = '8a686b8a-9e4a-11e5-7a69-97110004af3e' const DEMAND_ID = '13abf361-e9c6-45ea-a940-df70289a7f95' async function downloadPrintForm() { const ms = Moysklad({ fetch }) const body = { template: { meta: { href: ms.buildUrl( `entity/demand/metadata/customtemplate/${TEMPLATE_ID}` ), type: 'customtemplate', mediaType: 'application/json' } }, extension: 'pdf' } /** @type {import('undici').Response} */ const response = await ms.POST( `entity/demand/${DEMAND_ID}/export`, body, null, // вернуть результат запроса с редиректом без предварительного разбора { rawRedirect: true } ) const location = response.headers.get('location') console.log(location) // 'https://print-prod.moysklad.ru/temp/.../00123.pdf' const formResponse = await fetch(location) // TODO undici возвращает веб-стримы (апгрейдится до 18-й ноды пока рановато) const blob = await formResponse.blob() const buffer = Buffer.from(await blob.arrayBuffer()) await writeFile(path.join(process.cwd(), '__temp/form.pdf'), buffer) } downloadPrintForm()
-
Указание кастомного заголовка
const ms = Moysklad({ fetch: require('node-fetch') }) const folder = { meta: { type: 'productfolder', href: ms.buildUrl(`entity/productfolder/${FOLDER_ID}`) }, description: 'Новое описание группы товаров' } // Указываем кастомный заголовок X-Lognex-WebHook-Disable для PUT запроса const updatedFolder = await ms.PUT( `entity/productfolder/${FOLDER_ID}`, folder, null, { // вместо этого можно использовать webHookDisable: true headers: { 'X-Lognex-WebHook-Disable': true } } ) assert.equal(updatedFolder.description, folder.description)
-
Автоматический редирект
Идентификаторы товаров в приложении МойСклад отличаются от идентификаторов в API. Поэтому, при запросе товара по id из приложения, будет выполнен редирект на другой href.
const ms = Moysklad({ fetch }) // https://api.moysklad.ru/app/#good/edit?id=cb277549-34f4-4029-b9de-7b37e8e25a54 const PRODUCT_UI_ID = 'cb277549-34f4-4029-b9de-7b37e8e25a54' // Error: 308 Permanent Redirect await ms.fetchUrl(ms.buildUrl(`entity/product/${PRODUCT_UI_ID}`)) // Указана опция redirect const product = await ms.fetchUrl( ms.buildUrl(`entity/product/${PRODUCT_UI_ID}`), { redirect: 'follow' } ) assert.ok(product) // OK
События
Событие | Передаваемый объект | Момент наступления |
---|---|---|
request |
{ requestId, url, options } |
Отправлен http запрос |
response |
{ requestId, url, options, response } |
Получен ответ на запрос |
response:body |
{ requestId, url, options, response, body } |
Загружено тело ответа |
error |
Error , { requestId } |
Ошибка при выполнении запроса |
Пример использования:
const { fetch } = require('undici')
const { EventEmitter } = require('events')
const Moysklad = require('moysklad')
/** @type {Moysklad.MoyskladEmitter} */
const emitter = new EventEmitter()
const ms = Moysklad({ fetch, emitter })
emitter
.on('request', ({ requestId, url, options }) => {
console.log(`${requestId} ${options.method} ${url}`)
})
.on('error', (err, { requestId }) => {
console.log(requestId, err)
})
ms.GET('entity/customerorder', { limit: 1 }).then(res => {
console.log('Order name: ' + res.rows[0].name)
})
Более подробный пример смотрите в examples/events.js.
Работа с ошибками
В рамках работы с библиотекой выделены следующие виды ошибок:
№ | Название ошибки | Класс ошибки | Описание |
---|---|---|---|
1 | Ошибка библиотеки | MoyskladError | Напр. не верно указаны параметры одного из методов. |
2 | Ошибка запроса | MoyskladRequestError | Ответ получен с кодом ошибки, тело ответа НЕ содержит JSON с описанием ошибки в формате МойСклад. |
3 | Ошибка API МойСклад | MoyskladApiError | Ответ получен с кодом ошибки, тело ответа содержит JSON с описанием ошибки в формате МойСклад. |
4 | Ошибка в коллекции | MoyskladCollectionError | Ошибка в одном из элементов внутри коллекции. |
5 | Неявный редирект | MoyskladUnexpectedRedirectError | Ошибка возникает когда запрос вернул перенапраление (код 3xx ) и явно не указана опция запроса rawRedirect (опция redirect не равна follow ) |
Библиотека дает возможность указать параметры запроса muteApiErrors
и muteCollectionErrors
для игнорирования ошибок API п.3 и п.4 соответственно.
Ошибки глобального fetch модуля или переданного при инициализации экземпляра не перехватываются внутри библиотеки. Т.е. все описанные выше ошибки, связанные с выполнением запроса, формируются уже после анализа полученного ответа.
MoyskladError
Внутренняя ошибка библиотеки не связанная с выполнением запроса к API
Наследует класс Error
Примеры
Код с ошибкой:
await ms.GET('entity/product', {
filter: 123
})
Структура ошибки:
{
"name": "MoyskladError",
"message": "Поле filter запроса должно быть строкой или объектом"
}
MoyskladRequestError
Ошибка при выполнении запроса
Наследует класс MoyskladError
Примеры
Код с ошибкой:
const ms = Moysklad({ fetch, api: 'foo', apiVersion: '0' })
await ms.GET('foo/bar')
Структура ошибки:
{
"name": "MoyskladRequestError",
"message": "404 Not Found",
"url": "https://api.moysklad.ru/api/foo/0/foo/bar",
"status": 404,
"statusText": "Not Found"
}
MoyskladApiError
Ошибка API МойСклад
Наследует класс MoyskladRequestError
Ошибка формируется в случае, если API помимо HTTP кода ошибки, так же вернуло стандартное описание ошибки МойСклад в формате JSON. В обратном случае (ответ не содержит JSON с ошибкой) будет выброшена ошибка MoyskladRequestError
Примеры
Код с ошибкой:
await ms.GET('entity/product2')
Структура ошибки:
{
"name": "MoyskladApiError",
"message": "Неизвестный тип: 'product2' (https://dev.moysklad.ru/doc/api/remap/1.2/#error_1005)",
"url": "https://api.moysklad.ru/api/remap/1.2/entity/product2",
"status": 412,
"statusText": "Precondition Failed",
"code": 1005,
"moreInfo": "https://dev.moysklad.ru/doc/api/remap/1.2/#error_1005",
"errors": [
{
"error": "Неизвестный тип: 'product2'",
"code": 1005,
"moreInfo": "https://dev.moysklad.ru/doc/api/remap/1.2/#error_1005"
}
]
}
Можно игнорировать ошибку API, указав muteApiErrors:true
в опциях запроса.
const rawError1 = await ms.GET('entity/product2', null, {
muteApiErrors: true
})
console.log(rawError1.errors[0].error)
// Неизвестный тип: 'product2'
MoyskladCollectionError
Ошибка в коллекции при массовом создании/изменении сущностей
Наследует класс MoyskladApiError
Ошибка выбрасывается когда возвращаемая коллекция содержит хотя бы одну ошибку.
Например, когда при массовом обновлении нескольких объектов часть из них не были обновлены, то API вернет массив с результатами в части которых будет указана ошибка.
Примеры
Код с ошибкой:await ms.POST('entity/product', [
{ foo: 'bar' },
{
meta: {
type: 'product',
href: ms.buildUrl(`entity/product/${uuidFromApi}`)
},
weight: 42
},
{ name: 123 }
])
Структура ошибки:
{
"name": "MoyskladCollectionError",
"message": "Ошибка сохранения объекта: поле 'name' не может быть пустым или отсутствовать (https://dev.moysklad.ru/doc/api/remap/1.2/#error_3000)",
"url": "https://api.moysklad.ru/api/remap/1.2/entity/product",
"status": 400,
"statusText": "Bad Request",
"code": 3000,
"moreInfo": "https://dev.moysklad.ru/doc/api/remap/1.2/#error_3000",
"line": 1,
"column": 3,
"errors": [
{
"error": "Ошибка сохранения объекта: поле 'name' не может быть пустым или отсутствовать",
"code": 3000,
"parameter": "name",
"moreInfo": "https://dev.moysklad.ru/doc/api/remap/1.2/#error_3000",
"line": 1,
"column": 3
},
{
"error": "Ошибка формата: значение поля 'name' не соответствует типу строка",
"code": 2016,
"moreInfo": "https://dev.moysklad.ru/doc/api/remap/1.2/#error_2016",
"line": 1,
"column": 169
}
],
"errorsIndexes": [
[
0,
[
{
"error": "Ошибка сохранения объекта: поле 'name' не может быть пустым или отсутствовать",
"code": 3000,
"parameter": "name",
"moreInfo": "https://dev.moysklad.ru/doc/api/remap/1.2/#error_3000",
"line": 1,
"column": 3
}
]
],
[
2,
[
{
"error": "Ошибка формата: значение поля 'name' не соответствует типу строка",
"code": 2016,
"moreInfo": "https://dev.moysklad.ru/doc/api/remap/1.2/#error_2016",
"line": 1,
"column": 169
}
]
]
]
}
Можно игнорировать ошибки в коллекции, указав muteCollectionErrors:true
в опциях запроса.
const result2 = await ms.POST(
'entity/product',
[
{ foo: 'bar' },
{
meta: {
type: 'product',
href: ms.buildUrl(`entity/product/${uuidFromApi}`)
},
weight: 42
},
{ name: 123 }
],
null,
{
muteCollectionErrors: true
}
)
const collItemError = result2.find(it => it.errors)
if (collItemError) {
console.log(collItemError.errors[0].error)
// Ошибка сохранения объекта: поле 'name' не может быть пустым или отсутствовать
}
MoyskladUnexpectedRedirectError
Ошибка если запрос вернул перенапраление (код
3xx
), когда явно не указана опция запросаrawRedirect
и опцияredirect
не равнаfollow
Наследует класс MoyskladRequestError
Примеры
/** id товара из приложения МойСклад */
const uuidFromApp = 'cb277549-34f4-4029-b9de-7b37e8e25a54'
/** id товара из API (отличается от id из приложения) */
let uuidFromApi
const getProduct = id => ms.GET(`entity/product/${id}`)
try {
await getProduct(uuidFromApp)
} catch (err) {
if (err instanceof Moysklad.MoyskladUnexpectedRedirectError) {
uuidFromApi = ms.parseUrl(err.location).path.pop()
await getProduct(uuidFromApi)
} else {
throw err
}
}
Можно обработать перенаправление без перехвата ошибки:
const { fetch, Response } = require('undici')
const ms = Moysklad({ fetch })
let product = await ms.GET(`entity/product/${uuidFromApp}`, null, {
rawRedirect: true
})
if (product instanceof Response) {
uuidFromApi = ms.parseUrl(product.headers.get('location')).path.pop()
product = await ms.GET(`entity/product/${uuidFromApi}`)
}
console.log(product.id === uuidFromApp) // false
Или использовать автоматическое пренаправление, указав значение follow
в опции redirect
:
const product = await ms.GET(`entity/product/${uuidFromApp}`, null, {
redirect: 'follow'
})
console.log(product.id === uuidFromApp) // false
История изменений
Планы развития
Планируется немного переработанная версия библиотеки в другом репозитории и npm пакете. Без концептуальных изменений, но с убранным легаси кодом.
- Переписать на TypeScript
- Добавить новый метод для формирования объекта запроса
- Убрать всё легаси (в том числе то, что тянет лишние зависимости - "have2" и "stampit")
- Более развернутая документация с автогенерацией части описаний методов
TODO
Свалка мыслей по развитию библиотеки - TODO.md